Skip to content

finding(plugin-dashboard): the METRIC path has no structured-groupBy branch, so an authored { field, dateGranularity } node is posted as an analytics DIMENSION object #8613

Description

@os-justin

Measured while implementing #6864 on origin/main @ ce45a0306. Different code path, different failure mode, so filed separately rather than ridden into that PR. ⛔ Nothing was changed; this is a source read.

The measurement

ObjectChart.runAggregate (packages/plugin-charts/src/ObjectChart.tsx:597-613) knows about two authored aggregate.groupBy shapes and routes them to two different wires:

const gb = schema.aggregate.groupBy as any;
// Structured GroupBy node (e.g. `{ field, dateGranularity: 'day' }`)
// requires the spec-shape `{ groupBy: GroupByNode[], aggregations, where }`
// payload so the server-side date-bucket engine kicks in.
// The legacy `{ field, function, groupBy, filter }` cube/analytics
// path does NOT honour `dateGranularity`.
const isStructured = gb && typeof gb === 'object' && !Array.isArray(gb);
if (isStructured) { /* spec-shape call */ }
/* else: legacy `{ field, function, groupBy: gb, filter }` call */

The METRIC path has no such branch. ObjectMetricWidget.computeOne (packages/plugin-dashboard/src/ObjectMetricWidget.tsx:246-252) forwards the authored value straight through:

const results = await ds.aggregate(objectName, {
  field: aggregate.field,
  function: aggregate.function,
  groupBy: aggregate.groupBy || '_all',
  filter: filterForRun,
});

grep over that file for dateGranularity, isStructured and aggregations returns zero hits, so there is no structured handling anywhere else in it.

What that does on the wire

A structured node is an object and not an array, so looksLikeSpecShape is false and the call takes ObjectStackAdapter.aggregate's ANALYTICS branch, which builds

dimensions: params.groupBy && params.groupBy !== '_all' ? [params.groupBy] : []

⇒ the POST body carries dimensions: [{ field: 'closed_at', dateGranularity: 'month' }] — an object where the analytics contract declares a dimension NAME. By the comment quoted above, that path does not honour dateGranularity in the first place, so even if the server tolerated the shape the bucketing the author asked for would not happen.

Why the authored shape is real, not hypothetical

{ field, dateGranularity } and { field, alias } are shapes this repo already recognises as authored aggregate.groupBy:

  • packages/core/src/utils/chart-category-key.tschartCategoryKey resolves the category column for exactly those node shapes;
  • packages/core/src/utils/chart-category-key.test.ts:45 and :56 pin them;
  • packages/plugin-charts/src/ObjectChart.absentCategoryAxisRefusal-8168.test.tsx:125 and :350 carry aggregate: { field, function, groupBy: { dateGranularity: 'day' } };
  • packages/plugin-dashboard/src/__tests__/DashboardChart.categoryAxisKey-8269.test.tsx:154 and :158 carry the same shape in widget metadata.

Both relays hand the metric family the raw authored value: DashboardRenderer.tsx:722 and DashboardGridLayout.tsx:301 both build aggregate: { field: providerAgg.field, function: providerAgg.function, groupBy: providerAgg.groupBy } for the object-metric node.

Why the type system does not catch it

ObjectMetricWidget's own props declare aggregate?: { field: string; function: string; groupBy?: string } (:44) — a STRING. But the value reaching it crosses an any seam twice: isObjectProvider narrows to aggregate?: any (packages/plugin-dashboard/src/utils.ts:10), and computeOne(ds: any, …) takes the datasource untyped. So the declared string refuses nothing at runtime and nothing at compile time either.

Reachability: NOT established

Same posture as #6825 and #6864. Every one of the sites listed above is a TEST FIXTURE; a sweep of tracked .json/.ts/.tsx/.mdx/.md/.yaml/.yml (6557 files, with a lit control on the same command shape returning 208 lines across 103 files) found no shipped app metadata declaring a structured aggregate.groupBy on a metric widget. So this is latent, and I am NOT claiming a live defect — recording it because the severity judgement is triage's, not mine.

Relationship to #6864

#6864 is the ARRAY form on the same authored key: it selects the adapter's spec-shape branch and the legacy keys vanish. This is the OBJECT form on the metric path, which selects the analytics branch and sends the node itself as a dimension. #6864's fix (PR #8612) refuses the array case at the producer and deliberately does not touch this one — an object groupBy never reaches the spec-shape branch, so it is outside that gate by construction.

Refs: #6864 - #6825 / PR #6911 - #8266 / #8269 (the measure and category key contracts that read these same node shapes)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatneeds:contract-reviewpriority:p2

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions