Skip to content

feat: display korrel8r status in the graph#237

Open
alanconway wants to merge 1 commit into
openshift:mainfrom
alanconway:status
Open

feat: display korrel8r status in the graph#237
alanconway wants to merge 1 commit into
openshift:mainfrom
alanconway:status

Conversation

@alanconway
Copy link
Copy Markdown
Contributor

@alanconway alanconway commented May 15, 2026

Korrel8r can add status to query results to indicate special conditions.

  • updated korrel8r/korrel8r-openapi.yaml, includes markers in graph nodes.
  • updated topology nodes to display markers in graph.
  • mark node matching current view "selected", follow view changes.

Summary by CodeRabbit

  • New Features

    • Constraint-based object filtering on GET /objects
    • Per-query status counts and a StatusCount type for status summaries
  • Improvements

    • Topology shows per-status badges/labels and improved badge styling
    • Disabled nodes get dedicated styling/behavior; selection, navigation, and menus ignore them
  • Tests

    • New tests for status normalization and merging logic

@openshift-ci openshift-ci Bot requested review from PeterYurkovich and shwetaap May 15, 2026 20:31
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 15, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alanconway

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 15, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Walkthrough

This PR adds StatusCount schema/type and QueryCount.statuses, a status normalization/merge module, topology UI updates to render per-status decorators and ignore disabled nodes, switches Node error storage to a disabled message, and updates tests and CSS.

Changes

Status Count Support & Disabled Node Handling

