This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new f2de2dcb1 feat(webhook): allow closing incidents via request body 
instead of url (#8939)
f2de2dcb1 is described below

commit f2de2dcb19cdb2af5b2613a2b939f50a33bfd209
Author: bujjibabukatta <[email protected]>
AuthorDate: Mon Jun 22 08:58:04 2026 +0530

    feat(webhook): allow closing incidents via request body instead of url 
(#8939)
---
 backend/plugins/webhook/api/issues.go | 64 +++++++++++++++++++++++++++++++++++
 backend/plugins/webhook/impl/impl.go  |  9 +++++
 2 files changed, 73 insertions(+)

diff --git a/backend/plugins/webhook/api/issues.go 
b/backend/plugins/webhook/api/issues.go
index 0402be164..e280d4c9f 100644
--- a/backend/plugins/webhook/api/issues.go
+++ b/backend/plugins/webhook/api/issues.go
@@ -255,6 +255,42 @@ func CloseIssue(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, erro
        return closeIssue(input, err, connection)
 }
 
+// CloseIssueByBodyRequest is the body for the body-based close endpoint
+type CloseIssueByBodyRequest struct {
+       IssueKey       string     `mapstructure:"issueKey"       
validate:"required,max=255"`
+       ResolutionDate *time.Time `mapstructure:"resolutionDate"`
+       OriginalStatus string     `mapstructure:"originalStatus"`
+}
+
+// CloseIssueByBody
+// @Summary      close an issue (body-based)
+// @Description  Close an incident by passing issueKey in the request body.
+// @Description  Use this when the client (e.g. Kibana) cannot construct a 
dynamic URL.
+// @Tags          plugins/webhook
+// @Param         connectionId path  int                    true  "connection 
ID"
+// @Param         body         body  CloseIssueByBodyRequest true  "close 
request"
+// @Success 200  {string} noResponse ""
+// @Failure 400  {string} errcode.Error "Bad Request"
+// @Failure 500  {string} errcode.Error "Internal Error"
+// @Router        /plugins/webhook/connections/{connectionId}/issue/close 
[POST]
+func CloseIssueByBody(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       connection := &models.WebhookConnection{}
+       err := connectionHelper.First(connection, input.Params)
+       if err != nil {
+               return nil, err
+       }
+       request := &CloseIssueByBodyRequest{}
+       if err2 := helper.DecodeMapStruct(input.Body, request, true); err2 != 
nil {
+               return &plugin.ApiResourceOutput{Body: err2.Error(), Status: 
http.StatusBadRequest}, nil
+       }
+       vld = validator.New()
+       if err2 := errors.Convert(vld.Struct(request)); err2 != nil {
+               return &plugin.ApiResourceOutput{Body: err2.Error(), Status: 
http.StatusBadRequest}, nil
+       }
+       input.Params["issueKey"] = request.IssueKey
+       return closeIssue(input, err, connection)
+}
+
 // CloseIssueByName
 // @Summary set issue's status to DONE
 // @Description set issue's status to DONE
@@ -269,6 +305,34 @@ func CloseIssueByName(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
        return closeIssue(input, err, connection)
 }
 
+// CloseIssueByBodyByName
+// @Summary      close an issue by connection name (body-based)
+// @Description  Close an incident using connection name + issueKey in request 
body.
+// @Tags          plugins/webhook
+// @Param         connectionName path  string                 true "connection 
name"
+// @Param         body           body  CloseIssueByBodyRequest true "close 
request"
+// @Success 200  {string} noResponse ""
+// @Failure 400  {string} errcode.Error "Bad Request"
+// @Failure 500  {string} errcode.Error "Internal Error"
+// @Router        
/plugins/webhook/connections/by-name/{connectionName}/issue/close [POST]
+func CloseIssueByBodyByName(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       connection := &models.WebhookConnection{}
+       err := connectionHelper.FirstByName(connection, input.Params)
+       if err != nil {
+               return nil, err
+       }
+       request := &CloseIssueByBodyRequest{}
+       if err2 := helper.DecodeMapStruct(input.Body, request, true); err2 != 
nil {
+               return &plugin.ApiResourceOutput{Body: err2.Error(), Status: 
http.StatusBadRequest}, nil
+       }
+       vld = validator.New()
+       if err2 := errors.Convert(vld.Struct(request)); err2 != nil {
+               return &plugin.ApiResourceOutput{Body: err2.Error(), Status: 
http.StatusBadRequest}, nil
+       }
+       input.Params["issueKey"] = request.IssueKey
+       return closeIssue(input, err, connection)
+}
+
 func closeIssue(input *plugin.ApiResourceInput, err errors.Error, connection 
*models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
        if err != nil {
                return nil, err
diff --git a/backend/plugins/webhook/impl/impl.go 
b/backend/plugins/webhook/impl/impl.go
index 93f2ac5e0..97dc59adc 100644
--- a/backend/plugins/webhook/impl/impl.go
+++ b/backend/plugins/webhook/impl/impl.go
@@ -99,6 +99,9 @@ func (p Webhook) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler
                "connections/:connectionId/issue/:issueKey/close": {
                        "POST": api.CloseIssue,
                },
+               "connections/:connectionId/issue/close": {
+                       "POST": api.CloseIssueByBody,
+               },
                ":connectionId/deployments": {
                        "POST": api.PostDeployments,
                },
@@ -111,6 +114,9 @@ func (p Webhook) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler
                ":connectionId/issue/:issueKey/close": {
                        "POST": api.CloseIssue,
                },
+               ":connectionId/issue/close": {
+                       "POST": api.CloseIssueByBody,
+               },
                "connections/by-name/:connectionName": {
                        "GET":    api.GetConnectionByName,
                        "PATCH":  api.PatchConnectionByName,
@@ -128,6 +134,9 @@ func (p Webhook) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler
                "connections/by-name/:connectionName/issue/:issueKey/close": {
                        "POST": api.CloseIssueByName,
                },
+               "connections/by-name/:connectionName/issue/close": {
+                       "POST": api.CloseIssueByBodyByName,
+               },
                "projects/:projectName/deployments": {
                        "POST": api.PostDeploymentsByProjectName,
                },

Reply via email to