Skip to content

UN-4071 [FIX] Restore the Plan column on the LLM Whisperer API Keys page - #2270

Merged
jaseemjaskp merged 2 commits into
mainfrom
UN-4071-fix-nested-dataindex
Sep 1, 2026
Merged

UN-4071 [FIX] Restore the Plan column on the LLM Whisperer API Keys page#2270
jaseemjaskp merged 2 commits into
mainfrom
UN-4071-fix-nested-dataindex

Conversation

@jaseemjaskp

@jaseemjaskp jaseemjaskp commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What

  • Teach the shared shadcn DataTable adapter antd's nested dataIndex form (["product", "name"]), so it resolves as a path into the record rather than as a single key.
  • Restores the Plan column on the LLM Whisperer API Keys page, which has rendered blank for every row since the Ant Design removal.

Why

The cloud plugin's Plan column is unchanged by the migration — it is byte-identical before and after

{ title: "Plan", dataIndex: ["product", "name"], key: "product_name" }

An array dataIndex is antd's documented nested-path form, and real antd resolved it to record.product.name. Table now resolves to this DataTable (antd-structure.tsx), whose antd→TanStack adapter only did a flat, single-key lookup:

const value = c.dataIndex ? row.original?.[c.dataIndex] : undefined;

With c.dataIndex === ["product", "name"], JavaScript stringifies the array to the property name "product,name"undefined. The column declares no render, so that undefined goes straight into the cell. Nothing throws — the column id comes from c.key ("product_name") — so it fails silently: a header with an empty strip under it.

Backend is unaffected; GET /api/v1/llmwhisperer/keys/ still returns product: {id, name} per key.

This is the only nested dataIndex in either repo (grep -rn 'dataIndex:\s*\[' frontend/src → one hit, in the cloud plugin), which is why neither the build nor the existing 74 DataTable tests caught it.

How

frontend/src/components/data-table/DataTable.jsx:

  • New cellValue(record, dataIndex) helper — walks an array dataIndex as a path, short-circuiting on a nullish segment; unchanged flat lookup for a string.
  • The cell lookup uses it.
  • The TanStack accessor splits: accessorFn for a path, accessorKey kept for a string (unchanged behaviour).

Correction from self-review — the accessor half is NOT inert. The first commit's comment claimed "nothing reads row.getValue today". That is wrong: TanStack defaults every column to sortUndefined: 1, and getSortedRowModel calls rowA.getValue() to apply it before it consults sortingFn. I probed what that actually costs: a nested column carrying a sorter now sorts undefined-valued rows to the end — identically to how an equivalent string column already does (probed both; same order in, same order out). So the quirk is pre-existing and cross-cutting rather than introduced here, and no nested column declares a sorter today. The comment now states this instead of denying it.

Worth flagging separately, not fixed here: that sortUndefined pre-pass means sortingFn: () => 0 is not the complete guarantee against local reordering that the existing comment above it implies, for any sorter: true column with sparse values. Pre-existing and out of scope.

  • The id derivation at toColumn is deliberately untouched. It stringifies an array to "product,name" — the same spelling columnKey (ColumnFilter.jsx) and toSorterInfo use. Normalising it in one place only would silently break sorter/filter matching for a nested column.

frontend/src/components/data-table/DataTable.test.jsx: new describe("DataTable nested dataIndex") — resolves the path; renders empty rather than throwing on a missing segment; hands the resolved value to render(value, record, index).

Can this PR break any existing features? If yes, please list possible items. If no, please explain why.

Low risk, and scoped by construction:

  • The string dataIndex path is byte-equivalent — cellValue falls through to the same record?.[dataIndex], and those columns keep accessorKey exactly as before. Every column in both repos except one is a string.
  • The array branch is new behaviour on input that previously always produced undefined, so there is nothing working today for it to regress. The one observable knock-on — sortUndefined ordering on a nested sortable column — is described above; no nested column declares a sorter.
  • The id / columnKey / toSorterInfo spellings are unchanged, so sorting, filtering and onChange reporting are untouched.

