Uh oh!
There was an error while loading. Please reload this page.
fix(analytics): resolve a dotted dimension's select options against the relationship target - #4261
Merged
Merged
Conversation
…tionship target (#4053) A DatasetDimension whose `field` is a relationship path (`crm_account.industry`) got no select-option resolution: the label lookup read options as `baseObject.fields[<path>]`, which only matches the local spelling. For a dotted path the options live on the RELATED object, so the lookup missed silently and the chart plotted the raw stored enum — while the same field as a LOCAL dimension rendered its option labels beside it on the same dashboard. Extend the object-resolution step of that one lookup rather than forking a dotted-path variant: `resolveDimensionFieldOptions` walks each segment before the last as a declared relationship and reads the terminal field's options off the object that owns it. A single-segment path never enters the walk, so the local path resolves exactly as before and the two cannot drift. Multi-hop paths resolve too (ADR-0071, the shape the dataset designer emits). Hops ride the caller's existing `GET /meta/object/:name` channel, memoized per resolution, so no new fetch layer is added. Unresolvable paths yield no mapping and the raw value survives, as today. Wired at both sites that carried the lookup: DatasetWidget and ObjectChart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Aug 11, 2026
yinlianghui
marked this pull request as ready for review
August 11, 2026 09:55
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#4053
A
DatasetDimensionwhosefieldis a dotted relationship path (crm_account.industry) got no select-option resolution at all, so the chart plotted the raw stored enum (education,finance) — while the same underlying field reached as a local dimension renderedEducation,Financebeside it on the same dashboard. Nothing errored; end users just read database enum values.Source thread objectstack#5144 (required reading per the migration header) was reviewed: its triage ruling is "先按纯前端解析修" — fix as pure frontend resolution, and only split cross-domain if the spec surface must change. It did not: no
DatasetDimensionkey was added, so this stays entirely in the renderer.Where the resolver lives
Two sites carried the identical lookup, both reading options off the dataset's base object:
packages/plugin-dashboard/src/DatasetWidget.tsxobjSchema?.fields?.[fieldOf(dim)]?.optionspackages/plugin-charts/src/ObjectChart.tsx(dataset path)objSchema?.fields?.[f]?.optionsobjSchemais the dataset's base object, andfieldOf(dim)comes from the server'sdimensionFieldsmap — for the issue's widget A that is the literal stringcrm_account.industry. Indexing a base-object field map with a dotted path only ever matches the local spelling, so the lookup missed silently and the renderer fell through to the stored value.How the target object is reached
resolveDimensionFieldOptions(new, inpackages/core/src/utils/chart-series.tsbesidebuildDimensionLabelMap/buildOptionColorMap) is the object-resolution step of that same lookup, not a dotted-path variant beside it: a single-segment path never enters the walk and resolves exactly as before, so the local and joined paths cannot drift.Each segment before the last must be a declared relationship — type in
lookup/master_detail/masterdetail/master-detail, target read fromreference/reference_to/referenceTo/reference_to_object, accepting a bare name, a one-element array, or{ object }. Same canonicalization the dataset designer'sresolveReferenceToalready uses. The type gate is deliberate: a segment naming a plain field can never be turned into an object name and fetched speculatively.No new fetch layer. The related object's metadata was already reachable at this point — the resolver's own
GET /api/v1/meta/object/:nameread, which rides the hostapiFetchwhen a provider supplies one (objectui#4121). Hops go through that same channel via aloadObjectSchemacallback the call site passes in, so authentication, base-URL rewriting and draft-preview params are inherited rather than re-implemented. The loader is memoized per resolution and seeded with the base schema under its ownname, so sibling dimensions sharing a prefix (crm_account.industryalongsidecrm_account.tier) fetchcrm_accountonce and the base is never re-read.Multi-hop verdict: handled, and pinned
Multi-hop is not hypothetical here — the dataset designer emits it.
useDatasetFields.tsbuildsrelationship.relationship.fieldpaths under ADR-0071 (capped at 3 hops in the picker), so 2-hop paths genuinely reach this resolver. The walk is per-segment rather than single-hop special-casing, so N hops work for free;crm_account.owner.departmentis pinned end-to-end through the widget, asserting both the resolved label and that every hop was fetched in order. No artificial cap is imposed — each hop must be a real declared relationship, so a garbage path stops on its first segment.Evidence
Red-first, before any fix, with one fixture carrying both dimensions so the issue's side-by-side split is a single assertion pair:
The local dimension in that same fixture rendered
Education/Financepre-fix — the split reproduced exactly as reported. The 3 passing controls are the fall-through cases, green before and after.After the fix:
Test Files 132 passed (132),Tests 2108 passed (2108)acrosspackages/core/,packages/plugin-dashboard/,packages/plugin-charts/— including the existingDatasetWidget.relabel.test.tsxsuite and the objectui#4121apiFetch-routing pins, so local dimensions and probe routing are unchanged.Reverse verification (walk removed, fix otherwise intact — the direction was predicted before running): both dotted pins went RED with the raw values returning, and the three fall-through controls stayed GREEN. One core unit test I had predicted green also went red —
yields NO entry for a terminal field with no options…— because besides asserting the empty result it asserts the walk reachedcrm_accountonce; with the walk gone the fetch trace is empty. Its result assertion still held; only the trace moved. Reported rather than smoothed over.Controls pinned (all green before and after, i.e. today's behavior preserved):
crm_account.website) — raw value survives, nothing coerced;industry.nested) — no target invented, only the base object ever fetched.Gates
pnpm exec vitest runfor the three packages (repo root, per AGENTS.md) ·turbo run type-check lintfor the three packages: 17/17 ·node scripts/check-control-bytes.mjs: OK ·check-changeset-presence: OK ·check-changeset-no-major: OK. Changeset added (patch × 3; nevermajor, per the version-alignment rule — and noskip-changeset, per objectui#3724). No copy was touched, so the i18n gates do not apply.The core change is purely additive — two new exports, no existing signature narrowed — so no downstream consumer can break on it; the two consumers of the changed lookup are both edited and green here.
Scope
Ends at "the label is in hand", per the card's own not-a-duplicate reasoning. Whether that label then passes through the i18n bundle is objectstack#5076 and is deliberately neither fixed nor asserted here.
Generated by Claude Code