22 * sentry dashboard widget delete
33 *
44 * Remove a widget from an existing dashboard.
5+ *
6+ * Uses `buildDeleteCommand` — auto-injects `--yes`/`--force`/`--dry-run`
7+ * flags. Non-interactive guard is disabled (`noNonInteractiveGuard`) because
8+ * widget deletion is reversible (re-add the widget). `--yes`/`--force` are
9+ * accepted but have no effect today (no confirmation prompt); `--dry-run`
10+ * shows which widget would be removed without modifying the dashboard.
511 */
612
713import type { SentryContext } from "../../../context.js" ;
814import { getDashboard , updateDashboard } from "../../../lib/api-client.js" ;
915import { parseOrgProjectArg } from "../../../lib/arg-parsing.js" ;
10- import { buildCommand , numberParser } from "../../../lib/command.js" ;
16+ import { numberParser } from "../../../lib/command.js" ;
1117import { ValidationError } from "../../../lib/errors.js" ;
1218import { formatWidgetDeleted } from "../../../lib/formatters/human.js" ;
1319import { CommandOutput } from "../../../lib/formatters/output.js" ;
20+ import { buildDeleteCommand } from "../../../lib/mutate-command.js" ;
1421import { buildDashboardUrl } from "../../../lib/sentry-urls.js" ;
1522import {
1623 type DashboardDetail ,
@@ -27,6 +34,9 @@ import {
2734type DeleteFlags = {
2835 readonly index ?: number ;
2936 readonly title ?: string ;
37+ readonly yes : boolean ;
38+ readonly force : boolean ;
39+ readonly "dry-run" : boolean ;
3040 readonly json : boolean ;
3141 readonly fields ?: string [ ] ;
3242} ;
@@ -35,97 +45,134 @@ type DeleteResult = {
3545 dashboard : DashboardDetail ;
3646 widgetTitle : string ;
3747 url : string ;
48+ dryRun ?: boolean ;
3849} ;
3950
40- export const deleteCommand = buildCommand ( {
41- docs : {
42- brief : "Delete a widget from a dashboard" ,
43- fullDescription :
44- "Remove a widget from an existing Sentry dashboard.\n\n" +
45- "The dashboard can be specified by numeric ID or title.\n" +
46- "Identify the widget by --index (0-based) or --title.\n\n" +
47- "Examples:\n" +
48- " sentry dashboard widget delete 12345 --index 0\n" +
49- " sentry dashboard widget delete 'My Dashboard' --title 'Error Rate'" ,
50- } ,
51- output : {
52- human : formatWidgetDeleted ,
53- } ,
54- parameters : {
55- positional : {
56- kind : "array" ,
57- parameter : {
58- placeholder : "org/project/dashboard" ,
59- brief : "[<org/project>] <dashboard-id-or-title>" ,
60- parse : String ,
51+ export const deleteCommand = buildDeleteCommand (
52+ {
53+ docs : {
54+ brief : "Delete a widget from a dashboard" ,
55+ fullDescription :
56+ "Remove a widget from an existing Sentry dashboard.\n\n" +
57+ "The dashboard can be specified by numeric ID or title.\n" +
58+ "Identify the widget by --index (0-based) or --title.\n\n" +
59+ "Examples:\n" +
60+ " sentry dashboard widget delete 12345 --index 0\n" +
61+ " sentry dashboard widget delete 'My Dashboard' --title 'Error Rate'\n" +
62+ " sentry dashboard widget delete 12345 --index 0 --dry-run" ,
63+ } ,
64+ output : {
65+ human : formatWidgetDeleted ,
66+ jsonTransform : ( result : DeleteResult ) => {
67+ if ( result . dryRun ) {
68+ return {
69+ dryRun : true ,
70+ widgetTitle : result . widgetTitle ,
71+ widgetCount : result . dashboard . widgets ?. length ?? 0 ,
72+ url : result . url ,
73+ } ;
74+ }
75+ return {
76+ deleted : true ,
77+ widgetTitle : result . widgetTitle ,
78+ widgetCount : result . dashboard . widgets ?. length ?? 0 ,
79+ url : result . url ,
80+ } ;
6181 } ,
6282 } ,
63- flags : {
64- index : {
65- kind : "parsed" ,
66- parse : numberParser ,
67- brief : "Widget index (0-based)" ,
68- optional : true ,
83+ parameters : {
84+ positional : {
85+ kind : "array" ,
86+ parameter : {
87+ placeholder : "org/project/dashboard" ,
88+ brief : "[<org/project>] <dashboard-id-or-title>" ,
89+ parse : String ,
90+ } ,
6991 } ,
70- title : {
71- kind : "parsed" ,
72- parse : String ,
73- brief : "Widget title to match" ,
74- optional : true ,
92+ flags : {
93+ index : {
94+ kind : "parsed" ,
95+ parse : numberParser ,
96+ brief : "Widget index (0-based)" ,
97+ optional : true ,
98+ } ,
99+ title : {
100+ kind : "parsed" ,
101+ parse : String ,
102+ brief : "Widget title to match" ,
103+ optional : true ,
104+ } ,
75105 } ,
106+ aliases : { i : "index" , t : "title" } ,
76107 } ,
77- aliases : { i : "index" , t : "title" } ,
78- } ,
79- async * func ( this : SentryContext , flags : DeleteFlags , ...args : string [ ] ) {
80- const { cwd } = this ;
108+ async * func ( this : SentryContext , flags : DeleteFlags , ...args : string [ ] ) {
109+ const { cwd } = this ;
81110
82- if ( flags . index === undefined && ! flags . title ) {
83- throw new ValidationError (
84- "Specify --index or --title to identify the widget to delete." ,
85- "index"
111+ if ( flags . index === undefined && ! flags . title ) {
112+ throw new ValidationError (
113+ "Specify --index or --title to identify the widget to delete." ,
114+ "index"
115+ ) ;
116+ }
117+
118+ const { dashboardRef, targetArg } = parseDashboardPositionalArgs ( args ) ;
119+ const parsed = parseOrgProjectArg ( targetArg ) ;
120+ const orgSlug = await resolveOrgFromTarget (
121+ parsed ,
122+ cwd ,
123+ "sentry dashboard widget delete <org>/ <id> (--index <n> | --title <name>)"
86124 ) ;
87- }
125+ const dashboardId = await resolveDashboardId ( orgSlug , dashboardRef ) ;
88126
89- const { dashboardRef, targetArg } = parseDashboardPositionalArgs ( args ) ;
90- const parsed = parseOrgProjectArg ( targetArg ) ;
91- const orgSlug = await resolveOrgFromTarget (
92- parsed ,
93- cwd ,
94- "sentry dashboard widget delete <org>/ <id> (--index <n> | --title <name>)"
95- ) ;
96- const dashboardId = await resolveDashboardId ( orgSlug , dashboardRef ) ;
127+ // GET current dashboard → find widget
128+ const current = await getDashboard ( orgSlug , dashboardId ) . catch (
129+ ( error : unknown ) =>
130+ enrichDashboardError ( error , {
131+ orgSlug,
132+ dashboardId,
133+ operation : "view" ,
134+ } )
135+ ) ;
136+ const widgets = current . widgets ?? [ ] ;
97137
98- // GET current dashboard → find widget → splice → PUT
99- const current = await getDashboard ( orgSlug , dashboardId ) . catch (
100- ( error : unknown ) =>
101- enrichDashboardError ( error , { orgSlug, dashboardId, operation : "view" } )
102- ) ;
103- const widgets = current . widgets ?? [ ] ;
138+ const widgetIndex = resolveWidgetIndex ( widgets , flags . index , flags . title ) ;
139+ const widgetTitle = widgets [ widgetIndex ] ?. title ;
140+ const url = buildDashboardUrl ( orgSlug , dashboardId ) ;
104141
105- const widgetIndex = resolveWidgetIndex ( widgets , flags . index , flags . title ) ;
142+ // Dry-run mode: show what would be removed without removing it
143+ if ( flags [ "dry-run" ] ) {
144+ yield new CommandOutput ( {
145+ dashboard : current ,
146+ widgetTitle,
147+ url,
148+ dryRun : true ,
149+ } as DeleteResult ) ;
150+ return { hint : `Dashboard: ${ url } ` } ;
151+ }
106152
107- const widgetTitle = widgets [ widgetIndex ] ?. title ;
108- const updateBody = prepareDashboardForUpdate ( current ) ;
109- updateBody . widgets . splice ( widgetIndex , 1 ) ;
153+ // Splice the widget and PUT the updated dashboard
154+ const updateBody = prepareDashboardForUpdate ( current ) ;
155+ updateBody . widgets . splice ( widgetIndex , 1 ) ;
110156
111- const updated = await updateDashboard (
112- orgSlug ,
113- dashboardId ,
114- updateBody
115- ) . catch ( ( error : unknown ) =>
116- enrichDashboardError ( error , {
157+ const updated = await updateDashboard (
117158 orgSlug ,
118159 dashboardId ,
119- operation : "update" ,
120- } )
121- ) ;
122- const url = buildDashboardUrl ( orgSlug , dashboardId ) ;
160+ updateBody
161+ ) . catch ( ( error : unknown ) =>
162+ enrichDashboardError ( error , {
163+ orgSlug,
164+ dashboardId,
165+ operation : "update" ,
166+ } )
167+ ) ;
123168
124- yield new CommandOutput ( {
125- dashboard : updated ,
126- widgetTitle,
127- url,
128- } as DeleteResult ) ;
129- return { hint : `Dashboard: ${ url } ` } ;
169+ yield new CommandOutput ( {
170+ dashboard : updated ,
171+ widgetTitle,
172+ url,
173+ } as DeleteResult ) ;
174+ return { hint : `Dashboard: ${ url } ` } ;
175+ } ,
130176 } ,
131- } ) ;
177+ { noNonInteractiveGuard : true }
178+ ) ;
0 commit comments