Skip to content

fix: harden Data tab against sibling transform crashes and entity races#68

Merged
bburda merged 5 commits intomainfrom
fix/uds-data-tab-rendering
Apr 19, 2026
Merged

fix: harden Data tab against sibling transform crashes and entity races#68
bburda merged 5 commits intomainfrom
fix/uds-data-tab-rendering

Conversation

@bburda
Copy link
Copy Markdown
Contributor

@bburda bburda commented Apr 14, 2026

Summary

Fixes #67. Three independent regressions surfaced as the same symptom (Data tab shows "No data available" even when /<entity>/data returns items):

  • transformFault is now defensive: accepts status as string or object (aggregatedStatus), accepts code / fault_name as fallbacks for fault_code / description, and tolerates undefined severity. Without this any malformed fault payload threw inside a sibling transform and rejected the whole Promise.all in prefetchResourceCounts.
  • prefetchResourceCounts wraps each per-resource transform in its own try/catch. One bad resource can no longer wipe out the others.
  • The EntityDetailPanel resource-counts effect sets a cancelled flag in cleanup, so a late callback from a previously selected entity cannot overwrite the new entity's topicsData (caught a real race when navigating fast).
  • New typed access field on ComponentTopic, plumbed from x-medkit.access. DataPanel hides the write section for access==='read' and labels it Write Value instead of Publish Message for scalar writes.

Issue


Type

  • Bug fix
  • New feature
  • Breaking change
  • Documentation only

Testing

  • 17 new vitest cases (374 total, all green)
  • Manual reproduction of the empty Data tab against a gateway returning a non-canonical fault payload, before/after fix
  • Targeted Playwright run against an external diagnostics demo end-to-end suite went 12 fails → 0 across the chain of fixes here

Three independent failure modes that all surface as "No data available
for this component." even when the data endpoint returns items:

1. transformFault now accepts status as either a string or an object with
   an aggregatedStatus field, accepts code/fault_name as fallbacks for
   fault_code/description, and tolerates an undefined severity. Without
   these guards a single malformed fault payload threw inside a sibling
   transform and rejected the whole Promise.all in
   prefetchResourceCounts.
2. prefetchResourceCounts now wraps each per-resource transform in its
   own try/catch via a small safeCount helper. One bad resource can no
   longer wipe out the others.
3. The EntityDetailPanel resource-counts effect now sets a cancelled
   flag in its cleanup so a late Promise.all from a previously-selected
   entity cannot overwrite the new entity's topicsData.

Also adds a typed access field to ComponentTopic, plumbed from
x-medkit.access. DataPanel uses it to hide the write section for
access==='read' and to label the section "Write Value" instead of
"Publish Message" for scalar writes.

closes #67
Copilot AI review requested due to automatic review settings April 14, 2026 19:37
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Hardens the Data tab/resource prefetch path so malformed sibling payloads (notably faults) and fast entity navigation no longer cause the Data tab to appear empty despite /<entity>/data returning items; also adds per-topic access metadata to drive write UI behavior.

Changes:

  • Make transformFault and related transforms more defensive against payload drift; add ComponentTopic.access derived from x-medkit.access.
  • Isolate per-resource count transforms in prefetchResourceCounts so one bad payload can’t reject the whole counts prefetch.
  • Prevent stale async results from overwriting new entity state in EntityDetailPanel; adjust DataPanel write/publish UI based on access.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/lib/types.ts Adds typed ComponentTopic.access to represent write capability from vendor extension metadata.
src/lib/transforms.ts Hardens fault transform and plumbs x-medkit.access into ComponentTopic during data transform.
src/lib/transforms.test.ts Adds regression/unit tests for new defensive fault handling and access passthrough behavior.
src/lib/store.ts Wraps individual resource count transforms so failures don’t break sibling counts.
src/components/EntityDetailPanel.tsx Guards against selection-race overwrites by ignoring late async results after entity change.
src/components/DataPanel.tsx Hides write UI for access === 'read' and updates label for scalar writes.

