Skip to content
This repository has been archived by the owner on Mar 27, 2023. It is now read-only.

Commit

Permalink
Add connections to host details pane
Browse files Browse the repository at this point in the history
  • Loading branch information
Michel Zimmer committed Aug 29, 2022
1 parent 999d1b7 commit 2cd688d
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ LABEL org.opencontainers.image.vendor="neuland – Büro für Informatik GmbH"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.title="bandwhichd-ui"
LABEL org.opencontainers.image.description="bandwhichd ui displaying network topology and statistics"
LABEL org.opencontainers.image.version="0.2.0"
LABEL org.opencontainers.image.version="0.3.0"
COPY --from=build --chown=root:root /home/node/dist /usr/share/nginx/html
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bandwhichd-ui",
"version": "0.2.0",
"version": "0.3.0",
"description": "bandwhichd ui displaying network topology and statistics",
"license": "MIT",
"private": true,
Expand Down
14 changes: 12 additions & 2 deletions src/HostDetails.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
.hostDetails {
.host-details {
background-color: #eeeeee;
}

.hostDetails > section {
.host-details > section {
padding: .5em;
}

.host-details > section > ul {
list-style-type: none;
margin: 0;
padding: 0;
}

.host-details > section > ul > li + li {
margin-top: .5em;
}
95 changes: 92 additions & 3 deletions src/HostDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Host } from "./Stats";
import { Map } from "immutable";
import { Connection, Host, HostId, Stats, UnmonitoredHost } from "./Stats";

import style from "./HostDetails.module.css";

export interface HostDetailsProps {
maybeSelectedHost: Host | null;
maybeStats: Stats | null;
maybeSelectedHost: Host & { hostId: HostId } | null;
setMaybeSelectedHostId: (maybeSelectedHostId: HostId | null) => void,
}

export const HostDetails: React.FC<HostDetailsProps> =
Expand All @@ -13,9 +16,95 @@ export const HostDetails: React.FC<HostDetailsProps> =
}
const selectedHost = props.maybeSelectedHost;

return <aside className={style.hostDetails}>
const connectionsToMonitoredHosts = selectedHost
.connections
.flatMap<HostId, { hostDetails: Host, connectionDetails: Connection }>((connection, hostId) => {
if (props.maybeStats === null) {
return Map<HostId, { hostDetails: Host, connectionDetails: Connection }>();
}
const stats = props.maybeStats;

const maybeMonitoredHost = stats.hosts.get(hostId, null);
if (maybeMonitoredHost === null) {
return Map<HostId, { hostDetails: Host, connectionDetails: Connection }>();
}
const monitoredHost = maybeMonitoredHost;

return Map<HostId, { hostDetails: Host, connectionDetails: Connection }>({
[hostId]: {
hostDetails: monitoredHost,
connectionDetails: connection,
},
});
});

const connectionsToUnmonitoredHosts = selectedHost
.connections
.flatMap<HostId, { unmonitoredHostDetails: UnmonitoredHost, connectionDetails: Connection }>((connection, hostId) => {
if (props.maybeStats === null) {
return Map<HostId, { unmonitoredHostDetails: UnmonitoredHost, connectionDetails: Connection }>();
}
const stats = props.maybeStats;

const maybeUnmonitoredHost = stats.unmonitoredHosts.get(hostId, null);
if (maybeUnmonitoredHost === null) {
return Map<HostId, { unmonitoredHostDetails: UnmonitoredHost, connectionDetails: Connection }>();
}
const unmonitoredHost = maybeUnmonitoredHost;

return Map<HostId, { unmonitoredHostDetails: UnmonitoredHost, connectionDetails: Connection }>({
[hostId]: {
unmonitoredHostDetails: unmonitoredHost,
connectionDetails: connection,
},
});
});

return <aside className={style["host-details"]}>
<section>
<p>{selectedHost.hostname}</p>
</section>
<section>
{
connectionsToMonitoredHosts.isEmpty() && <p>No connections to monitored hosts</p>
}
{
!connectionsToMonitoredHosts.isEmpty() && <>
<p>Connections to monitored hosts:</p>
<ul>
{
connectionsToMonitoredHosts
.entrySeq()
.sortBy(([_, {hostDetails}]) => hostDetails.hostname)
.map(([hostId, { hostDetails }]) => <li key={hostId}>
{
hostId === selectedHost.hostId
? <button disabled>{hostDetails.hostname}</button>
: <button onClick={_ => props.setMaybeSelectedHostId(hostId)}>{hostDetails.hostname}</button>
}
</li>)
}
</ul>
</>
}
</section>
<section>
{
connectionsToUnmonitoredHosts.isEmpty() && <p>No connections to unmonitored hosts</p>
}
{
!connectionsToUnmonitoredHosts.isEmpty() && <>
<p>Connections to unmonitored hosts:</p>
<ul>
{
connectionsToUnmonitoredHosts
.entrySeq()
.sortBy(([_, {unmonitoredHostDetails}]) => unmonitoredHostDetails.host)
.map(([hostId, { unmonitoredHostDetails }]) => <li key={hostId}>{unmonitoredHostDetails.host}</li>)
}
</ul>
</>
}
</section>
</aside>;
};
1 change: 1 addition & 0 deletions src/Main.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.main > aside {
flex-grow: 0;
flex-shrink: 0;
overflow-y: scroll;
}