Verified: all 596 frontend tests pass (35 files), including the 74 pre-existing DataTable tests; vite build succeeds; biome clean.

Relevant Docs

Related Issues or PRs

Dependencies Versions / Env Variables

  • None.

Notes on Testing

The three new tests were written first and confirmed failing against the unpatched adapter — render received undefined where "LLM Whisperer Free" was expected — then passing after the change. Fixtures use the exact column definition from the cloud plugin and the exact payload shape the portal returns.

npx vitest run src/components/data-table/DataTable.test.jsx   # 79 passed
npx vitest run                                                # 598 passed, 35 files
npm run build                                                 # ✓ built

Verified in the browser, not just by the build. The fix was synced into a dev namespace over the DevSpace HMR loop (OSS_FRONTEND_PATH confirmed pointing at this branch's worktree, and the pod's DataTable.jsx confirmed to contain cellValue), then /llm-whisperer/<org-id>/api-keys was loaded. The same API key that rendered a blank Plan cell before the fix (e68ce309-9794-4bf5-9a6e-30e0524736bb) now renders LLM Whisperer Free. Read out of the live DOM:

headers:   ["API Key","Key ID","Plan","Generated At","State","Actions","Usage/Pages"]
planCells: ["LLM Whisperer Free"]

Also confirmed the page is genuinely the shadcn build and not a stale antd one (no antd stylesheet; wrapper is ant-table-wrapper w-full with shadcn table classes), so this exercises the fixed adapter rather than an antd fallback.

Both new guards were mutation-checked

Each fails against exactly the mutant it describes, and no other:

Mutant Result
id normalised to dataIndex.join(".") in toColumn only reports a keyless nested column through onChange when sorted
resolver handling only two segments walks a path of any depth, including an array index
unmutated 79/79 pass

Screenshots

Plan column rendering LLM Whisperer Free for key e68ce309-… on /llm-whisperer/<org-id>/api-keys — the same row that was blank before the fix. (Screenshot captured locally; happy to attach on request.)

Checklist

I have read and understood the Contribution Guidelines.

The LLM Whisperer API Keys table declares its Plan column with antd's
documented nested-path form, `dataIndex: ["product", "name"]`, which real
antd resolved to `record.product.name`.

The shadcn adapter that replaced antd's Table only ever did a flat,
single-key lookup, so it indexed the record with the array itself and
JavaScript stringified that to the property name "product,name" —
undefined for every row. The column declares no `render`, so the
undefined went straight to the cell: no error, just a blank column.

Resolve a path `dataIndex` by walking it, in both the cell lookup and
the TanStack accessor. String `dataIndex` keeps `accessorKey` so
TanStack's dotted-string deep access is unchanged.

The `id` derivation is deliberately left alone: it stringifies an array
to "product,name", the same spelling `columnKey` and `toSorterInfo` use,
so normalising it in one place only would break sorter/filter matching
for a nested column.

This is the only nested dataIndex in either repo today, which is why
neither the build nor the existing 74 DataTable tests caught it.
Self-review findings on the previous commit.

The inline comment claimed "nothing reads row.getValue today", and that
was wrong: TanStack defaults every column to sortUndefined: 1, and
getSortedRowModel calls rowA.getValue() to apply it BEFORE consulting
sortingFn. So the accessor half is live behaviour, not deferred
correctness.

Checked what that actually costs. A nested column with a sorter now
reorders undefined-valued rows to the end -- identically to how an
equivalent string column already does; verified both by probe, same
order in and out. So the quirk is pre-existing and cross-cutting rather
than introduced here, and no nested column declares a sorter today. The
comment now says that instead of denying it.

Also documented the id/columnKey/toSorterInfo three-way agreement at the
line it constrains. It was argued only in a commit message, and
"normalise this stringified array" is exactly the tidy-up a later reader
would attempt -- which would silently stop a nested column reporting its
sort.