Comment thread src/lib/transforms.ts Outdated
Comment thread src/lib/transforms.test.ts Outdated
Comment thread src/components/EntityDetailPanel.tsx
@bburda bburda self-assigned this Apr 14, 2026
- Guard `xm?.access` with a `typeof === 'string'` check before lowercasing
  so non-string vendor payloads no longer throw inside the data transform.
- Abort in-flight resource-count and entity-data fetches in the
  EntityDetailPanel cleanup via AbortController, so rapid entity navigation
  no longer piles up wasted requests. Threaded `signal` through
  prefetchResourceCounts to its three underlying GETs.
- Replace `as never` casts in transformFault alias tests with
  `as unknown as RawFaultItem` to keep the cast intent typed.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Hardens the Entity Detail “Data” tab against sibling resource transform failures and rapid navigation races, while adding support for a typed per-item access mode to hide/adjust the write UI when appropriate.

Changes:

  • Make transformFault tolerant of schema drift (status as object, field aliases, undefined severity).
  • Prevent prefetchResourceCounts from failing the whole counts fetch when one resource transform throws; add AbortSignal support through counts/data fetch to avoid entity-selection races.
  • Add ComponentTopic.access (from x-medkit.access) and use it in DataPanel to hide the write section for read-only items and adjust copy.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/lib/types.ts Adds optional ComponentTopic.access union type for read/write/readwrite modes.
src/lib/transforms.ts Hardens fault transform and plumbs x-medkit.access into ComponentTopic.
src/lib/transforms.test.ts Adds regression tests for fault schema drift and access parsing.
src/lib/store.ts Threads AbortSignal into prefetchResourceCounts and isolates per-resource transforms with try/catch.
src/components/EntityDetailPanel.tsx Adds AbortController + cancellation guard to prevent stale async results overwriting new entity state.
src/components/DataPanel.tsx Uses topic.access to hide write UI for read-only items and adjusts section label.

Comment thread src/lib/transforms.ts Outdated
Comment thread src/components/DataPanel.tsx Outdated
Comment thread src/lib/transforms.ts Outdated
Comment thread src/components/DataPanel.tsx Outdated
- transformFault: guard severity_label with typeof before lowercasing so
  non-string payloads do not throw.
- transformFault: when both fault_code and code are missing, generate a
  synthetic per-call id instead of the literal 'unknown'. A shared literal
  collapses store dedup (keyed by code+entity_id), duplicates React keys in
  the faults lists, and causes the expand-state Sets to toggle unrelated
  rows together.
- DataPanel: split the write-form condition so an explicit
  access='write'/'readwrite' always enables the form and the fallback
  typed-topic heuristic checks data presence rather than truthiness. Fixes
  the write section disappearing when the last observed value is a falsy
  scalar (0, false, '') and no type hint is reported.
- Add regression tests for synthetic fault codes and for the DataPanel
  canWrite logic across access modes.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Hardens the entity Data tab and related resource prefetching against malformed sibling payloads and fast-navigation races, while also adding explicit per-topic access control to hide or relabel write UI appropriately.

Changes:

  • Makes fault/data transforms more defensive (schema drift tolerant) and threads AbortSignal through prefetchResourceCounts + fetchEntityData.
  • Isolates per-resource count transforms so one malformed payload doesn’t poison parallel fetches.
  • Adds ComponentTopic.access from x-medkit.access and updates DataPanel write-section behavior and labeling; adds/extends Vitest coverage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/lib/types.ts Adds ComponentTopic.access union type for per-item access control.
src/lib/transforms.ts Hardens transformFault and plumbs x-medkit.access into transformed data topics.
src/lib/transforms.test.ts Adds regression tests for fault schema drift + synthetic codes + access passthrough.
src/lib/store.ts Extends prefetchResourceCounts to accept AbortSignal and isolates transform failures per resource.
src/components/EntityDetailPanel.tsx Prevents stale async results from overwriting current entity state via abort + cancelled guard.
src/components/DataPanel.tsx Uses access to hide write UI for read-only items and relabels write section.
src/components/DataPanel.test.tsx Adds unit tests covering canWrite behavior for access and falsy scalar values.
Comments suppressed due to low confidence (1)

