feat: show zone resolver diagnostics

This commit is contained in:
2026-05-25 00:09:18 +02:00
parent f1cd74dc7b
commit 1ad432270b
6 changed files with 126 additions and 3 deletions

View File

@@ -1111,6 +1111,14 @@ def graph_explorer_page() -> str:
};
};
const zoneDiagnosticCodes = {
emptySeedSet: "ZONE_EMPTY_SEED_SET",
nodeSeededByMultipleZones: "ZONE_NODE_SEEDED_BY_MULTIPLE_ZONES",
nodeAttractedByMultipleZones: "ZONE_NODE_ATTRACTED_BY_MULTIPLE_ZONES",
attractionDepthLimitReached: "ZONE_ATTRACTION_DEPTH_LIMIT_REACHED",
edgeCrossesZoneBoundary: "ZONE_EDGE_CROSSES_ZONE_BOUNDARY",
};
const zoneDefinitionsForData = (data, grouping = activeZoneGrouping) => {
if (grouping === "deploymentEnvironment") return defaultZoneDefinitions.deploymentEnvironment;
if (grouping === "accessZone") {
@@ -1176,7 +1184,7 @@ def graph_explorer_page() -> str:
if (candidates.length > 1) {
const diagnostic = {
severity: "warning",
code: "ZONE_NODE_SEEDED_BY_MULTIPLE_ZONES",
code: zoneDiagnosticCodes.nodeSeededByMultipleZones,
message: `${elementLabel(node)} matched multiple zone definitions.`,
};
candidates.forEach((candidate) => zones.get(candidate.definition.id)?.diagnostics.push(diagnostic));
@@ -1189,6 +1197,15 @@ def graph_explorer_page() -> str:
zone.nodes.push(node);
addElementToZone(chosen.id, node);
});
zones.forEach((zone) => {
if (!zone.nodes.length) {
zone.diagnostics.push({
severity: "warning",
code: zoneDiagnosticCodes.emptySeedSet,
message: `${zone.label} has no visible seed nodes.`,
});
}
});
elements.filter((element) => element.isEdge && element.isEdge()).forEach((edge) => {
const data = zoneElementData(edge);
const zoneIds = new Set(
@@ -1198,6 +1215,16 @@ def graph_explorer_page() -> str:
);
const sourceZoneId = assignments.get(edge.data("source"));
const targetZoneId = assignments.get(edge.data("target"));
if ((sourceZoneId || targetZoneId) && sourceZoneId !== targetZoneId) {
const diagnostic = {
severity: "info",
code: zoneDiagnosticCodes.edgeCrossesZoneBoundary,
message: `${elementLabel(edge)} crosses a zone boundary.`,
};
[sourceZoneId, targetZoneId]
.filter(Boolean)
.forEach((zoneId) => zones.get(zoneId)?.diagnostics.push(diagnostic));
}
if (sourceZoneId) zoneIds.add(sourceZoneId);
if (targetZoneId) zoneIds.add(targetZoneId);
zoneIds.forEach((zoneId) => addElementToZone(zoneId, edge));
@@ -1321,6 +1348,8 @@ def graph_explorer_page() -> str:
.flatMap((element) => zoneWarningsForData(element.data()).map((warning) =>
`${elementLabel(element)}: ${warning}`
));
const diagnostics = (zone.diagnostics || [])
.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`);
const rows = [
["visible nodes", String(visibleNodes.length)],
["deployment environments", valuesFor("deploymentEnvironment")],
@@ -1328,12 +1357,14 @@ def graph_explorer_page() -> str:
["access zones", valuesFor("accessZone")],
["routing authorities", valuesFor("routingAuthority")],
["policy authorities", valuesFor("policyAuthority")],
...diagnostics.slice(0, 8).map((diagnostic) => ["diagnostic", diagnostic]),
...warnings.slice(0, 8).map((warning) => ["warning", warning]),
];
if (diagnostics.length > 8) rows.push(["diagnostic", `${diagnostics.length - 8} additional zone diagnostics`]);
if (warnings.length > 8) rows.push(["warning", `${warnings.length - 8} additional route warnings`]);
detailList.innerHTML = rows
.filter(([, value]) => value)
.map(([key, value]) => `<li class="${key === "warning" ? "orientation-warning" : ""}"><strong>${escapeHtml(key)}</strong> ${escapeHtml(value)}</li>`)
.map(([key, value]) => `<li class="${key === "warning" || key === "diagnostic" ? "orientation-warning" : ""}"><strong>${escapeHtml(key)}</strong> ${escapeHtml(value)}</li>`)
.join("");
const contextIds = new Set(visibleNodes.map((node) => node.id()));
@@ -1346,6 +1377,7 @@ def graph_explorer_page() -> str:
orientationList.innerHTML = [
{label: "grouping", value: ruleAttributeLabels[zone.field] || humanize(zone.field), state: "good"},
{label: "zone", value: zone.label},
{label: "diagnostics", value: diagnostics.length ? String(diagnostics.length) : "none", state: diagnostics.length ? "warning" : "good"},
{label: "warnings", value: warnings.length ? String(warnings.length) : "none", state: warnings.length ? "warning" : "good"},
].map((row) => `
<li class="orientation-item ${orientationStateClass(row.state)}">