Layer / File(s) Summary
API schema and contract
korrel8r/korrel8r-openapi.yaml
OpenAPI spec declares new StatusCount schema with status and optional count, adds statuses array to QueryCount, introduces constraint parameter for GET /objects, removes x-go-type hint from Constraint, and bumps x-korrel8r-version to 0.10.1-dev.
TypeScript models & client export
web/src/korrel8r/types.ts, web/src/korrel8r/client/index.ts
Adds exported StatusCount alias, adds QueryCount.statuses: StatusCount[] and constructor initialization, and re-exports StatusCount from the client barrel.
Status utilities and tests
web/src/components/topology/status.ts, web/src/__tests__/status.spec.ts
New Status enum, statusName, statusForNode, toStatus, and mergeStatusCounts functions; Jest tests covering ordering, mapping, parsing, and merging per-query status counts.
Topology component refactor
web/src/components/topology/Korrel8rTopology.tsx
Computes merged status counts and node severity, renders status decorator and per-query Badge/LabelGroup, replaces error with disabled message handling, ignores disabled nodes for selection/actions, and synchronizes selection from URL.
Model tests updates
web/src/__tests__/types.spec.ts
Updates Node constructor tests to expect queries[].statuses = [] and Node.disabled string for constructor failures.
CSS updates
web/src/components/topology/korrel8rtopology.css
Removes .tp-plugin__topology_invalid_node, adds .tp-plugin__topology_marker_badge, overrides info status colors under .tp-plugin__topology, and adds .tp-plugin__topology_node--disabled styles.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Ipv6 And Disconnected Network Test Compatibility ⚠️ Warning New Go tests in pkg/server_test.go hardcode IPv4 localhost (127.0.0.1) without IPv6 support, failing IPv6-only CI jobs. No external connectivity issues detected. Update pkg/server_test.go to detect IPv4/IPv6 availability and use appropriate localhost addresses (127.0.0.1 for IPv4, ::1 for IPv6), or use the GetIPAddressFamily() helper from openshift/origin to adapt tests dynamically.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main purpose: adding status display capability to the topology graph. It aligns with the primary changes across OpenAPI schema, type definitions, and React component enhancements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed PR contains no Ginkgo tests. Repository uses standard Go testing and Jest for TypeScript. Ginkgo check not applicable.
Test Structure And Quality ✅ Passed Custom check requires Ginkgo test code review, but PR modifies only Jest tests (TypeScript) and no Ginkgo tests exist in repo.
Microshift Test Compatibility ✅ Passed PR adds Jest unit tests for web UI components, not Ginkgo e2e tests. The check applies only to Ginkgo e2e tests, so it is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests were added. The PR modifies OpenAPI schema, React web UI components, and Jest unit tests—not Go e2e tests subject to SNO compatibility requirements.
Topology-Aware Scheduling Compatibility ✅ Passed PR only modifies UI components (React/TypeScript), CSS styling, and an OpenAPI spec file. No deployment manifests, operator code, controllers, or scheduling constraints were added or modified.
Ote Binary Stdout Contract ✅ Passed PR contains no Go files (only YAML, TypeScript, CSS). OTE stdout contract check applies only to Go process-level code, not web frontend or schema changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/components/topology/Korrel8rTopology.tsx`:
- Around line 125-126: Guard access to node.class and prevent navigation for
disabled nodes: ensure any use of node.class is preceded by a presence check
(e.g., node?.class) and/or check node?.disabled before dereferencing; update the
context menu logic tied to hideContextMenuKebab to use node?.class safely, and
modify the selection/navigation path (handlers referenced by
onStatusDecoratorClick and the onSelect/navigation code) to early-return when
node?.disabled is true so disabled parse-failure nodes neither dereference class
nor trigger navigation.
- Around line 159-163: The useEffect currently returns early when locationQuery
is falsy, leaving stale selection; update the effect so that when locationQuery
is absent it calls setSelectedIds([]) to clear selection, otherwise compute id
from locationQuery.class and setSelectedIds(graph.node(id) ? [id] : []); keep
the logic inside the same useEffect that references locationQuery, graph,
setSelectedIds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: e776538d-4623-4935-8e4b-057ad1e9342b

📥 Commits

Reviewing files that changed from the base of the PR and between 8a8dbcf and e186aa5.

⛔ Files ignored due to path filters (1)
  • web/src/korrel8r/client/types.gen.ts is excluded by !**/*.gen.*
📒 Files selected for processing (6)
  • korrel8r/korrel8r-openapi.yaml
  • web/src/__tests__/types.spec.ts
  • web/src/components/topology/Korrel8rTopology.tsx
  • web/src/components/topology/korrel8rtopology.css
  • web/src/korrel8r/client/index.ts
  • web/src/korrel8r/types.ts

Comment thread web/src/components/topology/Korrel8rTopology.tsx Outdated
Comment thread web/src/components/topology/Korrel8rTopology.tsx
Comment thread web/src/components/topology/Korrel8rTopology.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/korrel8r/types.ts (1)

292-299: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Initialize statuses even when query parsing fails.

If Query.parse(qc.query) throws, this constructor keeps the instance alive via error, but statuses never gets assigned. That breaks the new QueryCount.statuses contract and can still crash downstream status-merging/rendering code on malformed API data. Move the defaulting outside the try so error cases still expose [].

Suggested fix
  constructor(qc: api.QueryCount) {
+    this.count = qc.count;
+    this.statuses = qc.statuses ?? [];
     try {
-      this.count = qc.count;
       this.query = Query.parse(qc.query);
-      this.statuses = qc.statuses ?? [];
     } catch (e) {
       this.error = e;
     }
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/korrel8r/types.ts` around lines 292 - 299, The constructor currently
sets this.statuses inside the try block so if Query.parse(qc.query) throws the
instance ends up with this.error but no statuses; move the defaulting so
this.statuses = qc.statuses ?? [] is executed outside (before or after) the
try/catch so QueryCount.statuses is always initialized to an array even on parse
failures; keep Query.parse(qc.query) inside the try/catch so exceptions are
still captured into this.error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web/src/korrel8r/types.ts`:
- Around line 292-299: The constructor currently sets this.statuses inside the
try block so if Query.parse(qc.query) throws the instance ends up with
this.error but no statuses; move the defaulting so this.statuses = qc.statuses
?? [] is executed outside (before or after) the try/catch so QueryCount.statuses
is always initialized to an array even on parse failures; keep
Query.parse(qc.query) inside the try/catch so exceptions are still captured into
this.error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: aa2fd041-0bc4-44d9-84d7-e6fd3ae57b69

📥 Commits

Reviewing files that changed from the base of the PR and between e186aa5 and 852fb5a.

⛔ Files ignored due to path filters (1)
  • web/src/korrel8r/client/types.gen.ts is excluded by !**/*.gen.*
📒 Files selected for processing (8)
  • korrel8r/korrel8r-openapi.yaml
  • web/src/__tests__/status.spec.ts
  • web/src/__tests__/types.spec.ts
  • web/src/components/topology/Korrel8rTopology.tsx
  • web/src/components/topology/korrel8rtopology.css
  • web/src/components/topology/status.ts
  • web/src/korrel8r/client/index.ts
  • web/src/korrel8r/types.ts
✅ Files skipped from review due to trivial changes (2)
  • web/src/korrel8r/client/index.ts
  • web/src/tests/types.spec.ts

Korrel8r can add status to query results to indicate special conditions.
"error"/"warning" korrel8r status is shown as patternfly "danger"/"warning"
Any other non-empty korrel8r status is shown as patternfly "info"

- updated korrel8r/korrel8r-openapi.yaml, includes status in graph nodes.
- updated topology nodes to display status in graph.
- mark node matching current view "selected", follow view changes.

Other:
- Updated devspace.yaml to ignore build churn in sync process.

WIP: add "info" status to nodes for anything that's not warning/error.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/korrel8r/types.ts (1)

292-299: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Initialize statuses even on parse failure paths.

Line 296 only sets this.statuses inside try. If query parsing fails, statuses stays undefined and later qc.statuses.forEach(...) can crash in topology status aggregation.

Proposed fix
  constructor(qc: api.QueryCount) {
+    this.statuses = qc.statuses ?? [];
     try {
       this.count = qc.count;
       this.query = Query.parse(qc.query);
-      this.statuses = qc.statuses ?? [];
     } catch (e) {
       this.error = e;
     }
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/korrel8r/types.ts` around lines 292 - 299, The constructor currently
sets this.statuses only inside the try block so if Query.parse(qc.query) throws,
this.statuses remains undefined; initialize this.statuses unconditionally (e.g.,
set this.statuses = qc.statuses ?? [] before or at the start of the constructor)
or ensure the catch block assigns this.statuses = qc.statuses ?? [] so that
later code (e.g., any qc.statuses.forEach) never encounters undefined; keep the
existing error capture (this.error = e) but always ensure this.statuses is a
defined array.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/components/topology/Korrel8rTopology.tsx`:
- Around line 258-266: The status labels render entries even when status is
empty, producing blank labels and invalid props for Label; update the rendering
in Korrel8rTopology to filter qc.statuses for truthy/non-empty status values
before mapping (e.g., use qc.statuses.filter(({status}) => Boolean(status)) ),
then map that filtered array to produce <Label> elements using
statusName(toStatus(status)) and count; ensure you only call toStatus/statusName
for entries with a valid status so Label's status prop is never passed an
invalid value.

In `@web/src/components/topology/status.ts`:
- Around line 41-44: The aggregation adds sc.count directly even though count is
optional; change the logic in the qc.statuses.forEach handler to treat
missing/invalid counts as zero (e.g., use (sc.count ?? 0) or Number(sc.count) ||
0) before doing m.set(sc.status, ...), so m.set(sc.status, (m.get(sc.status) ??
0) + (sc.count ?? 0)); keep the existing guard for sc.status and the s =
Math.max(s, toStatus(sc.status)) line unchanged.

---

Outside diff comments:
In `@web/src/korrel8r/types.ts`:
- Around line 292-299: The constructor currently sets this.statuses only inside
the try block so if Query.parse(qc.query) throws, this.statuses remains
undefined; initialize this.statuses unconditionally (e.g., set this.statuses =
qc.statuses ?? [] before or at the start of the constructor) or ensure the catch
block assigns this.statuses = qc.statuses ?? [] so that later code (e.g., any
qc.statuses.forEach) never encounters undefined; keep the existing error capture
(this.error = e) but always ensure this.statuses is a defined array.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 3ecb3d6d-6598-42d8-aaf7-a7300204e42b

📥 Commits

Reviewing files that changed from the base of the PR and between 852fb5a and 8821431.

⛔ Files ignored due to path filters (1)
  • web/src/korrel8r/client/types.gen.ts is excluded by !**/*.gen.*
📒 Files selected for processing (8)
  • korrel8r/korrel8r-openapi.yaml
  • web/src/__tests__/status.spec.ts
  • web/src/__tests__/types.spec.ts
  • web/src/components/topology/Korrel8rTopology.tsx
  • web/src/components/topology/korrel8rtopology.css
  • web/src/components/topology/status.ts
  • web/src/korrel8r/client/index.ts
  • web/src/korrel8r/types.ts

Comment on lines +258 to +266
{qc.statuses?.length > 0 && (
<LabelGroup>
{qc.statuses.map(({ status, count }) => (
<Label key={status} isCompact status={statusName(toStatus(status))}>
{status} {count}
</Label>
))}
</LabelGroup>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Filter empty status entries in query menu labels.

Lines 258-266 render status rows without checking status, which can produce blank labels and invalid status prop mapping for Label.

Proposed fix
-            {qc.statuses?.length > 0 && (
+            {qc.statuses?.some((s) => !!s.status) && (
               <LabelGroup>
-                {qc.statuses.map(({ status, count }) => (
-                  <Label key={status} isCompact status={statusName(toStatus(status))}>
+                {qc.statuses
+                  .filter(({ status }) => !!status)
+                  .map(({ status, count }) => (
+                  <Label key={status} isCompact status={statusName(toStatus(status))}>
                     {status} {count}
                   </Label>
-                ))}
+                ))}
               </LabelGroup>
             )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{qc.statuses?.length > 0 && (
<LabelGroup>
{qc.statuses.map(({ status, count }) => (
<Label key={status} isCompact status={statusName(toStatus(status))}>
{status} {count}
</Label>
))}
</LabelGroup>
)}
{qc.statuses?.some((s) => !!s.status) && (
<LabelGroup>
{qc.statuses
.filter(({ status }) => !!status)
.map(({ status, count }) => (
<Label key={status} isCompact status={statusName(toStatus(status))}>
{status} {count}
</Label>
))}
</LabelGroup>
)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/topology/Korrel8rTopology.tsx` around lines 258 - 266, The
status labels render entries even when status is empty, producing blank labels
and invalid props for Label; update the rendering in Korrel8rTopology to filter
qc.statuses for truthy/non-empty status values before mapping (e.g., use
qc.statuses.filter(({status}) => Boolean(status)) ), then map that filtered
array to produce <Label> elements using statusName(toStatus(status)) and count;
ensure you only call toStatus/statusName for entries with a valid status so
Label's status prop is never passed an invalid value.