src/components/DataPanel.tsx:224

  • handleCopyFromLast gates the deep-copy on if (topic.data), which will no-op for legitimate falsy scalar readings like 0, false, or '' (even though hasData will show the "Copy to Publish" button for those values). Use a null/undefined presence check instead of truthiness so scalar values can be copied reliably.
        topic.access === 'write' || topic.access === 'readwrite' ? 'Write Value' : 'Publish Message';

    const handleCopyFromLast = () => {
        if (topic.data) {
            setPublishValue(JSON.parse(JSON.stringify(topic.data)));

Comment thread src/components/DataPanel.test.tsx Outdated
Comment thread src/lib/transforms.ts Outdated
Comment thread src/components/DataPanel.tsx Outdated
- transformFault: accept an optional synthetic-id suffix and use the
  response array index (passed from transformFaultsResponse) instead of a
  timestamp+random token. The previous random suffix changed on every
  poll, so the store's code-based dedup saw the same code-less fault as
  new data on every refresh, churning React keys and resetting expand
  state. A per-index suffix keeps code-less faults distinct inside a
  response while remaining stable across polls.
- DataPanel: switch publishValue initialization from `||` to `??` so a
  gateway-reported scalar of 0 / false / '' is preserved instead of
  collapsing to an empty object, and replace the truthy guard in
  handleCopyFromLast with an explicit null/undefined presence check so
  "Copy to Publish" works for legitimate falsy scalar readings.
- Drop the truncated license header from DataPanel.test.tsx to match the
  header convention of the other component test files.
- Extend DataPanel and transforms tests with the corresponding
  regression cases (scalar-zero initial value, scalar-zero copy, stable
  deterministic synthetic codes across repeated transform calls).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Hardens the entity Data tab loading path so that malformed sibling resource payloads (faults/configs/ops) and fast entity navigation can no longer cause the Data tab to incorrectly fall back to an empty state, while also plumbing a new per-topic access mode to hide/rename the write UI appropriately.

Changes:

  • Made transformFault resilient to schema drift (status object/null, alias fields, missing severity) and stabilized synthetic fault codes for code-less items.
  • Updated prefetchResourceCounts to accept an AbortSignal and isolate per-resource transform failures; added effect cleanup guarding + request aborting in EntityDetailPanel.
  • Added ComponentTopic.access derived from x-medkit.access, updated DataPanel write gating/labeling, and added targeted Vitest coverage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/lib/types.ts Adds ComponentTopic.access typing for read/write/readwrite.
src/lib/transforms.ts Hardens fault + data transforms; threads access through data transform.
src/lib/transforms.test.ts Adds regression tests for fault status/aliases/synthetic codes and access passthrough.
src/lib/store.ts Extends prefetchResourceCounts with optional abort signal and per-transform safety.
src/components/EntityDetailPanel.tsx Prevents stale async results from overwriting current entity state via abort + cancelled guard.
src/components/DataPanel.tsx Uses access to hide write form for read-only topics, fixes falsy scalar handling, updates label.
src/components/DataPanel.test.tsx Adds regression tests for access gating and falsy scalar publish initialization/copy.

Comment thread src/lib/transforms.ts Outdated
The `(data.items || []).map(...)` fallback only catches nullish values,
so any truthy non-array payload (e.g. `{ items: {} }` or `{ items: 'x' }`)
would crash the transform with "map is not a function". Switch to
`Array.isArray` so malformed list payloads fall through to an empty
result instead of breaking the Data tab for sibling resources.

Covered by a new regression test that feeds a selection of non-array
truthy values into the transform and asserts an empty, crash-free result.
@mfaferek93 mfaferek93 self-requested a review April 19, 2026 12:21
@bburda bburda merged commit 9865263 into main Apr 19, 2026
3 checks passed
@bburda bburda deleted the fix/uds-data-tab-rendering branch April 19, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data tab silently empty when a sibling resource transform throws

3 participants