From c64100f9139e2d2d791b7ab7bf47308edbfa12f5 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Fri, 10 Jan 2025 20:58:42 +0530 Subject: [PATCH] Fix(CanvasForm): fetching Required field recursively resolving '' --- .../src/components/Form/CustomAutoFields.tsx | 2 +- .../Form/customField/CustomNestField.tsx | 2 +- .../Form/dataFormat/DataFormatEditor.tsx | 2 +- .../Form/loadBalancer/LoadBalancerEditor.tsx | 2 +- .../stepExpression/StepExpressionEditor.tsx | 2 +- .../Canvas/Form/CanvasFormBody.tsx | 2 +- .../get-required-properties-schema.test.ts | 35 ++++++++++-- .../utils/get-required-properties-schema.ts | 53 ++++++++++++------- 8 files changed, 72 insertions(+), 28 deletions(-) diff --git a/packages/ui/src/components/Form/CustomAutoFields.tsx b/packages/ui/src/components/Form/CustomAutoFields.tsx index 59672f6d6..7d92a22cd 100644 --- a/packages/ui/src/components/Form/CustomAutoFields.tsx +++ b/packages/ui/src/components/Form/CustomAutoFields.tsx @@ -40,7 +40,7 @@ export function CustomAutoFields({ const filteredfields = Object.entries(schemaObject ?? {}).filter( (field) => (!omitFields!.includes(field[0]) && field[0].toLowerCase().includes(cleanQueryTerm)) || - (field[1] as any).type === 'object', + (field[1] as { type: string }).type === 'object', ); const propertiesArray = getFieldGroups(Object.fromEntries(filteredfields)); diff --git a/packages/ui/src/components/Form/customField/CustomNestField.tsx b/packages/ui/src/components/Form/customField/CustomNestField.tsx index 962b8adc8..f80f0888f 100644 --- a/packages/ui/src/components/Form/customField/CustomNestField.tsx +++ b/packages/ui/src/components/Form/customField/CustomNestField.tsx @@ -52,7 +52,7 @@ export const CustomNestField = connectField( const { filteredFieldText, isGroupExpanded } = useContext(FilteredFieldContext); const cleanQueryTerm = filteredFieldText.replace(/\s/g, '').toLowerCase(); const filteredProperties = Object.entries(props.properties ?? {}).filter( - (field) => field[0].toLowerCase().includes(cleanQueryTerm) || (field[1] as any).type === 'object', + (field) => field[0].toLowerCase().includes(cleanQueryTerm) || (field[1] as { type: string }).type === 'object', ); const actualProperties = Object.fromEntries(filteredProperties); const propertiesArray = getFieldGroups(actualProperties); diff --git a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx index 6b43fb2f9..38d699605 100644 --- a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx +++ b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx @@ -67,7 +67,7 @@ export const DataFormatEditor: FunctionComponent = (props const processedSchema = useMemo(() => { if (props.formMode === 'Required') { - return getRequiredPropertiesSchema(dataFormatSchema ?? {}); + return getRequiredPropertiesSchema(dataFormatSchema, dataFormatSchema); } else if (props.formMode === 'All') { return dataFormatSchema; } else if (props.formMode === 'Modified') { diff --git a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx index 40a1358e9..9bf4b2435 100644 --- a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx +++ b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx @@ -68,7 +68,7 @@ export const LoadBalancerEditor: FunctionComponent = (p const processedSchema = useMemo(() => { if (props.formMode === 'Required') { - return getRequiredPropertiesSchema(loadBalancerSchema ?? {}); + return getRequiredPropertiesSchema(loadBalancerSchema, loadBalancerSchema); } else if (props.formMode === 'All') { return loadBalancerSchema; } else if (props.formMode === 'Modified') { diff --git a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx index 844d62dfc..c9b9d78ed 100644 --- a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx +++ b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx @@ -71,7 +71,7 @@ export const StepExpressionEditor: FunctionComponent const processedSchema = useMemo(() => { if (props.formMode === 'Required') { - return getRequiredPropertiesSchema(languageSchema ?? {}); + return getRequiredPropertiesSchema(languageSchema, languageSchema); } else if (props.formMode === 'All') { return languageSchema; } else if (props.formMode === 'Modified') { diff --git a/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx b/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx index d8f7b9b94..0bd33620b 100644 --- a/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx +++ b/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx @@ -32,7 +32,7 @@ export const CanvasFormBody: FunctionComponent = (props) => const model = visualComponentSchema?.definition; let processedSchema = visualComponentSchema?.schema; if (selectedTab === 'Required') { - processedSchema = getRequiredPropertiesSchema(visualComponentSchema?.schema ?? {}); + processedSchema = getRequiredPropertiesSchema(visualComponentSchema?.schema, visualComponentSchema?.schema); } else if (selectedTab === 'Modified') { processedSchema = { ...visualComponentSchema?.schema, diff --git a/packages/ui/src/utils/get-required-properties-schema.test.ts b/packages/ui/src/utils/get-required-properties-schema.test.ts index 18fd64dd0..ccb4d1085 100644 --- a/packages/ui/src/utils/get-required-properties-schema.test.ts +++ b/packages/ui/src/utils/get-required-properties-schema.test.ts @@ -4,6 +4,20 @@ import { KaotoSchemaDefinition } from '../models/kaoto-schema'; describe('getRequiredPropertiesSchema()', () => { const schema = { type: 'object', + definitions: { + testRef: { + type: 'object', + title: 'testRef', + properties: { + spec: { + type: 'string', + title: 'Specification', + description: 'Path to the OpenApi specification file.', + }, + }, + required: ['spec'], + }, + }, properties: { id: { title: 'Id', @@ -21,6 +35,9 @@ describe('getRequiredPropertiesSchema()', () => { title: 'Variable Receive', type: 'string', }, + testRef: { + $ref: '#/definitions/testRef', + }, parameters: { type: 'object', title: 'Endpoint Properties', @@ -154,6 +171,18 @@ describe('getRequiredPropertiesSchema()', () => { title: 'Uri', type: 'string', }, + testRef: { + type: 'object', + title: 'testRef', + properties: { + spec: { + type: 'string', + title: 'Specification', + description: 'Path to the OpenApi specification file.', + }, + }, + required: ['spec'], + }, parameters: { type: 'object', title: 'Endpoint Properties', @@ -200,13 +229,13 @@ describe('getRequiredPropertiesSchema()', () => { required: ['id', 'uri', 'labels'], }; - it('should return only the properties which are user Modified', () => { - const procesedSchema = getRequiredPropertiesSchema(schema); + it('should return only the properties which are Required', () => { + const procesedSchema = getRequiredPropertiesSchema(schema, schema); expect(procesedSchema).toMatchObject(expectedSchema); }); it('should return {}', () => { - const procesedSchema = getRequiredPropertiesSchema({}); + const procesedSchema = getRequiredPropertiesSchema({}, schema); expect(procesedSchema).toMatchObject({}); }); }); diff --git a/packages/ui/src/utils/get-required-properties-schema.ts b/packages/ui/src/utils/get-required-properties-schema.ts index 10f918fe2..bf11233b5 100644 --- a/packages/ui/src/utils/get-required-properties-schema.ts +++ b/packages/ui/src/utils/get-required-properties-schema.ts @@ -1,30 +1,45 @@ import { KaotoSchemaDefinition } from '../models'; import { isDefined } from './is-defined'; +import { resolveRefIfNeeded } from './resolve-ref-if-needed'; -export function getRequiredPropertiesSchema(schema: KaotoSchemaDefinition['schema']): KaotoSchemaDefinition['schema'] { - if (!isDefined(schema)) return {}; +/** + * Extracts a schema containing only the required properties. + * Recursively resolves `$ref` if necessary. + */ +export function getRequiredPropertiesSchema( + schema?: KaotoSchemaDefinition['schema'], + resolveFromSchema?: KaotoSchemaDefinition['schema'], +): KaotoSchemaDefinition['schema'] { + if (!isDefined(schema) || !isDefined(resolveFromSchema)) return {}; const schemaProperties = schema.properties; const requiredProperties = schema.required as string[]; - if (isDefined(requiredProperties) && isDefined(schemaProperties)) { - const requiredFormSchema = Object.entries(schemaProperties).reduce( - (acc, [property, definition]) => { - if (definition['type'] === 'object' && 'properties' in definition) { - const subSchema = getRequiredPropertiesSchema(definition); - if (Object.keys(subSchema.properties as object).length > 0) { - acc[property] = subSchema; - } - } else { - if (requiredProperties.indexOf(property) > -1) acc[property] = definition; + if (!isDefined(schemaProperties)) { + return { ...schema, properties: {} }; + } + + const requiredFormSchema = Object.entries(schemaProperties).reduce( + (acc, [property, definition]) => { + if ('$ref' in definition) { + const objectDefinition = resolveRefIfNeeded(definition, resolveFromSchema); + const subSchema = getRequiredPropertiesSchema(objectDefinition, resolveFromSchema); + if (Object.keys(subSchema.properties as object).length > 0) { + acc[property] = subSchema; } + } else if (definition['type'] === 'object' && 'properties' in definition) { + const subSchema = getRequiredPropertiesSchema(definition, resolveFromSchema); + if (Object.keys(subSchema.properties as object).length > 0) { + acc[property] = subSchema; + } + } else { + if (isDefined(requiredProperties) && requiredProperties.indexOf(property) > -1) acc[property] = definition; + } - return acc; - }, - {} as KaotoSchemaDefinition['schema'], - ); - return { ...schema, properties: requiredFormSchema }; - } + return acc; + }, + {} as KaotoSchemaDefinition['schema'], + ); - return { ...schema, properties: {} }; + return { ...schema, properties: requiredFormSchema }; }