Tests: assert the missing-segment cells are actually EMPTY rather than
just present; add a deeper path with an array-index segment; add the
keyless-nested onChange case that guards the id invariant above. Both new
tests were mutation-checked -- each fails against exactly the mutant it
describes and no other. Dropped the fleet-wide "only nested dataIndex in
either repo" claim, which a leaf test file cannot verify.

598 tests pass, build clean.
@jaseemjaskp
jaseemjaskp marked this pull request as ready for review September 1, 2026 12:12
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Frontend Lint Report (Biome)

All checks passed! No linting or formatting issues found.

@greptile-apps

greptile-apps Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR restores Ant Design-compatible nested dataIndex resolution in the shared DataTable adapter.

  • Adds path traversal for array-form dataIndex values while preserving flat-key behavior.
  • Uses a TanStack accessorFn for nested paths and retains existing column identity semantics.
  • Adds focused coverage for nested values, missing segments, deep paths, sorter reporting, and render callbacks.

Confidence Score: 5/5

The PR appears safe to merge, with no actionable defects identified in the changed behavior.

The nested resolver safely short-circuits missing segments, preserves existing flat-key handling and column identity, and is covered across rendering and sorter-reporting paths.

Important Files Changed

Filename Overview
frontend/src/components/data-table/DataTable.jsx Correctly resolves array-form nested paths for cell rendering and TanStack accessors without changing string-key behavior.
frontend/src/components/data-table/DataTable.test.jsx Adds comprehensive regression coverage for nested path resolution and existing adapter contracts.

Reviews (1): Last reviewed commit: "UN-4071 fix: correct the accessor commen..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-api-deployment e2e 3 0 0 0 22.0
e2e-coowners e2e 1 0 0 0 1.1
e2e-etl e2e 1 0 0 0 8.5
e2e-login e2e 2 0 0 0 0.9
e2e-prompt-studio e2e 1 0 0 0 4.0
e2e-smoke e2e 2 0 0 0 0.8
e2e-workflow e2e 1 0 0 0 19.9
ui e2e 0 1 0 0 0.0
TOTAL 11 1 0 0 57.3

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
💤 Covered, but not exercised in this build
  • adapter-register-llm — Register and validate an LLM adapter. (covered by integration-backend; no result reported in this build)
  • workflow-author — Create a workflow; its source+destination endpoints materialise and are configurable. (covered by integration-backend; no result reported in this build)
  • api-deployment-provision — Deploying a workflow as an API mints a usable key and a resolvable endpoint. (covered by integration-backend; no result reported in this build)
  • api-deployment-auth — Unauthenticated or mis-scoped API-deployment calls are rejected before dispatch. (covered by integration-backend; no result reported in this build)
  • mcp-server-auth — Unauthenticated or mis-scoped hosted-MCP calls are rejected before any tool runs. (covered by integration-backend; no result reported in this build)
  • mcp-platform-auth — The org-scoped MCP endpoint stays behind the platform-API-key middleware; unauthenticated or mis-scoped calls reach no tool. (covered by integration-backend; no result reported in this build)
  • prompt-studio-author — Create a Prompt Studio project and add a prompt to it. (covered by integration-backend; no result reported in this build)
  • connector-register-test — Connector credentials are validated against the live system and stored encrypted. (covered by integration-backend; no result reported in this build)
  • usage-aggregate-read — Per-run token usage aggregates correctly and stays scoped to its organization. (covered by integration-backend; no result reported in this build)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • co-owner-manage — covered by e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-run — covered by e2e-api-deployment
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • pipeline-etl-execute — covered by e2e-etl
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment
@jaseemjaskp
jaseemjaskp merged commit 3ef171d into main Sep 1, 2026
15 checks passed
@jaseemjaskp
jaseemjaskp deleted the UN-4071-fix-nested-dataindex branch September 1, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant