This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit b23fe7eacc9772969bcf25a8ca72265138559130 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Wed Feb 14 16:44:15 2024 -0500 Incoming navigation for #1109 --- karavan-space/src/App.tsx | 2 +- karavan-space/src/designer/DesignerStore.ts | 17 ++++- karavan-space/src/designer/KaravanDesigner.tsx | 10 +-- .../src/designer/route/DslConnections.tsx | 74 ++++++++++++++++++---- .../src/designer/utils/InfrastructureAPI.ts | 4 +- karavan-space/src/space/SpacePage.tsx | 7 +- karavan-space/src/topology/TopologyApi.tsx | 2 +- karavan-space/src/topology/TopologyStore.ts | 9 --- karavan-space/src/topology/TopologyTab.tsx | 5 +- 9 files changed, 94 insertions(+), 36 deletions(-) diff --git a/karavan-space/src/App.tsx b/karavan-space/src/App.tsx index 77d1289e..2adec90f 100644 --- a/karavan-space/src/App.tsx +++ b/karavan-space/src/App.tsx @@ -33,7 +33,7 @@ import {KnowledgebasePage} from "./knowledgebase/KnowledgebasePage"; import {EventBus} from "./designer/utils/EventBus"; import {Notification} from "./designer/utils/Notification"; import {TopologyTab} from "./topology/TopologyTab"; -import {IntegrationFile} from "./topology/TopologyStore"; +import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; class MenuItem { pageId: string = ''; diff --git a/karavan-space/src/designer/DesignerStore.ts b/karavan-space/src/designer/DesignerStore.ts index ef95b8d4..7a6aab41 100644 --- a/karavan-space/src/designer/DesignerStore.ts +++ b/karavan-space/src/designer/DesignerStore.ts @@ -20,6 +20,7 @@ import {DslPosition, EventBus} from "./utils/EventBus"; import {createWithEqualityFn} from "zustand/traditional"; import {shallow} from "zustand/shallow"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; +import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; interface IntegrationState { integration: Integration; @@ -27,6 +28,9 @@ interface IntegrationState { setIntegration: (integration: Integration, propertyOnly: boolean) => void; propertyOnly: boolean; reset: () => void; + files: IntegrationFile [] + setFiles: (files: IntegrationFile []) => void + resetFiles: (files: IntegrationFile []) => void } export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({ @@ -46,7 +50,18 @@ export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) }, reset: () => { set({integration: Integration.createNew("demo", "plain"), json: '{}', propertyOnly: false}); - } + }, + files: [], + setFiles: (files: IntegrationFile []) => { + set((state: IntegrationState) => { + return {files: files}; + }); + }, + resetFiles: (files: IntegrationFile []) => { + set((state: IntegrationState) => { + return {files: [...files]}; + }); + }, }), shallow) diff --git a/karavan-space/src/designer/KaravanDesigner.tsx b/karavan-space/src/designer/KaravanDesigner.tsx index 05831a38..9fbe3864 100644 --- a/karavan-space/src/designer/KaravanDesigner.tsx +++ b/karavan-space/src/designer/KaravanDesigner.tsx @@ -28,7 +28,7 @@ import { import './karavan.css'; import {RouteDesigner} from "./route/RouteDesigner"; import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml"; -import {Integration} from "karavan-core/lib/model/IntegrationDefinition"; +import {Integration, IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelUi} from "./utils/CamelUi"; import {useDesignerStore, useIntegrationStore} from "./DesignerStore"; @@ -48,7 +48,7 @@ interface Props { onSaveCustomCode: (name: string, code: string) => void onGetCustomCode: (name: string, javaType: string) => Promise<string | undefined> onSavePropertyPlaceholder: (key: string, value: string) => void - onInternalConsumerClick: (uri: string, name: string) => void + onInternalConsumerClick: (uri?: string, name?: string, routeId?: string) => void filename: string yaml: string dark: boolean @@ -57,6 +57,7 @@ interface Props { tab?: "routes" | "rest" | "beans" propertyPlaceholders: string[] beans: RegistryBeanDefinition[] + files: IntegrationFile[] } export function KaravanDesigner(props: Props) { @@ -65,8 +66,8 @@ export function KaravanDesigner(props: Props) { const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans] = useDesignerStore((s) => [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans], shallow) - const [integration, setIntegration] = useIntegrationStore((s) => - [s.integration, s.setIntegration], shallow) + const [integration, setIntegration, resetFiles] = useIntegrationStore((s) => + [s.integration, s.setIntegration, s.resetFiles], shallow) useEffect(() => { const sub = EventBus.onIntegrationUpdate()?.subscribe((update: IntegrationUpdate) => @@ -92,6 +93,7 @@ export function KaravanDesigner(props: Props) { setDark(props.dark); setPropertyPlaceholders(props.propertyPlaceholders) setBeans(props.beans) + resetFiles(props.files) setHideLogDSL(props.hideLogDSL === true); return () => { sub?.unsubscribe(); diff --git a/karavan-space/src/designer/route/DslConnections.tsx b/karavan-space/src/designer/route/DslConnections.tsx index ebb89933..99339f7c 100644 --- a/karavan-space/src/designer/route/DslConnections.tsx +++ b/karavan-space/src/designer/route/DslConnections.tsx @@ -24,28 +24,45 @@ import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt" import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils"; import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; import {v4 as uuidv4} from "uuid"; -import {DeleteElementIcon} from "../utils/ElementIcons"; -import {Button} from "@patternfly/react-core"; +import {Button, Tooltip} from "@patternfly/react-core"; import {InfrastructureAPI} from "../utils/InfrastructureAPI"; +import {getIntegrations} from "../../topology/TopologyApi"; +import {ComponentApi} from "karavan-core/lib/api/ComponentApi"; const overlapGap: number = 40; export function DslConnections() { - const [integration] = useIntegrationStore((state) => [state.integration], shallow) + const [integration, files] = useIntegrationStore((s) => [s.integration, s.files], shallow) const [width, height, top, left, hideLogDSL] = useDesignerStore((s) => [s.width, s.height, s.top, s.left, s.hideLogDSL], shallow) const [steps, addStep, deleteStep, clearSteps] = useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, s.clearSteps], shallow) const [svgKey, setSvgKey] = useState<string>('svgKey'); + const [tons, setTons] = useState<Map<string, string[]>>(new Map<string, string[]>()); useEffect(() => { + const integrations = getIntegrations(files); + setTons(prevState => { + const data = new Map<string, string[]>(); + TopologyUtils.findTopologyOutgoingNodes(integrations).forEach(t => { + const key = (t.step as any)?.uri + ':' + (t.step as any)?.parameters.name; + if (data.has(key)) { + const list = data.get(key) || []; + list.push(t.routeId); + data.set(key, list); + } else { + data.set(key, [t.routeId]); + } + }); + return data; + }); const sub1 = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt)); return () => { sub1?.unsubscribe(); }; - }); + }, [files]); useEffect(() => { const toDelete1: string[] = Array.from(steps.keys()).filter(k => CamelDefinitionApiExt.findElementInIntegration(integration, k) === undefined); @@ -63,11 +80,17 @@ export function DslConnections() { } } + function isElementInternalComponent (element: CamelElement): boolean { + const uri = (element as any).uri; + const component = ComponentApi.findByName(uri); + return component !== undefined && (TopologyUtils.isComponentInternal(component.component.label)); + } + function getIncomings() { let outs: [string, number][] = Array.from(steps.values()) .filter(pos => ["FromDefinition"].includes(pos.step.dslName)) - .filter(pos => !TopologyUtils.isElementInternalComponent(pos.step)) - .filter(pos => !(pos.step.dslName === 'FromDefinition' && TopologyUtils.hasInternalUri(pos.step))) + .filter(pos => !isElementInternalComponent(pos.step)) + // .filter(pos => !(pos.step.dslName === 'FromDefinition' && TopologyUtils.hasInternalUri(pos.step))) .filter(pos => !(pos.step.dslName === 'FromDefinition' && (pos.step as any).uri === 'kamelet:source')) .sort((pos1: DslPosition, pos2: DslPosition) => { const y1 = pos1.headerRect.y + pos1.headerRect.height / 2; @@ -104,9 +127,22 @@ export function DslConnections() { } } + // function getToDirectSteps(name: string) { + // return Array.from(steps.values()) + // .filter(s => s.step.dslName === 'ToDefinition') + // .filter(s => ['direct','seda'].includes((s.step as any)?.uri)) + // .filter(s => (s.step as any)?.parameters?.name === name) + // } + function getIncomingIcons(data: [string, number]) { const pos = steps.get(data[0]); if (pos) { + const step = (pos.step as any); + const uri = step?.uri; + const directOrSeda: boolean = step && uri && step?.dslName === 'FromDefinition' && ['direct','seda'].includes(uri); + const name: string = directOrSeda ? (step?.parameters?.name) : undefined; + const routes = directOrSeda ? tons.get(uri + ':' +name) || [] : []; + // const localDirects = getToDirectSteps(name); const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top; const r = pos.headerRect.height / 2; const incomingX = 20; @@ -116,6 +152,16 @@ export function DslConnections() { <div key={pos.step.uuid + "-icon"} style={{display: "block", position: "absolute", top: imageY, left: imageX}}> {CamelUi.getConnectionIcon(pos.step)} + {routes.map((routeId, index) => + <Tooltip key={`${routeId}:${index}`} content={`Go to route:${routeId}`} position={"right"}> + <Button style={{position: 'absolute', left: 27, top: (index * 16) + (12), whiteSpace: 'nowrap', zIndex: 300, padding: 0}} + variant={'link'} + aria-label="Goto" + onClick={_ => InfrastructureAPI.onInternalConsumerClick(undefined, undefined, routeId)}> + {routeId} + </Button> + </Tooltip> + )} </div> ) } @@ -204,13 +250,15 @@ export function DslConnections() { <div key={pos.step.uuid + "-icon"} style={{display: "block", position: "absolute", top: imageY, left: imageX}}> {CamelUi.getConnectionIcon(pos.step)} - {directOrSeda && - <Button style={{position: 'absolute', right: 27, top: -12, whiteSpace: 'nowrap', zIndex: 300, padding: 0}} - variant={'link'} - aria-label="Goto" - onClick={_ => InfrastructureAPI.onInternalConsumerClick(uri, name)}> - {name} - </Button> + {name !== undefined && + <Tooltip content={`Go to ${uri}:${name}`} position={"left"}> + <Button style={{position: 'absolute', right: 27, top: -12, whiteSpace: 'nowrap', zIndex: 300, padding: 0}} + variant={'link'} + aria-label="Goto" + onClick={_ => InfrastructureAPI.onInternalConsumerClick(uri, name, undefined)}> + {name} + </Button> + </Tooltip> } </div> ) diff --git a/karavan-space/src/designer/utils/InfrastructureAPI.ts b/karavan-space/src/designer/utils/InfrastructureAPI.ts index 77ef1bd3..c386d6fc 100644 --- a/karavan-space/src/designer/utils/InfrastructureAPI.ts +++ b/karavan-space/src/designer/utils/InfrastructureAPI.ts @@ -21,7 +21,7 @@ export class InfrastructureAPI { static onSaveCustomCode: (name: string, code: string) => void; static onSave: (filename: string, yaml: string, propertyOnly: boolean) => void; static onSavePropertyPlaceholder: (key: string, value: string) => void; - static onInternalConsumerClick: (uri: string, name: string) => void; + static onInternalConsumerClick: (uri?: string, name?: string, routeId?: string) => void; static setOnGetCustomCode(onGetCustomCode: (name: string, javaType: string) => Promise<string | undefined>){ this.onGetCustomCode = onGetCustomCode @@ -39,7 +39,7 @@ export class InfrastructureAPI { this.onSavePropertyPlaceholder = onSavePropertyPlaceholder } - static setOnInternalConsumerClick(onInternalConsumerClick:(uri: string, name: string) => void){ + static setOnInternalConsumerClick(onInternalConsumerClick:(uri?: string, name?: string, routeId?: string) => void){ this.onInternalConsumerClick = onInternalConsumerClick } diff --git a/karavan-space/src/space/SpacePage.tsx b/karavan-space/src/space/SpacePage.tsx index 24c62b66..548e5025 100644 --- a/karavan-space/src/space/SpacePage.tsx +++ b/karavan-space/src/space/SpacePage.tsx @@ -28,7 +28,7 @@ import DownloadImageIcon from "@patternfly/react-icons/dist/esm/icons/image-icon import GithubImageIcon from "@patternfly/react-icons/dist/esm/icons/github-icon"; import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon"; import {KaravanDesigner} from "../designer/KaravanDesigner"; -import Editor from "@monaco-editor/react"; +import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; import {UploadModal} from "./UploadModal"; import {EventBus} from "../designer/utils/EventBus"; @@ -111,9 +111,10 @@ export class SpacePage extends React.Component<Props, State> { propertyPlaceholders={[]} beans={[]} onSavePropertyPlaceholder={(key, value) => {}} - onInternalConsumerClick={(uri, name) => { - console.log("onInternalConsumerClick", uri, name) + onInternalConsumerClick={(uri?: string, name?: string, routeId?: string) => { + console.log("onInternalConsumerClick", uri, name, routeId) }} + files={[new IntegrationFile("example.camel.yaml", yaml)]} /> ) } diff --git a/karavan-space/src/topology/TopologyApi.tsx b/karavan-space/src/topology/TopologyApi.tsx index c60c197b..a640d508 100644 --- a/karavan-space/src/topology/TopologyApi.tsx +++ b/karavan-space/src/topology/TopologyApi.tsx @@ -39,7 +39,7 @@ import { TopologyRouteNode } from "karavan-core/lib/model/TopologyDefinition"; import CustomEdge from "./CustomEdge"; -import {IntegrationFile} from "./TopologyStore"; +import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; import CustomGroup from "./CustomGroup"; const NODE_DIAMETER = 60; diff --git a/karavan-space/src/topology/TopologyStore.ts b/karavan-space/src/topology/TopologyStore.ts index 4e9ee643..517e2794 100644 --- a/karavan-space/src/topology/TopologyStore.ts +++ b/karavan-space/src/topology/TopologyStore.ts @@ -18,15 +18,6 @@ import {createWithEqualityFn} from "zustand/traditional"; import {shallow} from "zustand/shallow"; -export class IntegrationFile { - name: string = ''; - code: string = ''; - - constructor(name: string, code: string) { - this.name = name; - this.code = code; - } -} interface TopologyState { selectedIds: string [] diff --git a/karavan-space/src/topology/TopologyTab.tsx b/karavan-space/src/topology/TopologyTab.tsx index 1f2d165e..694eeb64 100644 --- a/karavan-space/src/topology/TopologyTab.tsx +++ b/karavan-space/src/topology/TopologyTab.tsx @@ -31,10 +31,11 @@ import { } from '@patternfly/react-topology'; import {customComponentFactory, getModel} from "./TopologyApi"; import {shallow} from "zustand/shallow"; -import {IntegrationFile, useTopologyStore} from "./TopologyStore"; +import {useTopologyStore} from "./TopologyStore"; import {TopologyPropertiesPanel} from "./TopologyPropertiesPanel"; import {TopologyToolbar} from "./TopologyToolbar"; import {useDesignerStore} from "../designer/DesignerStore"; +import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; interface Props { files: IntegrationFile[], @@ -137,7 +138,7 @@ export function TopologyTab(props: Props) { }); }, [ranker, controller, setRanker]); - return ( + return ( <TopologyView className="topology-panel" contextToolbar={!props.hideToolbar