Skip to content

Commit f2de2dc

Browse files
feat(webhook): allow closing incidents via request body instead of url (#8939)
1 parent 57b2641 commit f2de2dc

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

backend/plugins/webhook/api/issues.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,42 @@ func CloseIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, erro
255255
return closeIssue(input, err, connection)
256256
}
257257

258+
// CloseIssueByBodyRequest is the body for the body-based close endpoint
259+
type CloseIssueByBodyRequest struct {
260+
IssueKey string `mapstructure:"issueKey" validate:"required,max=255"`
261+
ResolutionDate *time.Time `mapstructure:"resolutionDate"`
262+
OriginalStatus string `mapstructure:"originalStatus"`
263+
}
264+
265+
// CloseIssueByBody
266+
// @Summary close an issue (body-based)
267+
// @Description Close an incident by passing issueKey in the request body.
268+
// @Description Use this when the client (e.g. Kibana) cannot construct a dynamic URL.
269+
// @Tags plugins/webhook
270+
// @Param connectionId path int true "connection ID"
271+
// @Param body body CloseIssueByBodyRequest true "close request"
272+
// @Success 200 {string} noResponse ""
273+
// @Failure 400 {string} errcode.Error "Bad Request"
274+
// @Failure 500 {string} errcode.Error "Internal Error"
275+
// @Router /plugins/webhook/connections/{connectionId}/issue/close [POST]
276+
func CloseIssueByBody(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
277+
connection := &models.WebhookConnection{}
278+
err := connectionHelper.First(connection, input.Params)
279+
if err != nil {
280+
return nil, err
281+
}
282+
request := &CloseIssueByBodyRequest{}
283+
if err2 := helper.DecodeMapStruct(input.Body, request, true); err2 != nil {
284+
return &plugin.ApiResourceOutput{Body: err2.Error(), Status: http.StatusBadRequest}, nil
285+
}
286+
vld = validator.New()
287+
if err2 := errors.Convert(vld.Struct(request)); err2 != nil {
288+
return &plugin.ApiResourceOutput{Body: err2.Error(), Status: http.StatusBadRequest}, nil
289+
}
290+
input.Params["issueKey"] = request.IssueKey
291+
return closeIssue(input, err, connection)
292+
}
293+
258294
// CloseIssueByName
259295
// @Summary set issue's status to DONE
260296
// @Description set issue's status to DONE
@@ -269,6 +305,34 @@ func CloseIssueByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput
269305
return closeIssue(input, err, connection)
270306
}
271307

308+
// CloseIssueByBodyByName
309+
// @Summary close an issue by connection name (body-based)
310+
// @Description Close an incident using connection name + issueKey in request body.
311+
// @Tags plugins/webhook
312+
// @Param connectionName path string true "connection name"
313+
// @Param body body CloseIssueByBodyRequest true "close request"
314+
// @Success 200 {string} noResponse ""
315+
// @Failure 400 {string} errcode.Error "Bad Request"
316+
// @Failure 500 {string} errcode.Error "Internal Error"
317+
// @Router /plugins/webhook/connections/by-name/{connectionName}/issue/close [POST]
318+
func CloseIssueByBodyByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
319+
connection := &models.WebhookConnection{}
320+
err := connectionHelper.FirstByName(connection, input.Params)
321+
if err != nil {
322+
return nil, err
323+
}
324+
request := &CloseIssueByBodyRequest{}
325+
if err2 := helper.DecodeMapStruct(input.Body, request, true); err2 != nil {
326+
return &plugin.ApiResourceOutput{Body: err2.Error(), Status: http.StatusBadRequest}, nil
327+
}
328+
vld = validator.New()
329+
if err2 := errors.Convert(vld.Struct(request)); err2 != nil {
330+
return &plugin.ApiResourceOutput{Body: err2.Error(), Status: http.StatusBadRequest}, nil
331+
}
332+
input.Params["issueKey"] = request.IssueKey
333+
return closeIssue(input, err, connection)
334+
}
335+
272336
func closeIssue(input *plugin.ApiResourceInput, err errors.Error, connection *models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
273337
if err != nil {
274338
return nil, err

backend/plugins/webhook/impl/impl.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ func (p Webhook) ApiResources() map[string]map[string]plugin.ApiResourceHandler
9999
"connections/:connectionId/issue/:issueKey/close": {
100100
"POST": api.CloseIssue,
101101
},
102+
"connections/:connectionId/issue/close": {
103+
"POST": api.CloseIssueByBody,
104+
},
102105
":connectionId/deployments": {
103106
"POST": api.PostDeployments,
104107
},
@@ -111,6 +114,9 @@ func (p Webhook) ApiResources() map[string]map[string]plugin.ApiResourceHandler
111114
":connectionId/issue/:issueKey/close": {
112115
"POST": api.CloseIssue,
113116
},
117+
":connectionId/issue/close": {
118+
"POST": api.CloseIssueByBody,
119+
},
114120
"connections/by-name/:connectionName": {
115121
"GET": api.GetConnectionByName,
116122
"PATCH": api.PatchConnectionByName,
@@ -128,6 +134,9 @@ func (p Webhook) ApiResources() map[string]map[string]plugin.ApiResourceHandler
128134
"connections/by-name/:connectionName/issue/:issueKey/close": {
129135
"POST": api.CloseIssueByName,
130136
},
137+
"connections/by-name/:connectionName/issue/close": {
138+
"POST": api.CloseIssueByBodyByName,
139+
},
131140
"projects/:projectName/deployments": {
132141
"POST": api.PostDeploymentsByProjectName,
133142
},

0 commit comments

Comments
 (0)