@@ -507,60 +507,57 @@ export function assignDefaultLayout(
507507
508508// ---------------------------------------------------------------------------
509509// Server field stripping utilities
510+ //
511+ // The Sentry dashboard API returns many extra fields via GET that should NOT
512+ // be sent back in PUT requests. Using an allowlist approach ensures only
513+ // fields the API accepts are included, avoiding silent rejection of widgets.
510514// ---------------------------------------------------------------------------
511515
512- /**
513- * Server-generated fields on widget queries that must be stripped before PUT.
514- * NEVER strip user-controlled fields like conditions, columns, aggregates.
515- */
516- const QUERY_SERVER_FIELDS = [ "id" , "widgetId" , "dateCreated" ] as const ;
517-
518- /**
519- * Server-generated fields on widgets that must be stripped before PUT.
520- * CRITICAL: Never strip widgetType, displayType, or layout — these are
521- * user-controlled and stripping them causes widgets to reset to defaults.
522- */
523- const WIDGET_SERVER_FIELDS = [ "id" , "dashboardId" , "dateCreated" ] as const ;
524-
525- /**
526- * Server-generated fields on widget layout that must be stripped before PUT.
527- */
528- const LAYOUT_SERVER_FIELDS = [ "isResizable" ] as const ;
516+ /** Extract only the query fields the PUT API accepts */
517+ function cleanQuery ( q : DashboardWidgetQuery ) : DashboardWidgetQuery {
518+ return {
519+ name : q . name ?? "" ,
520+ conditions : q . conditions ?? "" ,
521+ columns : q . columns ?? [ ] ,
522+ aggregates : q . aggregates ?? [ ] ,
523+ fields : q . fields ?? [ ] ,
524+ ...( q . fieldAliases && { fieldAliases : q . fieldAliases } ) ,
525+ ...( q . orderby && { orderby : q . orderby } ) ,
526+ } ;
527+ }
529528
530529/**
531- * Strip server-generated fields from a single widget for PUT requests.
530+ * Strip server-generated and passthrough fields from a widget for PUT requests.
531+ *
532+ * Uses an allowlist approach: only includes fields the dashboard PUT API
533+ * accepts. The GET response includes many extra fields (description, thresholds,
534+ * interval, axisRange, datasetSource, etc.) that cause silent failures if
535+ * sent back in PUT.
532536 *
533537 * @param widget - Widget object from GET response
534- * @returns Widget safe for PUT (widgetType, displayType, layout preserved )
538+ * @returns Widget safe for PUT (only API-accepted fields )
535539 */
536540export function stripWidgetServerFields (
537541 widget : DashboardWidget
538542) : DashboardWidget {
539- const cleaned = { ...widget } ;
540-
541- // Strip widget-level server fields
542- for ( const field of WIDGET_SERVER_FIELDS ) {
543- delete ( cleaned as Record < string , unknown > ) [ field ] ;
544- }
545-
546- // Strip query-level server fields
547- if ( cleaned . queries ) {
548- cleaned . queries = cleaned . queries . map ( ( q ) => {
549- const cleanedQuery = { ...q } ;
550- for ( const field of QUERY_SERVER_FIELDS ) {
551- delete ( cleanedQuery as Record < string , unknown > ) [ field ] ;
552- }
553- return cleanedQuery ;
554- } ) ;
555- }
543+ const cleaned : DashboardWidget = {
544+ title : widget . title ,
545+ displayType : widget . displayType ,
546+ ...( widget . widgetType && { widgetType : widget . widgetType } ) ,
547+ ...( widget . queries && { queries : widget . queries . map ( cleanQuery ) } ) ,
548+ ...( widget . limit !== undefined &&
549+ widget . limit !== null && { limit : widget . limit } ) ,
550+ } ;
556551
557- // Strip layout server fields
558- if ( cleaned . layout ) {
559- const cleanedLayout = { ...cleaned . layout } ;
560- for ( const field of LAYOUT_SERVER_FIELDS ) {
561- delete ( cleanedLayout as Record < string , unknown > ) [ field ] ;
562- }
563- cleaned . layout = cleanedLayout ;
552+ // Preserve layout (x, y, w, h, minH only — not isResizable)
553+ if ( widget . layout ) {
554+ cleaned . layout = {
555+ x : widget . layout . x ,
556+ y : widget . layout . y ,
557+ w : widget . layout . w ,
558+ h : widget . layout . h ,
559+ ...( widget . layout . minH !== undefined && { minH : widget . layout . minH } ) ,
560+ } ;
564561 }
565562
566563 return cleaned ;
0 commit comments