diff --git a/packages/ui-tests/stories/canvas/Aggregate.stories.tsx b/packages/ui-tests/stories/canvas/Aggregate.stories.tsx index 41d1ba3f5..395109a60 100644 --- a/packages/ui-tests/stories/canvas/Aggregate.stories.tsx +++ b/packages/ui-tests/stories/canvas/Aggregate.stories.tsx @@ -31,10 +31,10 @@ const selectedNode: CanvasNode = { previousNode: undefined, label: 'test', getId: () => 'aggregate-6839', + getTitle: () => 'Aggregate', getOmitFormFields: () => [], getComponentSchema: () => { return { - title: 'aggregate', schema: { title: 'Aggregate', description: 'Aggregates many messages into a single message', diff --git a/packages/ui-tests/stories/canvas/CanvasSideBar.stories.tsx b/packages/ui-tests/stories/canvas/CanvasSideBar.stories.tsx index 3c38d75c9..9e456590e 100644 --- a/packages/ui-tests/stories/canvas/CanvasSideBar.stories.tsx +++ b/packages/ui-tests/stories/canvas/CanvasSideBar.stories.tsx @@ -34,10 +34,10 @@ const selectedNode: CanvasNode = { previousNode: undefined, label: 'test', getId: () => 'log-sink-6839', + getTitle: () => 'My Node', getOmitFormFields: () => [], getComponentSchema: () => { return { - title: 'My Node', schema: { type: 'object', properties: { @@ -76,6 +76,7 @@ const unknownSelectedNode: CanvasNode = { icon: NodeIconResolver.getIcon(''), } as IVisualizationNodeData, getId: () => 'test', + getTitle: () => 'My Node', getOmitFormFields: () => [], getComponentSchema: () => { return { diff --git a/packages/ui-tests/stories/metadataEditor/DataformatEditor.stories.tsx b/packages/ui-tests/stories/metadataEditor/DataformatEditor.stories.tsx index 3432a01c9..3f042418b 100644 --- a/packages/ui-tests/stories/metadataEditor/DataformatEditor.stories.tsx +++ b/packages/ui-tests/stories/metadataEditor/DataformatEditor.stories.tsx @@ -12,7 +12,6 @@ import { import { Meta, StoryFn } from '@storybook/react'; const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -37,6 +36,7 @@ const mockNode: CanvasNode = { type: 'node', data: { vizNode: { + getTitle: () => 'My Node', getComponentSchema: () => visualComponentSchema, updateModel: (_value: unknown) => {}, } as IVisualizationNode, diff --git a/packages/ui-tests/stories/metadataEditor/ExpressionEditor.stories.tsx b/packages/ui-tests/stories/metadataEditor/ExpressionEditor.stories.tsx index ad8e74160..d994f81a6 100644 --- a/packages/ui-tests/stories/metadataEditor/ExpressionEditor.stories.tsx +++ b/packages/ui-tests/stories/metadataEditor/ExpressionEditor.stories.tsx @@ -13,7 +13,6 @@ import { import { Meta, StoryFn } from '@storybook/react'; const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -39,6 +38,7 @@ const mockNode: CanvasNode = { type: 'node', data: { vizNode: { + getTitle: () => 'My Node', getComponentSchema: () => visualComponentSchema, } as IVisualizationNode, }, diff --git a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx index 12e79845e..1c6d4197d 100644 --- a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx +++ b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx @@ -19,7 +19,6 @@ describe('DataFormatEditor', () => { CamelCatalogService.setCatalogKey(CatalogKind.Dataformat, catalogsMap.dataformatCatalog); const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -53,7 +52,6 @@ describe('DataFormatEditor', () => { it('should render with only the user updated fields', () => { const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -95,7 +93,6 @@ describe('DataFormatEditor', () => { it('should render with only the Required fields', () => { const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { diff --git a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx index dc8c9783f..55a4b0074 100644 --- a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx +++ b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx @@ -18,7 +18,6 @@ describe('LoadBalancerEditor', () => { CamelCatalogService.setCatalogKey(CatalogKind.Loadbalancer, loadbalancerCatalog); const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -54,7 +53,6 @@ describe('LoadBalancerEditor', () => { it('should render with only the user updated fields', () => { const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -96,7 +94,6 @@ describe('LoadBalancerEditor', () => { it('should render with only the Required fields', () => { const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { diff --git a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx index d4818a711..af0d61e7e 100644 --- a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx +++ b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx @@ -18,7 +18,6 @@ describe('StepExpressionEditor', () => { CamelCatalogService.setCatalogKey(CatalogKind.Language, languageCatalog); const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { @@ -52,7 +51,6 @@ describe('StepExpressionEditor', () => { it('should render with only the user updated fields', () => { const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: { type: 'object', properties: { diff --git a/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.test.tsx b/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.test.tsx index dae0888d9..e62dd706a 100644 --- a/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.test.tsx +++ b/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.test.tsx @@ -24,9 +24,9 @@ import { EntitiesContext, EntitiesProvider } from '../../../../providers/entitie import { camelRouteJson, kameletJson } from '../../../../stubs'; import { getFirstCatalogMap } from '../../../../stubs/test-load-catalog'; import { ROOT_PATH } from '../../../../utils'; -import { CanvasForm } from './CanvasForm'; import { CanvasNode } from '../canvas.models'; import { FlowService } from '../flow.service'; +import { CanvasForm } from './CanvasForm'; describe('CanvasForm', () => { let camelRouteVisualEntity: CamelRouteVisualEntity; @@ -104,7 +104,6 @@ describe('CanvasForm', () => { it('should render nothing if no schema and no definition is available', () => { const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', schema: null as unknown as KaotoSchemaDefinition['schema'], definition: null, }; @@ -137,39 +136,6 @@ describe('CanvasForm', () => { expect(container).toMatchSnapshot(); }); - it('should update the parameters object if null', () => { - const visualComponentSchema: VisualComponentSchema = { - title: 'My Node', - schema: null as unknown as KaotoSchemaDefinition['schema'], - definition: { - parameters: null, - }, - }; - - const selectedNode: CanvasNode = { - id: '1', - type: 'node', - data: { - vizNode: { - getComponentSchema: () => visualComponentSchema, - getBaseEntity: () => new CamelRouteVisualEntity(camelRouteJson), - getId: () => 'route-8888', - getOmitFields: () => [], - } as unknown as IVisualizationNode, - }, - }; - - render( - - - - - , - ); - - expect(visualComponentSchema.definition.parameters).toEqual({}); - }); - it("should serialize empty strings `''` as `undefined`", async () => { const flowId = camelRouteVisualEntity.id; const dispatchSpy = jest.fn(); diff --git a/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.tsx b/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.tsx index 8a2273a5b..1bb12d4f3 100644 --- a/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.tsx +++ b/packages/ui/src/components/Visualization/Canvas/Form/CanvasForm.tsx @@ -1,5 +1,5 @@ import { Card, CardBody, CardHeader } from '@patternfly/react-core'; -import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef } from 'react'; +import { FunctionComponent, useCallback, useContext, useEffect, useRef } from 'react'; import { VisibleFlowsContext } from '../../../../providers'; import { ErrorBoundary } from '../../../ErrorBoundary'; import { CanvasNode } from '../canvas.models'; @@ -16,16 +16,7 @@ export const CanvasForm: FunctionComponent = ({ selectedNode, o const { visualFlowsApi } = useContext(VisibleFlowsContext)!; const flowIdRef = useRef(undefined); const vizNode = selectedNode.data?.vizNode; - - const visualComponentSchema = useMemo(() => { - const answer = vizNode?.getComponentSchema(); - // Overriding parameters with an empty object When the parameters property is mistakenly set to null - if (answer?.definition?.parameters === null) { - answer!.definition.parameters = {}; - } - return answer; - }, [vizNode]); - const title = visualComponentSchema?.title; + const title = vizNode?.getTitle(); /** Store the flow's initial Id */ useEffect(() => { diff --git a/packages/ui/src/components/Visualization/Canvas/Form/__snapshots__/CanvasForm.test.tsx.snap b/packages/ui/src/components/Visualization/Canvas/Form/__snapshots__/CanvasForm.test.tsx.snap index a14c36fa9..5776ce3ce 100644 --- a/packages/ui/src/components/Visualization/Canvas/Form/__snapshots__/CanvasForm.test.tsx.snap +++ b/packages/ui/src/components/Visualization/Canvas/Form/__snapshots__/CanvasForm.test.tsx.snap @@ -314,7 +314,7 @@ exports[`CanvasForm should render nothing if no schema and no definition is avai data-ouia-component-type="PF5/Title" data-ouia-safe="true" > - My Node + route
+ > + route +
@@ -1414,7 +1414,7 @@ exports[`Canvas should render correctly with more routes 1`] = ` data-layer-id="groups" > @@ -2141,7 +2141,7 @@ exports[`Canvas should render the Catalog button if \`CatalogModalContext\` is p data-layer-id="groups" > diff --git a/packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap b/packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap index 1c5f0769e..5f040493f 100644 --- a/packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap +++ b/packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap @@ -51,6 +51,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "child2", }, ], "data": { @@ -61,8 +62,10 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": undefined, "previousNode": undefined, + "title": "group", }, "previousNode": undefined, + "title": "child1", }, }, "height": 75, @@ -120,6 +123,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "child1", }, [Circular], ], @@ -131,8 +135,10 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": undefined, "previousNode": undefined, + "title": "group", }, "previousNode": undefined, + "title": "child2", }, }, "height": 75, @@ -178,6 +184,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "child1", }, VisualizationNode { "DISABLED_NODE_INTERACTION": { @@ -197,6 +204,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "child2", }, ], "data": { @@ -207,6 +215,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a group wi "nodeInteraction": undefined, "parentNode": undefined, "previousNode": undefined, + "title": "group", }, }, "group": true, @@ -297,6 +306,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a multiple "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "when-leaf", }, ], "data": {}, @@ -305,6 +315,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a multiple "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "when", }, VisualizationNode { "DISABLED_NODE_INTERACTION": { @@ -348,6 +359,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a multiple "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "log", }, ], "data": {}, @@ -356,6 +368,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a multiple "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "process", }, ], "data": {}, @@ -364,6 +377,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a multiple "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "otherwise", }, ], "data": {}, @@ -386,18 +400,22 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a multiple "nodeInteraction": undefined, "parentNode": undefined, "previousNode": [Circular], + "title": "direct", }, "nodeInteraction": undefined, "parentNode": undefined, "previousNode": [Circular], + "title": "choice", }, "nodeInteraction": undefined, "parentNode": undefined, "previousNode": [Circular], + "title": "set-header", }, "nodeInteraction": undefined, "parentNode": undefined, "previousNode": undefined, + "title": "node", }, }, "height": 75, @@ -444,6 +462,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a simple V "nodeInteraction": undefined, "parentNode": undefined, "previousNode": undefined, + "title": "node", }, }, "height": 75, @@ -492,6 +511,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a two-node "nodeInteraction": undefined, "parentNode": [Circular], "previousNode": undefined, + "title": "child", }, ], "data": {}, @@ -500,6 +520,7 @@ exports[`FlowService getFlowDiagram should return nodes and edges for a two-node "nodeInteraction": undefined, "parentNode": undefined, "previousNode": undefined, + "title": "node", }, }, "height": 75, diff --git a/packages/ui/src/models/visualization/base-visual-entity.ts b/packages/ui/src/models/visualization/base-visual-entity.ts index 69f994960..c51c8d9e5 100644 --- a/packages/ui/src/models/visualization/base-visual-entity.ts +++ b/packages/ui/src/models/visualization/base-visual-entity.ts @@ -80,6 +80,12 @@ export interface IVisualizationNode { }); }); + describe('getComponentSchema', () => { + it('should return undefined if path is not provided', () => { + const result = abstractVisualEntity.getComponentSchema(); + expect(result).toBeUndefined(); + }); + + it('should return visualComponentSchema when path is valid', () => { + const path = 'from.steps.0'; + const visualComponentSchema = { + schema: {}, + definition: { + parameters: { some: 'parameter' }, + }, + }; + + jest.spyOn(CamelComponentSchemaService, 'getVisualComponentSchema').mockReturnValue(visualComponentSchema); + + const result = abstractVisualEntity.getComponentSchema(path); + expect(result).toEqual(visualComponentSchema); + }); + + it('should override parameters with an empty object when parameters is null', () => { + const path = 'from.steps.0'; + const visualComponentSchema = { + schema: {}, + definition: { + parameters: null, + }, + }; + + jest.spyOn(CamelComponentSchemaService, 'getVisualComponentSchema').mockReturnValue(visualComponentSchema); + + const result = abstractVisualEntity.getComponentSchema(path); + expect(result?.definition.parameters).toEqual({}); + }); + + it.each([undefined, { property: 'value' }])('should not do anything when parameters is not null', (value) => { + const path = 'from.steps.0'; + const visualComponentSchema = { + schema: {}, + definition: { + parameters: value, + }, + }; + const expected = JSON.parse(JSON.stringify(visualComponentSchema)); + + jest.spyOn(CamelComponentSchemaService, 'getVisualComponentSchema').mockReturnValue(visualComponentSchema); + + const result = abstractVisualEntity.getComponentSchema(path); + expect(result).toEqual(expected); + }); + }); + describe('updateModel', () => { it('should update the model with the new value', () => { const newUri = 'timer:MyTimer'; diff --git a/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts b/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts index 26d2849a5..c5f7d31fe 100644 --- a/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts @@ -14,10 +14,10 @@ import { VisualComponentSchema, } from '../base-visual-entity'; import { createVisualizationNode } from '../visualization-node'; +import { NodeMapperService } from './nodes/node-mapper.service'; import { CamelComponentDefaultService } from './support/camel-component-default.service'; import { CamelComponentSchemaService } from './support/camel-component-schema.service'; import { CamelProcessorStepsProperties, CamelRouteVisualEntityData } from './support/camel-component-types'; -import { NodeMapperService } from './nodes/node-mapper.service'; import { ModelValidationService } from './support/validators/model-validation.service'; export abstract class AbstractCamelVisualEntity implements BaseVisualCamelEntity { @@ -63,6 +63,11 @@ export abstract class AbstractCamelVisualEntity implements Bas const componentModel = getValue(this.route, path); const visualComponentSchema = CamelComponentSchemaService.getVisualComponentSchema(path, componentModel); + /** Overriding parameters with an empty object When the parameters property is mistakenly set to null */ + if (visualComponentSchema?.definition?.parameters === null) { + visualComponentSchema.definition.parameters = {}; + } + return visualComponentSchema; } @@ -213,7 +218,7 @@ export abstract class AbstractCamelVisualEntity implements Bas } toVizNode(): IVisualizationNode { - const routeGroupNode = createVisualizationNode(this.id, { + const routeGroupNode = createVisualizationNode('route', { path: ROOT_PATH, entity: this, isGroup: true, diff --git a/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.test.ts b/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.test.ts index 315958bfc..4bfc2d2e0 100644 --- a/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.test.ts +++ b/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.test.ts @@ -97,12 +97,6 @@ describe('CamelErrorHandlerVisualEntity', () => { expect(entity.getComponentSchema().schema).toEqual(errorHandlerSchema); }); - - it('should return hardcoded schema title', () => { - const entity = new CamelErrorHandlerVisualEntity(errorHandlerDef); - - expect(entity.getComponentSchema().title).toEqual('Error Handler'); - }); }); describe('updateModel', () => { @@ -176,6 +170,13 @@ describe('CamelErrorHandlerVisualEntity', () => { }); }); + it('should return hardcoded schema title', () => { + const entity = new CamelErrorHandlerVisualEntity(errorHandlerDef); + const vizNode = entity.toVizNode(); + + expect(vizNode.getTitle()).toEqual('Error Handler'); + }); + it('should serialize the errorHandler definition', () => { const entity = new CamelErrorHandlerVisualEntity(errorHandlerDef); diff --git a/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.ts b/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.ts index a11d2ec35..86a7f1c1a 100644 --- a/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.ts @@ -89,7 +89,6 @@ export class CamelErrorHandlerVisualEntity implements BaseVisualCamelEntity { return { definition: Object.assign({}, this.errorHandlerDef.errorHandler), schema: schema, - title: 'Error Handler', }; } @@ -133,6 +132,7 @@ export class CamelErrorHandlerVisualEntity implements BaseVisualCamelEntity { errorHandlerGroupNode.data.entity = this; errorHandlerGroupNode.data.isGroup = true; errorHandlerGroupNode.data.icon = NodeIconResolver.getIcon(this.type, NodeIconType.VisualEntity); + errorHandlerGroupNode.setTitle('Error Handler'); return errorHandlerGroupNode; } diff --git a/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.test.ts b/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.test.ts index 406d0a52b..bfe2758a5 100644 --- a/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.test.ts +++ b/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.test.ts @@ -86,12 +86,6 @@ describe('CamelRestConfigurationVisualEntity', () => { expect(entity.getComponentSchema().schema).toEqual(restConfigurationSchema); }); - - it('should return hardcoded schema title', () => { - const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); - - expect(entity.getComponentSchema().title).toEqual('Rest Configuration'); - }); }); describe('updateModel', () => { @@ -207,6 +201,13 @@ describe('CamelRestConfigurationVisualEntity', () => { processorName: 'restConfiguration', }); }); + + it('should return hardcoded schema title', () => { + const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); + const vizNode = entity.toVizNode(); + + expect(vizNode.getTitle()).toEqual('Rest Configuration'); + }); }); it('should serialize the restConfiguration definition', () => { diff --git a/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.ts b/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.ts index 36ff90bab..c722a409f 100644 --- a/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.ts @@ -74,7 +74,6 @@ export class CamelRestConfigurationVisualEntity implements BaseVisualCamelEntity return { definition: Object.assign({}, this.restConfigurationDef.restConfiguration), schema: schema?.propertiesSchema || {}, - title: 'Rest Configuration', }; } @@ -127,6 +126,7 @@ export class CamelRestConfigurationVisualEntity implements BaseVisualCamelEntity restConfigurationGroupNode.data.entity = this; restConfigurationGroupNode.data.isGroup = true; restConfigurationGroupNode.data.icon = NodeIconResolver.getIcon(this.type, NodeIconType.VisualEntity); + restConfigurationGroupNode.setTitle('Rest Configuration'); return restConfigurationGroupNode; } diff --git a/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.test.ts b/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.test.ts new file mode 100644 index 000000000..3b52def53 --- /dev/null +++ b/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.test.ts @@ -0,0 +1,227 @@ +import catalogLibrary from '@kaoto/camel-catalog/index.json'; +import { CatalogLibrary, RouteConfigurationDefinition } from '@kaoto/camel-catalog/types'; +import { routeConfigurationStub } from '../../../stubs/route-configuration'; +import { getFirstCatalogMap } from '../../../stubs/test-load-catalog'; +import { CatalogKind } from '../../catalog-kind'; +import { AbstractCamelVisualEntity } from './abstract-camel-visual-entity'; +import { CamelCatalogService } from './camel-catalog.service'; +import { CamelRouteConfigurationVisualEntity } from './camel-route-configuration-visual-entity'; + +describe('CamelRouteConfigurationVisualEntity', () => { + const ROUTE_CONFIGURATION_ID_REGEXP = /^routeConfiguration-[a-zA-Z0-9]{4}$/; + let routeConfigurationDef: { routeConfiguration: RouteConfigurationDefinition }; + + beforeAll(async () => { + const catalogsMap = await getFirstCatalogMap(catalogLibrary as CatalogLibrary); + CamelCatalogService.setCatalogKey(CatalogKind.Entity, catalogsMap.entitiesCatalog); + }); + + afterAll(() => { + CamelCatalogService.clearCatalogs(); + }); + + beforeEach(() => { + routeConfigurationDef = { + routeConfiguration: { + ...routeConfigurationStub.routeConfiguration, + }, + }; + }); + + describe('isApplicable', () => { + it.each([ + [true, { routeConfiguration: {} }], + [true, { routeConfiguration: { intercept: [] } }], + [true, routeConfigurationStub], + [false, { from: { id: 'from-1234', steps: [] } }], + [false, { routeConfiguration: { intercept: [] }, anotherProperty: true }], + ])('should return %s for %s', (result, definition) => { + expect(CamelRouteConfigurationVisualEntity.isApplicable(definition)).toEqual(result); + }); + }); + + describe('constructor', () => { + it('should set id to generated id', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.id).toMatch(ROUTE_CONFIGURATION_ID_REGEXP); + }); + }); + + it('should return id', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.getId()).toMatch(ROUTE_CONFIGURATION_ID_REGEXP); + }); + + it('should set id', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + const newId = 'newId'; + entity.setId(newId); + + expect(entity.getId()).toEqual(newId); + }); + + it('should delegate to super return node label', () => { + const superGetNodeLabelSpy = jest + .spyOn(AbstractCamelVisualEntity.prototype, 'getNodeLabel') + .mockReturnValueOnce('label'); + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.getNodeLabel()).toEqual('label'); + expect(superGetNodeLabelSpy).toHaveBeenCalled(); + }); + + it('should return tooltip content', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.getTooltipContent(CamelRouteConfigurationVisualEntity.ROOT_PATH)).toEqual('routeConfiguration'); + }); + + it('should delegate to super to return tooltip content', () => { + const superGetTooltipContentSpy = jest + .spyOn(AbstractCamelVisualEntity.prototype, 'getTooltipContent') + .mockReturnValueOnce('tooltip'); + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.getTooltipContent()).toEqual('tooltip'); + expect(superGetTooltipContentSpy).toHaveBeenCalled(); + }); + + describe('getComponentSchema', () => { + it('should return entity current definition', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.getComponentSchema(CamelRouteConfigurationVisualEntity.ROOT_PATH)?.definition).toEqual( + routeConfigurationDef.routeConfiguration, + ); + }); + + it('should return schema from store', () => { + const catalogServiceSpy = jest.spyOn(CamelCatalogService, 'getComponent'); + + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + entity.getComponentSchema(CamelRouteConfigurationVisualEntity.ROOT_PATH); + + expect(catalogServiceSpy).toHaveBeenCalledWith(CatalogKind.Entity, 'routeConfiguration'); + }); + }); + + describe('updateModel', () => { + it('should update model', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + const path = 'routeConfiguration.description'; + const value = 'This is a Route configuration node'; + + entity.updateModel(path, value); + + expect(routeConfigurationDef.routeConfiguration.description).toEqual(value); + }); + + it('should not update model if path is not defined', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + const value = 'This is a Route configuration node'; + + entity.updateModel(undefined, value); + + expect(routeConfigurationDef.routeConfiguration.description).toBeUndefined(); + }); + + it('should reset the routeConfiguration object if it is not defined', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + entity.updateModel('routeConfiguration', {}); + + expect(routeConfigurationDef.routeConfiguration).toEqual({}); + }); + }); + + it('return no interactions for ROOT_PATH', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.getNodeInteraction({ path: CamelRouteConfigurationVisualEntity.ROOT_PATH })).toEqual({ + canHavePreviousStep: false, + canHaveNextStep: false, + canHaveChildren: false, + canHaveSpecialChildren: true, + canRemoveStep: false, + canReplaceStep: false, + canRemoveFlow: true, + canBeDisabled: false, + }); + }); + + it('should delegate to super to return interactions for non ROOT_PATH', () => { + const mockInteractions = { + canHavePreviousStep: true, + canHaveNextStep: true, + canHaveChildren: true, + canHaveSpecialChildren: true, + canRemoveStep: true, + canReplaceStep: true, + canRemoveFlow: true, + canBeDisabled: true, + }; + const superGetNodeInteractionSpy = jest + .spyOn(AbstractCamelVisualEntity.prototype, 'getNodeInteraction') + .mockReturnValueOnce(mockInteractions); + + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + const result = entity.getNodeInteraction({ path: 'another path' }); + + expect(superGetNodeInteractionSpy).toHaveBeenCalled(); + expect(result).toEqual(mockInteractions); + }); + + describe('getNodeValidationText', () => { + it('should return undefined for valid definitions', () => { + const entity = new CamelRouteConfigurationVisualEntity({ + routeConfiguration: { + ...routeConfigurationDef.routeConfiguration, + }, + }); + + expect(entity.getNodeValidationText()).toBeUndefined(); + }); + + it('should not modify the original definition when validating', () => { + const originalRouteConfigurationDef: RouteConfigurationDefinition = { + ...routeConfigurationDef.routeConfiguration, + }; + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + entity.getNodeValidationText(); + + expect(routeConfigurationDef.routeConfiguration).toEqual(originalRouteConfigurationDef); + }); + }); + + describe('toVizNode', () => { + it('should return visualization node', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + const vizNode = entity.toVizNode(); + + expect(vizNode.data).toEqual({ + entity, + icon: '', + isGroup: true, + path: 'routeConfiguration', + processorName: 'routeConfiguration', + }); + }); + + it('should return hardcoded schema title', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + const vizNode = entity.toVizNode(); + + expect(vizNode.getTitle()).toEqual('Route Configuration'); + }); + }); + + it('should serialize the routeConfiguration definition', () => { + const entity = new CamelRouteConfigurationVisualEntity(routeConfigurationDef); + + expect(entity.toJSON()).toEqual(routeConfigurationDef); + }); +}); diff --git a/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.ts b/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.ts index af2e37c80..9400f7633 100644 --- a/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.ts @@ -26,7 +26,7 @@ export class CamelRouteConfigurationVisualEntity { id: string; readonly type = EntityType.RouteConfiguration; - private static readonly ROOT_PATH = 'routeConfiguration'; + static readonly ROOT_PATH = 'routeConfiguration'; private schemaValidator: ValidateFunction | undefined; private readonly OMIT_FORM_FIELDS = [ ...SchemaService.OMIT_FORM_FIELDS, @@ -87,7 +87,6 @@ export class CamelRouteConfigurationVisualEntity if (path === CamelRouteConfigurationVisualEntity.ROOT_PATH) { const schema = CamelCatalogService.getComponent(CatalogKind.Entity, 'routeConfiguration'); return { - title: 'Route Configuration', schema: schema?.propertiesSchema || {}, definition: Object.assign({}, this.routeConfigurationDef.routeConfiguration), }; @@ -148,6 +147,7 @@ export class CamelRouteConfigurationVisualEntity icon: NodeIconResolver.getIcon(this.type, NodeIconType.VisualEntity), processorName: CamelRouteConfigurationVisualEntity.ROOT_PATH, }); + routeConfigurationGroupNode.setTitle('Route Configuration'); CamelComponentSchemaService.getProcessorStepsProperties( CamelRouteConfigurationVisualEntity.ROOT_PATH as keyof ProcessorDefinition, diff --git a/packages/ui/src/models/visualization/flows/camel-route-visual-entity.test.ts b/packages/ui/src/models/visualization/flows/camel-route-visual-entity.test.ts index d38bc71e9..d14244d95 100644 --- a/packages/ui/src/models/visualization/flows/camel-route-visual-entity.test.ts +++ b/packages/ui/src/models/visualization/flows/camel-route-visual-entity.test.ts @@ -106,7 +106,6 @@ describe('Camel Route', () => { const result = camelEntity.getComponentSchema('test'); expect(result).toEqual({ - title: 'test', schema: {}, definition: undefined, }); @@ -115,7 +114,6 @@ describe('Camel Route', () => { it('should return the component schema', () => { const spy = jest.spyOn(CamelComponentSchemaService, 'getVisualComponentSchema'); spy.mockReturnValueOnce({ - title: 'test', schema: {} as KaotoSchemaDefinition['schema'], definition: {}, }); diff --git a/packages/ui/src/models/visualization/flows/kamelet-visual-entity.test.ts b/packages/ui/src/models/visualization/flows/kamelet-visual-entity.test.ts index 1d73e4c57..8a0685623 100644 --- a/packages/ui/src/models/visualization/flows/kamelet-visual-entity.test.ts +++ b/packages/ui/src/models/visualization/flows/kamelet-visual-entity.test.ts @@ -122,4 +122,21 @@ describe('KameletVisualEntity', () => { const kamelet = new KameletVisualEntityTest(kameletDef); expect(kamelet.getRootUri()).toEqual('timer:tutorial'); }); + + describe('toVizNode', () => { + it('should delegate to the super class toVizNode', () => { + const toVizNodeSpy = jest.spyOn(AbstractCamelVisualEntity.prototype, 'toVizNode'); + const kamelet = new KameletVisualEntity(kameletDef); + kamelet.toVizNode(); + + expect(toVizNodeSpy).toHaveBeenCalled(); + }); + + it('should return a visualization node with title Kamelet', () => { + const kamelet = new KameletVisualEntity(kameletDef); + const vizNode = kamelet.toVizNode(); + + expect(vizNode.getTitle()).toEqual('Kamelet'); + }); + }); }); diff --git a/packages/ui/src/models/visualization/flows/kamelet-visual-entity.ts b/packages/ui/src/models/visualization/flows/kamelet-visual-entity.ts index ada187e0f..900acbcff 100644 --- a/packages/ui/src/models/visualization/flows/kamelet-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/kamelet-visual-entity.ts @@ -12,7 +12,7 @@ import { EntityType } from '../../camel/entities'; import { CatalogKind } from '../../catalog-kind'; import { IKameletDefinition } from '../../kamelets-catalog'; import { KaotoSchemaDefinition } from '../../kaoto-schema'; -import { AddStepMode, IVisualizationNodeData, VisualComponentSchema } from '../base-visual-entity'; +import { AddStepMode, IVisualizationNode, IVisualizationNodeData, VisualComponentSchema } from '../base-visual-entity'; import { AbstractCamelVisualEntity } from './abstract-camel-visual-entity'; import { CamelCatalogService } from './camel-catalog.service'; import { CamelComponentDefaultService } from './support/camel-component-default.service'; @@ -45,7 +45,6 @@ export class KameletVisualEntity extends AbstractCamelVisualEntity { it('should return the component schema', () => { const spy = jest.spyOn(KameletSchemaService, 'getVisualComponentSchema'); spy.mockReturnValueOnce({ - title: 'test', schema: {} as KaotoSchemaDefinition['schema'], definition: {}, }); pipeVisualEntity.getComponentSchema('source'); - expect(spy).toBeCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(1); }); }); @@ -175,6 +174,24 @@ describe('Pipe', () => { expect(vizNode.getNodeLabel()).toEqual('webhook-binding'); }); + it('should set the title to `Pipe`', () => { + const vizNode = pipeVisualEntity.toVizNode(); + + expect(vizNode.getTitle()).toEqual('Pipe'); + }); + + it('should set the title to children nodes', () => { + const vizNode = pipeVisualEntity.toVizNode(); + + const sourceNode = vizNode.getChildren()![0]; + const stepNode = vizNode.getChildren()![1]; + const sinkNode = vizNode.getChildren()![2]; + + expect(sourceNode.getTitle()).toEqual('webhook-source'); + expect(stepNode.getTitle()).toEqual('delay-action'); + expect(sinkNode.getTitle()).toEqual('log-sink'); + }); + it('should set the node labels as `Unknown` if the uri is not available', () => { pipeVisualEntity = new PipeVisualEntity({}); diff --git a/packages/ui/src/models/visualization/flows/pipe-visual-entity.ts b/packages/ui/src/models/visualization/flows/pipe-visual-entity.ts index 3fa9d6ade..8d42f3e39 100644 --- a/packages/ui/src/models/visualization/flows/pipe-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/pipe-visual-entity.ts @@ -79,7 +79,6 @@ export class PipeVisualEntity implements BaseVisualCamelEntity { if (!path) return undefined; if (path === ROOT_PATH) { return { - title: 'Pipe', schema: this.getRootPipeSchema(), definition: getCustomSchemaFromPipe(this.pipe), }; @@ -220,6 +219,7 @@ export class PipeVisualEntity implements BaseVisualCamelEntity { const sinkNode = this.getVizNodeFromStep(this.pipe.spec!.sink, 'sink'); /** If there are no steps, we link the `source` and the `sink` together */ + pipeGroupNode.setTitle('Pipe'); pipeGroupNode.addChild(sourceNode); stepNodes.forEach((stepNode) => pipeGroupNode.addChild(stepNode)); pipeGroupNode.addChild(sinkNode); @@ -261,7 +261,10 @@ export class PipeVisualEntity implements BaseVisualCamelEntity { icon, }; - return createVisualizationNode(step?.ref?.name ?? path, data); + const vizNode = createVisualizationNode(step?.ref?.name ?? path, data); + vizNode.setTitle(kameletDefinition?.metadata.name ?? ''); + + return vizNode; } private getVizNodesFromSteps(steps?: PipeStep[]): IVisualizationNode[] { diff --git a/packages/ui/src/models/visualization/flows/support/__snapshots__/camel-component-schema.service.test.ts.snap b/packages/ui/src/models/visualization/flows/support/__snapshots__/camel-component-schema.service.test.ts.snap index 3ef89bf23..e5387ff00 100644 --- a/packages/ui/src/models/visualization/flows/support/__snapshots__/camel-component-schema.service.test.ts.snap +++ b/packages/ui/src/models/visualization/flows/support/__snapshots__/camel-component-schema.service.test.ts.snap @@ -235,7 +235,6 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should build the a ], "type": "object", }, - "title": "route", } `; @@ -589,7 +588,6 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should build the a "title": "To", "type": "object", }, - "title": "log", } `; @@ -668,7 +666,6 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should build the a "title": "Logger", "type": "object", }, - "title": "log", } `; @@ -731,7 +728,6 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should not build a "title": "To", "type": "object", }, - "title": "non-existing-component", } `; @@ -810,7 +806,6 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should transform a "title": "Logger", "type": "object", }, - "title": "log", } `; @@ -925,7 +920,6 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should transform a "title": "To", "type": "object", }, - "title": "bean", } `; @@ -1072,6 +1066,5 @@ exports[`CamelComponentSchemaService getVisualComponentSchema should transform a "title": "To D", "type": "object", }, - "title": "bean", } `; diff --git a/packages/ui/src/models/visualization/flows/support/__snapshots__/kamelet-schema.service.test.ts.snap b/packages/ui/src/models/visualization/flows/support/__snapshots__/kamelet-schema.service.test.ts.snap index 6432840bc..934cbfee9 100644 --- a/packages/ui/src/models/visualization/flows/support/__snapshots__/kamelet-schema.service.test.ts.snap +++ b/packages/ui/src/models/visualization/flows/support/__snapshots__/kamelet-schema.service.test.ts.snap @@ -17,7 +17,6 @@ exports[`KameletSchemaService getVisualComponentSchema should build the appropri "title": "Beer Source", "type": "object", }, - "title": "beer-source", } `; @@ -50,7 +49,6 @@ exports[`KameletSchemaService getVisualComponentSchema should build the appropri "title": "XJ Template Action", "type": "object", }, - "title": "xj-template-action", } `; @@ -71,7 +69,6 @@ exports[`KameletSchemaService getVisualComponentSchema should provide a default "title": "Beer Source", "type": "object", }, - "title": "", } `; diff --git a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts index b25a1d66c..a3655709d 100644 --- a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts +++ b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts @@ -52,7 +52,6 @@ describe('CamelComponentSchemaService', () => { const result = CamelComponentSchemaService.getVisualComponentSchema(path, definition); expect(result).toEqual({ - title: 'non-existing-processor', schema: {}, definition, }); diff --git a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts index d3983a7d1..323d27cfc 100644 --- a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts +++ b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts @@ -33,7 +33,6 @@ export class CamelComponentSchemaService { const updatedDefinition = this.getUpdatedDefinition(camelElementLookup, definition); return { - title: camelElementLookup.componentName ?? camelElementLookup.processorName, schema: this.getSchema(camelElementLookup), definition: updatedDefinition, }; diff --git a/packages/ui/src/models/visualization/flows/support/kamelet-schema.service.ts b/packages/ui/src/models/visualization/flows/support/kamelet-schema.service.ts index a36b78e1e..d5f64c68c 100644 --- a/packages/ui/src/models/visualization/flows/support/kamelet-schema.service.ts +++ b/packages/ui/src/models/visualization/flows/support/kamelet-schema.service.ts @@ -14,7 +14,6 @@ export class KameletSchemaService { const definition = this.getKameletDefinition(stepModel); return { - title: definition?.metadata.name || '', schema: definition?.propertiesSchema || ({} as KaotoSchemaDefinition['schema']), definition: stepModel?.properties || {}, }; diff --git a/packages/ui/src/models/visualization/visualization-node.test.ts b/packages/ui/src/models/visualization/visualization-node.test.ts index f9b8fc6b2..f3127c312 100644 --- a/packages/ui/src/models/visualization/visualization-node.test.ts +++ b/packages/ui/src/models/visualization/visualization-node.test.ts @@ -20,6 +20,10 @@ describe('VisualizationNode', () => { expect(node.id).toEqual('test-1234'); }); + it('should create a node and set the ID as the title', () => { + expect(node.getTitle()).toEqual('test'); + }); + it('should return the base entity ID', () => { const visualEntity = new CamelRouteVisualEntity(camelRouteJson); node = createVisualizationNode('test', { entity: visualEntity }); diff --git a/packages/ui/src/models/visualization/visualization-node.ts b/packages/ui/src/models/visualization/visualization-node.ts index a369ffa06..d14ffcc7c 100644 --- a/packages/ui/src/models/visualization/visualization-node.ts +++ b/packages/ui/src/models/visualization/visualization-node.ts @@ -14,7 +14,12 @@ import { export const createVisualizationNode = ( id: string, data: T, -): IVisualizationNode => new VisualizationNode(getCamelRandomId(id), data); +): IVisualizationNode => { + const vizNode = new VisualizationNode(getCamelRandomId(id), data); + vizNode.setTitle(id); + + return vizNode; +}; /** * VisualizationNode @@ -22,6 +27,7 @@ export const createVisualizationNode = implements IVisualizationNode { + private title = ''; private parentNode: IVisualizationNode | undefined = undefined; private previousNode: IVisualizationNode | undefined = undefined; private nextNode: IVisualizationNode | undefined = undefined; @@ -46,6 +52,14 @@ class VisualizationNode