[
https://issues.apache.org/jira/browse/ARTEMIS-4680?focusedWorklogId=917106&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-917106
]
ASF GitHub Bot logged work on ARTEMIS-4680:
-------------------------------------------
Author: ASF GitHub Bot
Created on: 01/May/24 06:47
Start Date: 01/May/24 06:47
Worklog Time Spent: 10m
Work Description: andytaylor commented on code in PR #2:
URL:
https://github.com/apache/activemq-artemis-console/pull/2#discussion_r1585945469
##########
artemis-console-extension/artemis-extension/src/artemis-extension/artemis/brokers/BrokerTopology.tsx:
##########
@@ -0,0 +1,671 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as React from 'react';
+
+import { RegionsIcon as Icon1 } from '@patternfly/react-icons';
+import { FolderOpenIcon as Icon2 } from '@patternfly/react-icons';
+import { ClusterIcon } from '@patternfly/react-icons';
+
+import {
+ ColaLayout,
+ ComponentFactory,
+ DefaultEdge,
+ DefaultGroup,
+ DefaultNode,
+ DragObjectWithType,
+ Edge,
+ EdgeModel,
+ EdgeStyle,
+ Graph,
+ GraphComponent,
+ graphDropTargetSpec,
+ GRAPH_LAYOUT_END_EVENT,
+ groupDropTargetSpec,
+ Layout,
+ LayoutFactory,
+ Model,
+ ModelKind,
+ Node,
+ nodeDragSourceSpec,
+ nodeDropTargetSpec,
+ NodeModel,
+ NodeShape,
+ NodeStatus,
+ observer,
+ SELECTION_EVENT,
+ TopologyView,
+ Visualization,
+ VisualizationProvider,
+ VisualizationSurface,
+ withDndDrop,
+ WithDndDropProps,
+ withDragNode,
+ WithDragNodeProps,
+ withPanZoom,
+ withSelection,
+ WithSelectionProps,
+ withTargetDrag
+} from '@patternfly/react-topology';
+import { useEffect, useState } from 'react';
+import { artemisService, BrokerInfo } from '../artemis-service';
+import { eventService } from '@hawtio/react';
+import { ToolbarItem, Select, SelectVariant, SelectOption } from
'@patternfly/react-core';
+
+
+const BadgeColors = [
+ {
+ name: 'A',
+ badgeColor: '#ace12e',
+ badgeTextColor: '#0f280d',
+ badgeBorderColor: '#486b00'
+ },
+ {
+ name: 'B',
+ badgeColor: '#F2F0FC',
+ badgeTextColor: '#5752d1',
+ badgeBorderColor: '#CBC1FF'
+ },
+ {
+ name: 'Broker',
+ badgeColor: '#c12766',
+ badgeTextColor: 'white',
+ badgeBorderColor: '#CBC1FF'
+ },
+ {
+ name: 'Address',
+ badgeColor: '#3e489f',
+ badgeTextColor: 'white',
+ badgeBorderColor: '#CBC1FF'
+ },
+ {
+ name: 'Queue',
+ badgeColor: '#50621d',
+ badgeTextColor: 'white',
+ badgeBorderColor: '#CBC1FF'
+ },
+ {
+ name: 'Internal Address',
+ badgeColor: 'white',
+ badgeTextColor: 'black',
+ badgeBorderColor: '#CBC1FF'
+ },
+ {
+ name: 'Internal Queue',
+ badgeColor: 'white',
+ badgeTextColor: 'black',
+ badgeBorderColor: '#CBC1FF'
+ }
+];
+
+interface ControllerState {
+ selectedIds: string[];
+ viewOptions: ViewOptions;
+}
+
+type CustomNodeProps = {
+ element: Node;
+} & WithDragNodeProps;
+
+const CustomNode: React.FC<CustomNodeProps & WithSelectionProps &
WithDragNodeProps & WithDndDropProps> = ({ element, onSelect, selected, ...rest
}) => {
+
+ const data = element.getData();
+ const Icon = data.isAlternate ? Icon2 : Icon1;
+ const badgeColors = BadgeColors.find(badgeColor => badgeColor.name ===
data.badge);
+
+ return (
+ <DefaultNode
+ element={element}
+ showStatusDecorator
+ badgeColor={badgeColors?.badgeColor}
+ badgeTextColor={badgeColors?.badgeTextColor}
+ badgeBorderColor={badgeColors?.badgeBorderColor}
+ className="artemisBroker"
+ onSelect={onSelect}
+ selected={selected}
+ {...rest}
+ >
+ <g transform={`translate(25, 25)`}>
+ <Icon style={{ color: '#393F44' }} width={25} height={25} />
+ </g>
+ </DefaultNode>
+ );
+};
+
+const BrokerCustomNode: React.FC<CustomNodeProps & WithSelectionProps &
WithDragNodeProps & WithDndDropProps> = observer(({ element, onSelect,
selected, ...rest }) => {
+ const data = element.getData();
+ const Icon = ClusterIcon;
+ const badgeColors = BadgeColors.find(badgeColor => badgeColor.name ===
data.badge);
+ const { viewOptions } = element.getController().getState<ControllerState>();
+
+ return (
+ <DefaultNode
+ element={element}
+ showStatusDecorator
+ badge={data.badge}
+ badgeColor={badgeColors?.badgeColor}
+ badgeTextColor={badgeColors?.badgeTextColor}
+ badgeBorderColor={badgeColors?.badgeBorderColor}
+ showLabel={viewOptions.showLabels}
+ className="artemisBroker"
+ onSelect={onSelect}
+ selected={selected}
+ {...rest}
+ >
+ <g transform={`translate(25, 25)`}>
+ <Icon style={{ color: '#393F44' }} width={25} height={25} />
+ </g>
+ </DefaultNode>
+ );
+});
+
+const AddressCustomNode: React.FC<CustomNodeProps & WithSelectionProps &
WithDragNodeProps & WithDndDropProps> = ({ element, onSelect, selected, ...rest
}) => {
+ const data = element.getData();
+ const badgeColors = BadgeColors.find(badgeColor => badgeColor.name ===
data.badge);
+ const { viewOptions } = element.getController().getState<ControllerState>();
+
+ return (
+ <DefaultNode
+ element={element}
+ showStatusDecorator
+ badge={data.badge}
+ badgeColor={badgeColors?.badgeColor}
+ badgeTextColor={badgeColors?.badgeTextColor}
+ badgeBorderColor={badgeColors?.badgeBorderColor}
+ showLabel={viewOptions.showLabels}
+ onSelect={onSelect}
+ selected={selected}
+ className="artemisAddress"
+ {...rest}
+ >
+ </DefaultNode>
+ );
+};
+
+const InternalAddressCustomNode: React.FC<CustomNodeProps & WithSelectionProps
& WithDragNodeProps & WithDndDropProps> = ({ element, onSelect, selected,
...rest }) => {
+ const data = element.getData();
+ const badgeColors = BadgeColors.find(badgeColor => badgeColor.name ===
data.badge);
+ const { viewOptions } = element.getController().getState<ControllerState>();
+
+ return (
+ <DefaultNode
+ element={element}
+ showStatusDecorator
+ badge={data.badge}
+ badgeColor={badgeColors?.badgeColor}
+ badgeTextColor={badgeColors?.badgeTextColor}
+ badgeBorderColor={badgeColors?.badgeBorderColor}
+ showLabel={viewOptions.showLabels}
+ className="artemisInternalAddress"
+ onSelect={onSelect}
+ selected={selected}
+ {...rest}
+ >
+ </DefaultNode>
+ );
+};
+
+
+const QueueCustomNode: React.FC<CustomNodeProps & WithSelectionProps &
WithDragNodeProps & WithDndDropProps> = ({ element, onSelect, selected, ...rest
}) => {
+ const data = element.getData();
+ const badgeColors = BadgeColors.find(badgeColor => badgeColor.name ===
data.badge);
+ const { viewOptions } = element.getController().getState<ControllerState>();
+
+ return (
+ <DefaultNode
+ element={element}
+ showStatusDecorator
+ badge={data.badge}
+ badgeColor={badgeColors?.badgeColor}
+ badgeTextColor={badgeColors?.badgeTextColor}
+ badgeBorderColor={badgeColors?.badgeBorderColor}
+ showLabel={viewOptions.showLabels}
+ className="artemisQueue"
+ onSelect={onSelect}
+ selected={selected}
+ {...rest}
+ >
+ </DefaultNode>
+ );
+};
+
+const InternalQueueCustomNode: React.FC<CustomNodeProps & WithSelectionProps &
WithDragNodeProps & WithDndDropProps> = ({ element, onSelect, selected, ...rest
}) => {
+ const data = element.getData();
+ const badgeColors = BadgeColors.find(badgeColor => badgeColor.name ===
data.badge);
+ const { viewOptions } = element.getController().getState<ControllerState>();
+
+ return (
+ <DefaultNode
+ element={element}
+ showStatusDecorator
+ badge={data.badge}
+ badgeColor={badgeColors?.badgeColor}
+ badgeTextColor={badgeColors?.badgeTextColor}
+ badgeBorderColor={badgeColors?.badgeBorderColor}
+ showLabel={viewOptions.showLabels}
+ className="artemisInternalQueue"
+ onSelect={onSelect}
+ selected={selected}
+ {...rest}
+ >
+ </DefaultNode>
+ );
+};
+
+const customLayoutFactory: LayoutFactory = (type: string, graph: Graph):
Layout | undefined => {
+ switch (type) {
+ case 'Cola':
+ return new ColaLayout(graph);
+ default:
+ return new ColaLayout(graph, { layoutOnDrag: true });
+ }
+};
+
+const CONNECTOR_TARGET_DROP = 'connector-target-drop';
+
+const customComponentFactory: ComponentFactory = (kind: ModelKind, type:
string): any => {
+ switch (type) {
+ case 'group':
+ return
withDndDrop(groupDropTargetSpec)(withDragNode(nodeDragSourceSpec('group'))(withSelection()(DefaultGroup)));
+ default:
+ switch (kind) {
+ case ModelKind.graph:
+ return
withDndDrop(graphDropTargetSpec())(withPanZoom()(GraphComponent));
+ case ModelKind.node:
+ switch(type) {
+ case 'broker':
+ return withDndDrop(nodeDropTargetSpec([CONNECTOR_TARGET_DROP]))(
+ withDragNode(nodeDragSourceSpec('node', true,
true))(BrokerCustomNode));
+ case 'address':
+ return withDndDrop(nodeDropTargetSpec([CONNECTOR_TARGET_DROP]))(
+ withDragNode(nodeDragSourceSpec('node', true,
true))(AddressCustomNode));
+ case 'queue':
+ return withDndDrop(nodeDropTargetSpec([CONNECTOR_TARGET_DROP]))(
+ withDragNode(nodeDragSourceSpec('node', true,
true))(QueueCustomNode));
+ case 'internalAddress':
+ return withDndDrop(nodeDropTargetSpec([CONNECTOR_TARGET_DROP]))(
+ withDragNode(nodeDragSourceSpec('node', true,
true))(InternalAddressCustomNode));
+ case 'internalQueue':
+ return withDndDrop(nodeDropTargetSpec([CONNECTOR_TARGET_DROP]))(
+ withDragNode(nodeDragSourceSpec('node', true,
true))(InternalQueueCustomNode));
+ default:
+ return withDndDrop(nodeDropTargetSpec([CONNECTOR_TARGET_DROP]))(
+ withDragNode(nodeDragSourceSpec('node', true,
true))(CustomNode));
+ }
+ case ModelKind.edge:
+ return withTargetDrag<
+ DragObjectWithType,
+ Node,
+ { dragging?: boolean },
+ {
+ element: Edge;
+ }
+ >({
+ item: { type: CONNECTOR_TARGET_DROP },
+ begin: (monitor, props) => {
+ props.element.raise();
+ return props.element;
+ },
+ drag: (event, monitor, props) => {
+ props.element.setEndPoint(event.x, event.y);
+ },
+ end: (dropResult, monitor, props) => {
+ if (monitor.didDrop() && dropResult && props) {
+ props.element.setTarget(dropResult);
+ }
+ props.element.setEndPoint();
+ },
+ collect: monitor => ({
+ dragging: monitor.isDragging()
+ })
+ // @ts-ignore
+ })(DefaultEdge);
+ default:
+ return undefined;
+ }
+ }
+};
+
+const BROKER_NODE_DIAMETER = 75;
+const ADDRESS_NODE_DIAMETER = 50;
+const QUEUE_NODE_DIAMETER = 50;
+
+const NODES: NodeModel[] = [];
+const EDGES: EdgeModel[] = [];
+
+interface ViewOptions {
+ showLabels: boolean;
+ showAddresses: boolean;
+ showQueues: boolean;
+ showInternalAddresses: boolean;
+ showInternalQueues: boolean;
+ showConnectors: boolean;
+}
+
+export const DefaultViewOptions: ViewOptions = {
+ showLabels: false,
Review Comment:
I have enabled labels by default.
Issue Time Tracking
-------------------
Worklog Id: (was: 917106)
Time Spent: 8h 50m (was: 8h 40m)
> Upgrade the console to use HawtIO 4
> -----------------------------------
>
> Key: ARTEMIS-4680
> URL: https://issues.apache.org/jira/browse/ARTEMIS-4680
> Project: ActiveMQ Artemis
> Issue Type: Improvement
> Components: Web Console
> Reporter: Andy Taylor
> Assignee: Andy Taylor
> Priority: Major
> Time Spent: 8h 50m
> Remaining Estimate: 0h
>
> The current console is based upon HawtIO 1 which in turn is built on
> Bootstrap. Bootstrap is old and no longer actively being maintained.
>
> This improvement is to migrate the current console to use HawtIO 4 which i
> based on Typescript, react and Patternfly.
>
> A WIP can be found
> [here|https://github.com/andytaylor/activemq-artemis/tree/artemis-console-ng]
--
This message was sent by Atlassian Jira
(v8.20.10#820010)