You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Follow-up: lift dashboard container/tile invariants into shared schema #2225
The external dashboards API (packages/api/src/routers/external-api/v2/utils/dashboards.ts, buildDashboardBodySchema) enforces four invariants in a superRefine at the boundary:
Container id uniqueness across the dashboard
Tab id uniqueness within each container
tile.containerId resolves to a real container
tile.tabId requires tile.containerId and resolves to a tab in that container
These rules currently live only at the external API. Internal writers (MCP server, hyperdx_save_dashboard, future endpoints) all go through DashboardSchema in packages/common-utils/src/types.ts, which only enforces container-id uniqueness. A non-API writer can persist a dashboard whose tile references a missing container or tab, and the GET endpoint will then re-emit the broken state.
PR #2201 narrowed the gap by tightening containerId/tabId to z.string().min(1).optional() and adding .max() bounds, but it stopped short of moving the cross-reference invariants because that was scope-creep against the API PR.
Proposed fix:
Extract a addDashboardContainerInvariantsIssues(data, ctx) helper into packages/common-utils/src/types.ts (alongside the existing addDuplicateTileIdIssues).
Call it from DashboardSchema.superRefine so internal callers are validated.
Drop the duplicated body inside buildDashboardBodySchema's superRefine and call the same helper.
Risk to address as part of the work:
Existing Mongo docs may have invalid state. A migration or read-time normalization may be needed if any dashboards in production violate the invariants. Worth scanning before tightening.
Companion task (also from compound review on #2201): extract applyCommonTileFields(internal, external) for tile-shell metadata so convertToExternalTileChartConfig (new format) and translateExternalChartToTileConfig (legacy series format) both share the metadata-passthrough path. Today both translators duplicate containerId/tabId propagation and the next tile-shell field will too.
Activity