feat(dashboards): discriminate widget batch-add openapi schema (4/4)#61457
Conversation
|
Size Change: 0 B Total Size: 81.9 MB ℹ️ View Unchanged
|
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
products/dashboards/frontend/generated/api.zod.ts:564-570
**Duplicate `.enum()` call breaks the Zod schema at runtime**
The generated `widget_type` field calls `.enum(['error_tracking_list']).describe('...').enum(['error_tracking_list']).describe('...')`. In Zod v3, `.enum` on a `ZodEnum` instance is a getter property that returns `{ error_tracking_list: 'error_tracking_list' }` — a plain object, not a function. The second `.enum(['error_tracking_list'])` invocation would throw `TypeError: (...).enum is not a function` when the module is first loaded, making the entire schema unusable.
This pattern appears twice here (lines 564–570 for `error_tracking_list` and lines 689–695 for `session_replay_list`) and identically in `services/mcp/src/generated/dashboards/api.ts` (lines 530–536 and 655–661). The root cause is that drf-spectacular emits two `description` entries for the `ChoiceField` (one for the choice-value representation and one for `help_text`), and the Orval/Zod generator renders each as a separate `.enum(...).describe(...)` chain. The fix should be in the OpenAPI output or in the Orval code-generator config, not in these generated files.
Reviews (1): Last reviewed commit: "feat(dashboards): discriminate widget ba..." | Re-trigger Greptile |
| widget_type: zod | ||
| .enum(['error_tracking_list']) | ||
| .describe('\* `error_tracking_list` - error_tracking_list') | ||
| .enum(['error_tracking_list']) | ||
| .describe( | ||
| 'Widget type identifier. Supported values: error_tracking_list, session_replay_list. Use dashboard-widget-catalog-list for config_schema_hints per type.\n\n\* `error_tracking_list` - error_tracking_list' | ||
| ), |
There was a problem hiding this comment.
Duplicate
.enum() call breaks the Zod schema at runtime
The generated widget_type field calls .enum(['error_tracking_list']).describe('...').enum(['error_tracking_list']).describe('...'). In Zod v3, .enum on a ZodEnum instance is a getter property that returns { error_tracking_list: 'error_tracking_list' } — a plain object, not a function. The second .enum(['error_tracking_list']) invocation would throw TypeError: (...).enum is not a function when the module is first loaded, making the entire schema unusable.
This pattern appears twice here (lines 564–570 for error_tracking_list and lines 689–695 for session_replay_list) and identically in services/mcp/src/generated/dashboards/api.ts (lines 530–536 and 655–661). The root cause is that drf-spectacular emits two description entries for the ChoiceField (one for the choice-value representation and one for help_text), and the Orval/Zod generator renders each as a separate .enum(...).describe(...) chain. The fix should be in the OpenAPI output or in the Orval code-generator config, not in these generated files.
Prompt To Fix With AI
This is a comment left during a code review.
Path: products/dashboards/frontend/generated/api.zod.ts
Line: 564-570
Comment:
**Duplicate `.enum()` call breaks the Zod schema at runtime**
The generated `widget_type` field calls `.enum(['error_tracking_list']).describe('...').enum(['error_tracking_list']).describe('...')`. In Zod v3, `.enum` on a `ZodEnum` instance is a getter property that returns `{ error_tracking_list: 'error_tracking_list' }` — a plain object, not a function. The second `.enum(['error_tracking_list'])` invocation would throw `TypeError: (...).enum is not a function` when the module is first loaded, making the entire schema unusable.
This pattern appears twice here (lines 564–570 for `error_tracking_list` and lines 689–695 for `session_replay_list`) and identically in `services/mcp/src/generated/dashboards/api.ts` (lines 530–536 and 655–661). The root cause is that drf-spectacular emits two `description` entries for the `ChoiceField` (one for the choice-value representation and one for `help_text`), and the Orval/Zod generator renders each as a separate `.enum(...).describe(...)` chain. The fix should be in the OpenAPI output or in the Orval code-generator config, not in these generated files.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
This seems fair, but not sure why there is a duplicate enum/describe thing here 😕, maybe it's an outdated generated artefact?
d117cbb to
30b777e
Compare
e2de932 to
5f314de
Compare
d117cbb to
2484fe6
Compare
There was a problem hiding this comment.
Gates denied: this PR modifies public API contracts (OpenAPI schema discrimination for widget batch-add) and spans generated types across dashboards frontend and the MCP service — changes that require human review to validate correctness and downstream compatibility.
9b02dc0 to
be2f3ac
Compare
2484fe6 to
285d4bc
Compare
be2f3ac to
c64c892
Compare
285d4bc to
72f9878
Compare
c64c892 to
936dc70
Compare
72f9878 to
504e583
Compare
504e583 to
65b7b63
Compare
936dc70 to
12a8e9a
Compare
65b7b63 to
a17b4ab
Compare
12a8e9a to
3d71e86
Compare
|
⏭️ Skipped snapshot commit because branch advanced to The new commit will trigger its own snapshot update workflow. If you expected this workflow to succeed: This can happen due to concurrent commits. To get a fresh workflow run, either:
|
3d71e86 to
4c398d5
Compare
a17b4ab to
6f79ef2
Compare
4c398d5 to
2d1ad74
Compare
6f79ef2 to
4a0a6c3
Compare
|
⏭️ Skipped snapshot commit because branch advanced to The new commit will trigger its own snapshot update workflow. If you expected this workflow to succeed: This can happen due to concurrent commits. To get a fresh workflow run, either:
|
Query snapshots: Backend query snapshots updatedChanges: 2 snapshots (2 modified, 0 added, 0 deleted) What this means:
Next steps:
|
vdekrijger
left a comment
There was a problem hiding this comment.
Same comment as the previous one regarding the .ambr ones, but otherwise LGTM! Just a few notes from the bots that seem fair.
| widget_type: zod | ||
| .enum(['error_tracking_list']) | ||
| .describe('\* `error_tracking_list` - error_tracking_list') | ||
| .enum(['error_tracking_list']) | ||
| .describe( | ||
| 'Widget type identifier. Supported values: error_tracking_list, session_replay_list. Use dashboard-widget-catalog-list for config_schema_hints per type.\n\n\* `error_tracking_list` - error_tracking_list' | ||
| ), |
There was a problem hiding this comment.
This seems fair, but not sure why there is a duplicate enum/describe thing here 😕, maybe it's an outdated generated artefact?
| widget_type: zod | ||
| .enum(['session_replay_list']) | ||
| .describe('\* `session_replay_list` - session_replay_list') | ||
| .enum(['session_replay_list']) |
There was a problem hiding this comment.
This also has the udplicate enum / describe thing
| widget_type: zod | ||
| .enum(['session_replay_list']) | ||
| .describe('* `session_replay_list` - session_replay_list') | ||
| .enum(['session_replay_list']) |
There was a problem hiding this comment.
Just flagging this one as well as the bots did not flag this duplicate (probably due to it being implicitly referenced by their comments already).
5a5a735 to
26e655d
Compare
9638929 to
d96d2ae
Compare
Use widget_type-discriminated polymorphic request serializers for dashboard-widgets-batch-add so generated zod/MCP schemas tie config shape to widget_type. Runtime validation unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
Assert discriminator keys against EXPECTED_WIDGET_TYPES, reuse widget flag patch targets, and patch immutability test with a valid alternate type. Co-authored-by: Cursor <cursoragent@cursor.com>
26e655d to
a210144
Compare
d96d2ae to
081b88d
Compare
|
⏭️ Skipped snapshot commit because branch advanced to The new commit will trigger its own snapshot update workflow. If you expected this workflow to succeed: This can happen due to concurrent commits. To get a fresh workflow run, either:
|

Problem
MCP agents calling
dashboard-widgets-batch-addsaw widgetconfigas an untyped JSON blob (or an uncorrelated config union). That made it easy to send replay config with an error-trackingwidget_typewithout schema feedback.Changes
AddDashboardWidgetRequestserializers keyed bywidget_type(batch exports pattern)widgets_batch@extend_schema(request=AddDashboardWidgetsBatchRequestOpenApiSerializer)while keeping runtime validation on the existing serializerwidget_typetoChoiceFieldoverEXPECTED_WIDGET_TYPEShogli build:openapi)How did you test this code?
Agent-authored. Ran:
./bin/hogli test products/dashboards/backend/api/test/test_dashboard_widgets.py::TestDashboardWidgetOpenApiSchema./bin/hogli build:openapiAutomatic notifications
Docs update
🤖 Agent context
Cursor agent stacked this on the MCP widgets PR. Chose a separate OpenAPI-only batch-add serializer so runtime widget validation stays unchanged while spectacular emits a
widget_typediscriminator with per-type config shapes. PATCHtiles[].widgetremains untyped in OpenAPI — follow-up if we wantdashboard-updateagent ergonomics too.Made with Cursor