.main > section {
Expand Down
11 changes: 8 additions & 3 deletions src/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ export const Main: React.FC =
const [maybeStats, setMaybeStats] = useState<Stats | null>(null);
const [maybeSelectedHostId, setMaybeSelectedHostId] = useState<HostId | null>(null);

const maybeSelectedHost: Host | null =
const maybeSelectedHostWithoutId =
maybeStats === null || maybeSelectedHostId === null
? null
: maybeStats.hosts.get(maybeSelectedHostId, null);
: maybeStats.hosts.get(maybeSelectedHostId, null)

const maybeSelectedHost: Host & { hostId: HostId } | null =
maybeSelectedHostId === null || maybeSelectedHostWithoutId == null
? null
: { hostId: maybeSelectedHostId, ...maybeSelectedHostWithoutId };

useEffect(() => {
fetchStats(configuration).then(stats => {
Expand All @@ -26,6 +31,6 @@ export const Main: React.FC =
return <main className={styles.main}>
<NodeSelector {...{ maybeStats, maybeSelectedHostId, setMaybeSelectedHostId }} />
<Graph {...{ maybeSelectedHostId, setMaybeSelectedHostId }} />
<HostDetails {...{ maybeSelectedHost }} />
<HostDetails {...{ maybeStats, maybeSelectedHost, setMaybeSelectedHostId }} />
</main>;
};
34 changes: 24 additions & 10 deletions src/Stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,49 @@ import { decode } from "./lib/io-ts/promiseUtils";
const hostIdTag = Symbol("HostId");
export type HostId = string & { readonly _tag: typeof hostIdTag; };
const createHostId: (value: string) => HostId = (value) => value as HostId;
export const hostIdDecoder = Decoder.map(createHostId)(Decoder.string);
const hostIdDecoder = Decoder.map(createHostId)(Decoder.string);

const hostnameTag = Symbol("Hostname");
export type Hostname = string & { readonly _tag: typeof hostnameTag; };
const createHostname: (value: string) => Hostname = (value) => value as Hostname;
export const hostnameDecoder = Decoder.map(createHostname)(Decoder.string);
const hostnameDecoder = Decoder.map(createHostname)(Decoder.string);

export interface Connection { }
const connectionDecoder = Decoder.struct({});

export interface Host {
readonly hostname: Hostname;
readonly connections: Map<HostId, Connection>;
}
export const hostDecoder = Decoder.struct({
const hostDecoder = Decoder.struct({
hostname: hostnameDecoder,
connections: mapDecoder(hostIdDecoder, connectionDecoder),
});

export interface UnmonitoredHost {
host: string;
}
const unmonitoredHostDecoder = Decoder.struct({
host: Decoder.string,
});

export interface Stats {
hosts: Map<HostId, Host>
hosts: Map<HostId, Host>;
unmonitoredHosts: Map<HostId, UnmonitoredHost>;
}
export const statsDecoder = Decoder.struct({
const statsDecoder = Decoder.struct({
hosts: mapDecoder(hostIdDecoder, hostDecoder),
unmonitoredHosts: mapDecoder(hostIdDecoder, unmonitoredHostDecoder),
});

export const fetchStats =
export const fetchStats =
async (configuration: Configuration): Promise<Stats> =>
await fetch(`${configuration.apiServer}/v1/stats`, {
await window.fetch(`${configuration.apiServer}/v1/stats`, {
method: "GET",
headers: {
"Accept": "application/json; q=1.0"
}
})
.then(rejectNonOk())
.then(parseBodyAsJson())
.then(decode(statsDecoder));
.then(rejectNonOk())
.then(parseBodyAsJson())
.then(decode(statsDecoder));
2 changes: 1 addition & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ html, body, #bandwhichd-root {
#bandwhichd-root {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: min-content 1fr min-content;
grid-template-rows: min-content minmax(20em, 1fr) min-content;
}

0 comments on commit 2cd688d

Please sign in to comment.