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
The following commit(s) were added to refs/heads/main by this push: new 64470747 Fix #1309 64470747 is described below commit 64470747568c1ee4e87a15c81b9979b0be6220ee Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Mon Jun 3 17:29:11 2024 -0400 Fix #1309 --- .../src/main/webui/src/editor/DesignerEditor.tsx | 2 +- .../apache/camel/karavan/project/CodeService.java | 5 +- karavan-vscode/src/utils.ts | 2 +- karavan-vscode/webview/topology/CustomNode.tsx | 2 +- karavan-vscode/webview/topology/TopologyApi.tsx | 84 +++++++++++++++++----- .../webview/topology/TopologyPropertiesPanel.tsx | 21 +++++- karavan-vscode/webview/topology/TopologyStore.ts | 15 ++-- karavan-vscode/webview/topology/TopologyTab.tsx | 14 ++-- .../webview/topology/TopologyToolbar.tsx | 22 +++++- karavan-vscode/webview/topology/topology.css | 17 ++++- 10 files changed, 141 insertions(+), 43 deletions(-) diff --git a/karavan-app/src/main/webui/src/editor/DesignerEditor.tsx b/karavan-app/src/main/webui/src/editor/DesignerEditor.tsx index 104321e6..cb182328 100644 --- a/karavan-app/src/main/webui/src/editor/DesignerEditor.tsx +++ b/karavan-app/src/main/webui/src/editor/DesignerEditor.tsx @@ -99,7 +99,7 @@ export function DesignerEditor(props: Props) { } } } else { - const nodes = TopologyUtils.findTopologyOutgoingNodes(integrations) + const nodes = TopologyUtils.findTopologyRouteOutgoingNodes(integrations) .filter(t => t.routeId === routeId); for (const node of nodes) { const switchToFile = files.filter(f => f.name === node.fileName).at(0); diff --git a/karavan-projects/src/main/java/org/apache/camel/karavan/project/CodeService.java b/karavan-projects/src/main/java/org/apache/camel/karavan/project/CodeService.java index b17ddd91..7ae0d662 100644 --- a/karavan-projects/src/main/java/org/apache/camel/karavan/project/CodeService.java +++ b/karavan-projects/src/main/java/org/apache/camel/karavan/project/CodeService.java @@ -173,10 +173,11 @@ public class CodeService { // result.put(APPLICATION_PROPERTIES_FILENAME, getResourceFile(TEMPLATES_PATH + "openshift-" + APPLICATION_PROPERTIES_FILENAME)); // result.put(BUILD_SCRIPT_FILENAME, getResourceFile(TEMPLATES_PATH + "openshift-" + BUILD_SCRIPT_FILENAME)); // } else { - result.put(APPLICATION_PROPERTIES_FILENAME, getResourceFile(TEMPLATES_PATH + "kubernetes-" + APPLICATION_PROPERTIES_FILENAME)); - result.put(BUILD_SCRIPT_FILENAME, getResourceFile(TEMPLATES_PATH + "kubernetes-" + BUILD_SCRIPT_FILENAME)); + result.put(APPLICATION_PROPERTIES_FILENAME, getResourceFile(TEMPLATES_PATH + "kubernetes-" + APPLICATION_PROPERTIES_FILENAME)); + result.put(BUILD_SCRIPT_FILENAME, getResourceFile(TEMPLATES_PATH + "kubernetes-" + BUILD_SCRIPT_FILENAME)); // } result.put(BUILDER_ENV_MAPPING_FILENAME, getResourceFile(TEMPLATES_PATH + BUILDER_ENV_MAPPING_FILENAME)); + result.put(PROJECT_DEPLOYMENT_JKUBE_FILENAME, getResourceFile(TEMPLATES_PATH + PROJECT_DEPLOYMENT_JKUBE_FILENAME)); } else { result.put(APPLICATION_PROPERTIES_FILENAME, getResourceFile(TEMPLATES_PATH + "docker-" + APPLICATION_PROPERTIES_FILENAME)); result.put(BUILD_SCRIPT_FILENAME, getResourceFile(TEMPLATES_PATH + "docker-" + BUILD_SCRIPT_FILENAME)); diff --git a/karavan-vscode/src/utils.ts b/karavan-vscode/src/utils.ts index 2cb0b402..f6eeac96 100644 --- a/karavan-vscode/src/utils.ts +++ b/karavan-vscode/src/utils.ts @@ -512,7 +512,7 @@ export async function getFileWithIntegnalConsumer(fullPath: string, uri: string, export async function getFileWithInternalProducer(fullPath: string, routeId: string) { try { const integrations = await getIntegrations(path.dirname(fullPath)) - const route = TopologyUtils.findTopologyOutgoingNodes(integrations) + const route = TopologyUtils.findTopologyRouteOutgoingNodes(integrations) .filter(t => t.routeId === routeId).at(0); if (route) { return path.join(path.dirname(fullPath), route.fileName); diff --git a/karavan-vscode/webview/topology/CustomNode.tsx b/karavan-vscode/webview/topology/CustomNode.tsx index d683d04f..88263966 100644 --- a/karavan-vscode/webview/topology/CustomNode.tsx +++ b/karavan-vscode/webview/topology/CustomNode.tsx @@ -24,7 +24,7 @@ import {CamelUi} from "../designer/utils/CamelUi"; import './topology.css'; function getIcon(data: any) { - if (['route', 'rest'].includes(data.icon)) { + if (['route', 'rest', 'routeConfiguration'].includes(data.icon)) { return ( <g transform={`translate(14, 14)`}> {getDesignerIcon(data.icon)} diff --git a/karavan-vscode/webview/topology/TopologyApi.tsx b/karavan-vscode/webview/topology/TopologyApi.tsx index a00ac286..90302a02 100644 --- a/karavan-vscode/webview/topology/TopologyApi.tsx +++ b/karavan-vscode/webview/topology/TopologyApi.tsx @@ -26,20 +26,21 @@ import { NodeModel, NodeShape, NodeStatus, - withPanZoom, withSelection + withPanZoom, + withSelection } from '@patternfly/react-topology'; import CustomNode from "./CustomNode"; -import {Integration} from "core/model/IntegrationDefinition"; +import {Integration, IntegrationFile} from "core/model/IntegrationDefinition"; import {CamelDefinitionYaml} from "core/api/CamelDefinitionYaml"; import {TopologyUtils} from "core/api/TopologyUtils"; import { TopologyIncomingNode, TopologyOutgoingNode, TopologyRestNode, + TopologyRouteConfigurationNode, TopologyRouteNode } from "core/model/TopologyDefinition"; import CustomEdge from "./CustomEdge"; -import {IntegrationFile} from "core/model/IntegrationDefinition"; import CustomGroup from "./CustomGroup"; const NODE_DIAMETER = 60; @@ -94,6 +95,28 @@ export function getRoutes(tins: TopologyRouteNode[]): NodeModel[] { return node; }); } +export function getRouteConfigurations(trcs: TopologyRouteConfigurationNode[]): NodeModel[] { + return trcs.map(tin => { + const node: NodeModel = { + id: tin.id, + type: 'node', + label: tin.title, + width: NODE_DIAMETER, + height: NODE_DIAMETER, + shape: NodeShape.rect, + status: NodeStatus.default, + data: { + isAlternate: false, + type: 'routeConfiguration', + icon: 'routeConfiguration', + step: tin.routeConfiguration, + routeConfigurationId: tin.routeConfigurationId, + fileName: tin.fileName, + } + } + return node; + }); +} export function getOutgoingNodes(tons: TopologyOutgoingNode[]): NodeModel[] { return tons.filter(tin => tin.type === 'external').map(tin => { @@ -227,41 +250,64 @@ export function getInternalEdges(tons: TopologyOutgoingNode[], tins: TopologyInc return result; } -export function getModel(files: IntegrationFile[]): Model { +export function getModel(files: IntegrationFile[], grouping?: boolean): Model { const integrations = getIntegrations(files); const tins = TopologyUtils.findTopologyIncomingNodes(integrations); const troutes = TopologyUtils.findTopologyRouteNodes(integrations); - const tons = TopologyUtils.findTopologyOutgoingNodes(integrations); + const tons = TopologyUtils.findTopologyRouteOutgoingNodes(integrations); const trestns = TopologyUtils.findTopologyRestNodes(integrations); + const trcs = TopologyUtils.findTopologyRouteConfigurationNodes(integrations); + const trcons = TopologyUtils.findTopologyRouteConfigurationOutgoingNodes(integrations); + const nodes: NodeModel[] = []; - const groups: NodeModel[] = troutes.map(r => { - const children = [r.id] - children.push(...tins.filter(i => i.routeId === r.routeId && i.type === 'external').map(i => i.id)); - children.push(...tons.filter(i => i.routeId === r.routeId && i.type === 'external').map(i => i.id)); - return { - id: 'group-' + r.routeId, - children: children, - type: 'group', - group: true, - label: r.title, - style: { - padding: 40 - } + const groups: NodeModel[] = []; + + const children1 = [] + children1.push(...tins.filter(i => i.type === 'external').map(i => i.id)); + children1.push(...trestns.map(i => i.id)); + groups.push({ + id: 'consumer-group', + children: children1, + type: 'group', + group: true, + label: 'Consumer group', + style: { + padding: 10, + strokeWidth: "2px", + } + }) + + const children2 = [...tons.filter(i => i.type === 'external').map(i => i.id)]; + groups.push({ + id: 'producer-group', + children: children2, + type: 'group', + group: true, + label: 'Producer group', + style: { + padding: 10, + strokeWidth: "2px" } }) nodes.push(...getRestNodes(trestns)) nodes.push(...getIncomingNodes(tins)) nodes.push(...getRoutes(troutes)) + nodes.push(...getRouteConfigurations(trcs)) nodes.push(...getOutgoingNodes(tons)) - // nodes.push(...groups) + nodes.push(...getOutgoingNodes(trcons)) + + if (grouping === true) { + nodes.push(...groups) + } const edges: EdgeModel[] = []; edges.push(...getIncomingEdges(tins)); edges.push(...getOutgoingEdges(tons)); edges.push(...getRestEdges(trestns, tins)); edges.push(...getInternalEdges(tons, tins)); + edges.push(...getInternalEdges(trcons, tins)); edges.push(...getExternalEdges(tons,tins)); return {nodes: nodes, edges: edges, graph: {id: 'g1', type: 'graph', layout: 'Dagre'}}; diff --git a/karavan-vscode/webview/topology/TopologyPropertiesPanel.tsx b/karavan-vscode/webview/topology/TopologyPropertiesPanel.tsx index 27ef71b5..a32d6969 100644 --- a/karavan-vscode/webview/topology/TopologyPropertiesPanel.tsx +++ b/karavan-vscode/webview/topology/TopologyPropertiesPanel.tsx @@ -48,6 +48,14 @@ export function TopologyPropertiesPanel(props: Props) { return false; } + function isRouteConfiguration() { + return (nodeData && nodeData.type === 'routeConfiguration'); + } + + function isRest() { + return (nodeData && nodeData.type === 'rest'); + } + function isKamelet() { if (nodeData && nodeData.type === 'step') { const uri: string = nodeData?.step?.uri || ''; @@ -70,7 +78,16 @@ export function TopologyPropertiesPanel(props: Props) { } function getTitle () { - return isRoute() ? "Route" : (isKamelet() ? "Kamelet" : "Component"); + if (isRoute()) { + return "Route"; + } else if (isKamelet()) { + return "Kamelet"; + } else if (isRouteConfiguration()) { + return "Route Configuration"; + } else if (isRest()) { + return "REST"; + } + return "Component"; } function getHeader() { @@ -118,7 +135,7 @@ export function TopologyPropertiesPanel(props: Props) { return ( <TopologySideBar className="topology-sidebar" - show={selectedIds.length > 0} + show={selectedIds.length > 0 && nodeData} header={getHeader()} > <DslProperties designerType={'routes'}/> diff --git a/karavan-vscode/webview/topology/TopologyStore.ts b/karavan-vscode/webview/topology/TopologyStore.ts index 517e2794..514f1b37 100644 --- a/karavan-vscode/webview/topology/TopologyStore.ts +++ b/karavan-vscode/webview/topology/TopologyStore.ts @@ -28,6 +28,8 @@ interface TopologyState { setRanker: (ranker: string) => void nodeData: any setNodeData: (nodeData: any) => void + showGroups?: boolean + setShowGroups: (showGroups: boolean) => void } export const useTopologyStore = createWithEqualityFn<TopologyState>((set) => ({ @@ -50,8 +52,13 @@ export const useTopologyStore = createWithEqualityFn<TopologyState>((set) => ({ }, nodeData: undefined, setNodeData: (nodeData: any) => { - set((state: TopologyState) => { - return {nodeData: nodeData}; - }); -}, + set((state: TopologyState) => { + return {nodeData: nodeData}; + }); + }, + setShowGroups: (showGroups: boolean) => { + set((state: TopologyState) => { + return {showGroups: showGroups}; + }); + }, }), shallow) diff --git a/karavan-vscode/webview/topology/TopologyTab.tsx b/karavan-vscode/webview/topology/TopologyTab.tsx index 474899fc..51f5d24e 100644 --- a/karavan-vscode/webview/topology/TopologyTab.tsx +++ b/karavan-vscode/webview/topology/TopologyTab.tsx @@ -49,8 +49,8 @@ interface Props { export function TopologyTab(props: Props) { - const [selectedIds, setSelectedIds, setFileName, ranker, setRanker, setNodeData] = useTopologyStore((s) => - [s.selectedIds, s.setSelectedIds, s.setFileName, s.ranker, s.setRanker, s.setNodeData], shallow); + const [selectedIds, setSelectedIds, setFileName, ranker, setRanker, setNodeData, showGroups] = useTopologyStore((s) => + [s.selectedIds, s.setSelectedIds, s.setFileName, s.ranker, s.setRanker, s.setNodeData, s.showGroups], shallow); const [setSelectedStep] = useDesignerStore((s) => [s.setSelectedStep], shallow) function setTopologySelected(model: Model, ids: string []) { @@ -60,8 +60,8 @@ export function TopologyTab(props: Props) { if (node && node.length > 0) { const data = node[0].data; setNodeData(data); - setFileName(data.fileName) - if (data.step) { + if (data && data.step) { + setFileName(data.fileName) setSelectedStep(data.step) } else { setSelectedStep(undefined); @@ -72,7 +72,7 @@ export function TopologyTab(props: Props) { } const controller = React.useMemo(() => { - const model = getModel(props.files); + const model = getModel(props.files, showGroups); const newController = new Visualization(); newController.registerLayoutFactory((_, graph) => new DagreLayout(graph, { @@ -99,9 +99,9 @@ export function TopologyTab(props: Props) { React.useEffect(() => { setSelectedIds([]) - const model = getModel(props.files); + const model = getModel(props.files, showGroups); controller.fromModel(model, false); - }, [ranker, controller, setSelectedIds, props.files]); + }, [ranker, controller, setSelectedIds, props.files, showGroups]); const controlButtons = React.useMemo(() => { // const customButtons = [ diff --git a/karavan-vscode/webview/topology/TopologyToolbar.tsx b/karavan-vscode/webview/topology/TopologyToolbar.tsx index 5dbe4949..0de61fee 100644 --- a/karavan-vscode/webview/topology/TopologyToolbar.tsx +++ b/karavan-vscode/webview/topology/TopologyToolbar.tsx @@ -17,10 +17,12 @@ import * as React from 'react'; import { - Button, ToolbarContent, + Button, Switch, ToolbarContent, ToolbarItem, Tooltip } from '@patternfly/react-core'; import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; +import {useTopologyStore} from "./TopologyStore"; +import {shallow} from "zustand/shallow"; interface Props { onClickAddRoute: () => void @@ -31,8 +33,22 @@ interface Props { export function TopologyToolbar (props: Props) { + const [showGroups, setShowGroups] = useTopologyStore((s) => + [s.showGroups, s.setShowGroups], shallow); + return ( - <ToolbarContent> + <div className='topology-toolbar'> + <ToolbarItem className="group-switch"> + <Tooltip content={"Show Consumer and Producer Groups"} position={"bottom-start"}> + <Switch + id="reversed-switch" + label="Groups" + isChecked={showGroups} + onChange={(_, checked) => setShowGroups(checked)} + isReversed + /> + </Tooltip> + </ToolbarItem> <ToolbarItem align={{default:"alignRight"}}> <Tooltip content={"Add Integration Route"} position={"bottom"}> <Button className="dev-action-button" size="sm" @@ -77,6 +93,6 @@ export function TopologyToolbar (props: Props) { </Button> </Tooltip> </ToolbarItem> - </ToolbarContent> + </div> ) } \ No newline at end of file diff --git a/karavan-vscode/webview/topology/topology.css b/karavan-vscode/webview/topology/topology.css index 24fe7679..7d21c7b2 100644 --- a/karavan-vscode/webview/topology/topology.css +++ b/karavan-vscode/webview/topology/topology.css @@ -15,17 +15,28 @@ * limitations under the License. */ +.karavan .topology-panel .topology-toolbar { + width: 100%; + display: flex; + flex-direction: row; +} + +.karavan .topology-panel .topology-toolbar .group-switch { + flex-grow: 4; + margin-top: auto; + margin-bottom: auto; + margin-left: 6px; +} + .karavan .topology-panel .pf-v5-c-toolbar { padding: 0; - display: flex; - flex-direction: column; - align-items: flex-end; height: fit-content; row-gap: 0; } .karavan .topology-panel .pf-v5-c-toolbar .pf-v5-c-toolbar__content { padding: 0 6px 0 0; + width: 100%; } .karavan .topology-panel .pf-v5-c-toolbar .pf-v5-c-toolbar__content-section {