From 11874ea2633ed72f76cc56fc7be697a6d0ff4161 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 2 Jun 2026 17:09:49 +0000 Subject: [PATCH] fix: use processRequestWithEnhancedErrors for dashboard routes The External API dashboard endpoints used validateRequestWithEnhancedErrors, which validates request data but does not mutate req.body with the parsed result. This meant Zod defaults (like where: '') were computed but discarded, causing undefined values to be stored in MongoDB. Switch to processRequestWithEnhancedErrors which assigns the parsed data (including Zod defaults and transforms) back to req.body/params/query. This fixes the issue where creating a heatmap tile without an explicit 'where' field would store undefined in MongoDB, causing subsequent UI-based dashboard updates to fail. Add integration tests verifying that: - Heatmap tile config.where defaults to '' in MongoDB when omitted - Select-item where (stored as aggCondition) defaults to '' when omitted Co-authored-by: Drew Davis --- .../external-api/__tests__/dashboards.test.ts | 60 +++++++++++++++++++ .../src/routers/external-api/v2/dashboards.ts | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/api/src/routers/external-api/__tests__/dashboards.test.ts b/packages/api/src/routers/external-api/__tests__/dashboards.test.ts index 4d8cbb7d44..3f9c075b23 100644 --- a/packages/api/src/routers/external-api/__tests__/dashboards.test.ts +++ b/packages/api/src/routers/external-api/__tests__/dashboards.test.ts @@ -2573,6 +2573,66 @@ describe('External API v2 Dashboards - new format', () => { ); }); + it('persists where as empty string when omitted from heatmap tile', async () => { + const heatmapNoWhere = { + name: 'Heatmap Without Where', + x: 0, + y: 0, + w: 6, + h: 3, + config: { + displayType: 'heatmap', + sourceId: traceSource._id.toString(), + select: [{ valueExpression: 'Duration' }], + }, + }; + + const response = await authRequest('post', BASE_URL) + .send({ + name: 'Dashboard Heatmap No Where', + tiles: [heatmapNoWhere], + tags: [], + }) + .expect(200); + + const dashboardInDb = await Dashboard.findById( + response.body.data.id, + ).lean(); + + expect((dashboardInDb!.tiles[0].config as any).where).toBe(''); + }); + + it('persists select-item where as empty string when omitted', async () => { + const lineChartNoWhere = { + name: 'Line Chart Without Where', + x: 0, + y: 0, + w: 6, + h: 3, + config: { + displayType: 'line', + sourceId: traceSource._id.toString(), + select: [{ aggFn: 'count' }], + }, + }; + + const response = await authRequest('post', BASE_URL) + .send({ + name: 'Dashboard Line No Where', + tiles: [lineChartNoWhere], + tags: [], + }) + .expect(200); + + const dashboardInDb = await Dashboard.findById( + response.body.data.id, + ).lean(); + + expect( + (dashboardInDb!.tiles[0].config as any).select[0].aggCondition, + ).toBe(''); + }); + it('does not silently downgrade a corrupted heatmap to line on GET', async () => { // Seed a Dashboard directly via Mongo with a heatmap tile whose // select[0] lacks a non-empty valueExpression. The current API diff --git a/packages/api/src/routers/external-api/v2/dashboards.ts b/packages/api/src/routers/external-api/v2/dashboards.ts index cb3735ae82..da509c1414 100644 --- a/packages/api/src/routers/external-api/v2/dashboards.ts +++ b/packages/api/src/routers/external-api/v2/dashboards.ts @@ -4,7 +4,7 @@ import { z } from 'zod'; import { deleteDashboard } from '@/controllers/dashboard'; import Dashboard, { IDashboard } from '@/models/dashboard'; -import { validateRequestWithEnhancedErrors as validateRequest } from '@/utils/enhancedErrors'; +import { processRequestWithEnhancedErrors as validateRequest } from '@/utils/enhancedErrors'; import { ExternalDashboardTileWithId, objectIdSchema } from '@/utils/zod'; import {