Comment on lines +41 to +44
qc.statuses.forEach((sc) => {
if (!sc.status) return;
m.set(sc.status, (m.get(sc.status) ?? 0) + sc.count);
s = Math.max(s, toStatus(sc.status));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard optional status counts before aggregation.

Line 43 adds sc.count directly, but count is optional in the API schema. undefined here yields NaN totals and broken badges/tooltips.

Proposed fix
-      m.set(sc.status, (m.get(sc.status) ?? 0) + sc.count);
+      m.set(sc.status, (m.get(sc.status) ?? 0) + (sc.count ?? 0));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
qc.statuses.forEach((sc) => {
if (!sc.status) return;
m.set(sc.status, (m.get(sc.status) ?? 0) + sc.count);
s = Math.max(s, toStatus(sc.status));
qc.statuses.forEach((sc) => {
if (!sc.status) return;
m.set(sc.status, (m.get(sc.status) ?? 0) + (sc.count ?? 0));
s = Math.max(s, toStatus(sc.status));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/topology/status.ts` around lines 41 - 44, The aggregation
adds sc.count directly even though count is optional; change the logic in the
qc.statuses.forEach handler to treat missing/invalid counts as zero (e.g., use
(sc.count ?? 0) or Number(sc.count) || 0) before doing m.set(sc.status, ...), so
m.set(sc.status, (m.get(sc.status) ?? 0) + (sc.count ?? 0)); keep the existing
guard for sc.status and the s = Math.max(s, toStatus(sc.status)) line unchanged.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 20, 2026

@alanconway: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants