mirror of
https://github.com/Noratrieb/haesli.git
synced 2026-01-16 12:45:04 +01:00
do some graph things
This commit is contained in:
parent
d77c31aaad
commit
f1bded7192
6 changed files with 433 additions and 25 deletions
|
|
@ -15,3 +15,7 @@ td {
|
|||
border-collapse: collapse;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.graph {
|
||||
height: 50vh;
|
||||
}
|
||||
|
|
|
|||
112
haesli_dashboard/frontend/src/components/EntityGraph.tsx
Normal file
112
haesli_dashboard/frontend/src/components/EntityGraph.tsx
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import React from 'react';
|
||||
import { GraphView } from 'react-digraph';
|
||||
import { Binding, Data, Exchange } from '../types';
|
||||
|
||||
const sample = {
|
||||
nodes: [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Exchange A',
|
||||
type: 'empty',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Queue A',
|
||||
type: 'empty',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 1,
|
||||
target: 2,
|
||||
type: 'emptyEdge',
|
||||
},
|
||||
{
|
||||
source: 2,
|
||||
target: 4,
|
||||
type: 'emptyEdge',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const graphConfig = {
|
||||
nodeTypes: {
|
||||
exchange: {
|
||||
// required to show empty nodes
|
||||
typeText: 'Exchange',
|
||||
shapeId: '#empty', // relates to the type property of a node
|
||||
shape: (
|
||||
<symbol viewBox="0 0 100 100" id="empty" key="0">
|
||||
<circle cx='50' cy='50' r='45'/>
|
||||
</symbol>
|
||||
),
|
||||
},
|
||||
queue: {
|
||||
// required to show empty nodes
|
||||
typeText: 'Queue',
|
||||
shapeId: '#empty', // relates to the type property of a node
|
||||
shape: (
|
||||
<symbol viewBox="0 0 100 100" id="empty" key="0">
|
||||
<circle cx='50' cy='50' r='45'/>
|
||||
</symbol>
|
||||
),
|
||||
},
|
||||
},
|
||||
nodeSubtypes: {},
|
||||
edgeTypes: {},
|
||||
};
|
||||
|
||||
type Props = {
|
||||
data: Data;
|
||||
};
|
||||
|
||||
const SPACE = 200;
|
||||
|
||||
const EntityGraph = ({ data }: Props) => {
|
||||
const queueTotal = data.queues.length * SPACE / 2;
|
||||
const queues = data.queues.map((q, i) => ({
|
||||
id: q.name,
|
||||
title: q.name,
|
||||
y: 300,
|
||||
x: (i * SPACE) - queueTotal,
|
||||
type: 'queue',
|
||||
}));
|
||||
const exchTotal = data.exchanges.length * SPACE / 2;
|
||||
const exchanges = data.exchanges.map((e, i) => ({
|
||||
id: e.name,
|
||||
title: e.name,
|
||||
y: 0,
|
||||
x: (i * SPACE) - exchTotal,
|
||||
type: 'exchange',
|
||||
}));
|
||||
|
||||
const nodes = [...queues, ...exchanges];
|
||||
|
||||
const edges = data.exchanges
|
||||
.flatMap((e) => e.bindings.map((b) => [b, e] as [Binding, Exchange]))
|
||||
.map(([b, e]) => ({
|
||||
source: b.queue,
|
||||
target: e.name,
|
||||
label_to: `'${b.routingKey}'`,
|
||||
type: 'emptyEdge'
|
||||
}));
|
||||
|
||||
const nodeTypes = graphConfig.nodeTypes;
|
||||
const nodeSubtypes = graphConfig.nodeSubtypes;
|
||||
const edgeTypes = graphConfig.edgeTypes;
|
||||
|
||||
return (
|
||||
<div className="graph">
|
||||
<GraphView
|
||||
nodeKey="id"
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
nodeTypes={nodeTypes}
|
||||
nodeSubtypes={nodeSubtypes}
|
||||
edgeTypes={edgeTypes}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EntityGraph;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
||||
import Table from './table';
|
||||
import type { Data } from '../types';
|
||||
import EntityGraph from './EntityGraph';
|
||||
|
||||
const fetchData = async (prefix: string): Promise<Data> => {
|
||||
const url = `${prefix}api/data`;
|
||||
|
|
@ -27,6 +28,10 @@ const DataPage: FC<Props> = ({ prefix }) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<section>
|
||||
<h2>Graph</h2>
|
||||
{data ? <EntityGraph data={data} /> : <div>Loading...</div>}
|
||||
</section>
|
||||
<section>
|
||||
<h2>Connections</h2>
|
||||
{data ? (
|
||||
|
|
@ -42,22 +47,6 @@ const DataPage: FC<Props> = ({ prefix }) => {
|
|||
<div>Loading...</div>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
<h2>Queues</h2>
|
||||
{data ? (
|
||||
<Table
|
||||
headers={['Queue ID', 'Name', 'Durable', 'Message Count']}
|
||||
rows={data.queues.map((queue) => [
|
||||
queue.id,
|
||||
queue.name,
|
||||
queue.durable ? 'Yes' : 'No',
|
||||
queue.messages,
|
||||
])}
|
||||
/>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
<h2>Channels</h2>
|
||||
{data ? (
|
||||
|
|
@ -81,6 +70,37 @@ const DataPage: FC<Props> = ({ prefix }) => {
|
|||
<div>Loading...</div>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
<h2>Queues</h2>
|
||||
{data ? (
|
||||
<Table
|
||||
headers={['Queue ID', 'Name', 'Durable', 'Message Count']}
|
||||
rows={data.queues.map((queue) => [
|
||||
queue.id,
|
||||
queue.name,
|
||||
queue.durable ? 'Yes' : 'No',
|
||||
queue.messages,
|
||||
])}
|
||||
/>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
<h2>Exchanges</h2>
|
||||
{data ? (
|
||||
<Table
|
||||
headers={['Name', 'Durable', 'Bindings']}
|
||||
rows={data.exchanges.map((exchange) => [
|
||||
exchange.name,
|
||||
exchange.durable ? 'Yes' : 'No',
|
||||
exchange.bindings.length,
|
||||
])}
|
||||
/>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,7 +16,19 @@ export type Queue = {
|
|||
messages: number;
|
||||
};
|
||||
|
||||
export type Binding = {
|
||||
queue: string;
|
||||
routingKey: string;
|
||||
};
|
||||
|
||||
export type Exchange = {
|
||||
name: string;
|
||||
durable: boolean;
|
||||
bindings: ReadonlyArray<Binding>;
|
||||
};
|
||||
|
||||
export type Data = {
|
||||
connections: ReadonlyArray<Connection>;
|
||||
queues: ReadonlyArray<Queue>;
|
||||
exchanges: ReadonlyArray<Exchange>;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue