mirror of
https://github.com/jcreek/OpenNetworkDiagram.git
synced 2026-07-12 18:43:44 +00:00
fix(#2): harden network data parsing and link resolution
This commit is contained in:
@@ -20,7 +20,15 @@ export default async function loadNetworkData(jsonPath: string): Promise<Network
|
|||||||
throw new Error(`Failed to load ${jsonPath} (${response.status} ${response.statusText})`);
|
throw new Error(`Failed to load ${jsonPath} (${response.status} ${response.statusText})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawData: unknown = await response.json();
|
let rawData: unknown;
|
||||||
|
try {
|
||||||
|
rawData = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to parse JSON at ${jsonPath}`, {
|
||||||
|
cause: error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!isNetworkData(rawData)) {
|
if (!isNetworkData(rawData)) {
|
||||||
throw new Error(`JSON at ${jsonPath} is not valid network data`);
|
throw new Error(`JSON at ${jsonPath} is not valid network data`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,16 @@ import type { ConnectedPortRef, NetworkData, Port } from '../types';
|
|||||||
import type { GraphEdgeElement, GraphNodeElement, GraphTransformResult } from './types';
|
import type { GraphEdgeElement, GraphNodeElement, GraphTransformResult } from './types';
|
||||||
|
|
||||||
interface EndpointOwner {
|
interface EndpointOwner {
|
||||||
|
kind: 'machine' | 'device';
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
name: string;
|
name: string;
|
||||||
ports: Map<string, Port>;
|
ports: Map<string, Port>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toOwnerKey(kind: EndpointOwner['kind'], name: string): string {
|
||||||
|
return `${kind}:${name}`;
|
||||||
|
}
|
||||||
|
|
||||||
function toMachineNodeId(name: string): string {
|
function toMachineNodeId(name: string): string {
|
||||||
return `machine:${encodeURIComponent(name)}`;
|
return `machine:${encodeURIComponent(name)}`;
|
||||||
}
|
}
|
||||||
@@ -58,7 +63,7 @@ export function transformNetworkDataToGraph(data: NetworkData): GraphTransformRe
|
|||||||
vmCount: number;
|
vmCount: number;
|
||||||
}
|
}
|
||||||
> = {};
|
> = {};
|
||||||
const ownersByName = new Map<string, EndpointOwner>();
|
const ownersByKey = new Map<string, EndpointOwner>();
|
||||||
const seenWarnings = new Set<string>();
|
const seenWarnings = new Set<string>();
|
||||||
|
|
||||||
const warn = (message: string) => {
|
const warn = (message: string) => {
|
||||||
@@ -69,13 +74,14 @@ export function transformNetworkDataToGraph(data: NetworkData): GraphTransformRe
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addOwner = (owner: EndpointOwner) => {
|
const addOwner = (owner: EndpointOwner) => {
|
||||||
if (ownersByName.has(owner.name)) {
|
const ownerKey = toOwnerKey(owner.kind, owner.name);
|
||||||
|
if (ownersByKey.has(ownerKey)) {
|
||||||
warn(
|
warn(
|
||||||
`Duplicate node name "${owner.name}" found. First occurrence will be used for link resolution.`
|
`Duplicate ${owner.kind} name "${owner.name}" found. First occurrence will be used for link resolution.`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ownersByName.set(owner.name, owner);
|
ownersByKey.set(ownerKey, owner);
|
||||||
};
|
};
|
||||||
|
|
||||||
let hostingEdgeCounter = 0;
|
let hostingEdgeCounter = 0;
|
||||||
@@ -163,6 +169,7 @@ export function transformNetworkDataToGraph(data: NetworkData): GraphTransformRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
addOwner({
|
addOwner({
|
||||||
|
kind: 'machine',
|
||||||
nodeId: machineNodeId,
|
nodeId: machineNodeId,
|
||||||
name: machine.machineName,
|
name: machine.machineName,
|
||||||
ports: machinePorts
|
ports: machinePorts
|
||||||
@@ -198,6 +205,7 @@ export function transformNetworkDataToGraph(data: NetworkData): GraphTransformRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
addOwner({
|
addOwner({
|
||||||
|
kind: 'device',
|
||||||
nodeId: deviceNodeId,
|
nodeId: deviceNodeId,
|
||||||
name: device.name,
|
name: device.name,
|
||||||
ports: devicePortMap
|
ports: devicePortMap
|
||||||
@@ -207,12 +215,21 @@ export function transformNetworkDataToGraph(data: NetworkData): GraphTransformRe
|
|||||||
const seenPhysicalConnections = new Set<string>();
|
const seenPhysicalConnections = new Set<string>();
|
||||||
let physicalEdgeCounter = 0;
|
let physicalEdgeCounter = 0;
|
||||||
|
|
||||||
for (const owner of ownersByName.values()) {
|
for (const owner of ownersByKey.values()) {
|
||||||
for (const port of owner.ports.values()) {
|
for (const port of owner.ports.values()) {
|
||||||
const connectedTo = parseConnectedTo(port.connectedTo);
|
const connectedTo = parseConnectedTo(port.connectedTo);
|
||||||
if (!connectedTo) continue;
|
if (!connectedTo) continue;
|
||||||
|
|
||||||
const targetOwner = ownersByName.get(connectedTo.device);
|
const machineTargetOwner = ownersByKey.get(toOwnerKey('machine', connectedTo.device));
|
||||||
|
const deviceTargetOwner = ownersByKey.get(toOwnerKey('device', connectedTo.device));
|
||||||
|
if (machineTargetOwner && deviceTargetOwner) {
|
||||||
|
warn(
|
||||||
|
`Link from "${owner.name}:${port.portName}" references ambiguous device "${connectedTo.device}" (matches both machine and device names).`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetOwner = machineTargetOwner ?? deviceTargetOwner;
|
||||||
if (!targetOwner) {
|
if (!targetOwner) {
|
||||||
warn(
|
warn(
|
||||||
`Link from "${owner.name}:${port.portName}" references unknown device "${connectedTo.device}".`
|
`Link from "${owner.name}:${port.portName}" references unknown device "${connectedTo.device}".`
|
||||||
|
|||||||
+5
-5
@@ -1,14 +1,14 @@
|
|||||||
|
export interface ConnectedPortRef {
|
||||||
|
device: string;
|
||||||
|
port: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Port {
|
export interface Port {
|
||||||
portName: string;
|
portName: string;
|
||||||
speedGbps?: number; // default to 1 if not provided
|
speedGbps?: number; // default to 1 if not provided
|
||||||
connectedTo?: ConnectedPortRef;
|
connectedTo?: ConnectedPortRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConnectedPortRef {
|
|
||||||
device: string;
|
|
||||||
port: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Hardware {
|
export interface Hardware {
|
||||||
cpu: string;
|
cpu: string;
|
||||||
ram: string;
|
ram: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user