Skip to content

Commit 5ec1d32

Browse files
author
Rodrigo Silva
authored
feat(webhook): Support for Webhook Name in Webhook Endpoints (#8203)
* feat(webhooks): add by-name methods to connection helper * feat(webhooks): add connection endpoints by name * feat(webhooks): add post deployment endpoint by name * feat(webhooks): add issue endpoints by name
1 parent 3d61b78 commit 5ec1d32

File tree

5 files changed

+164
-2
lines changed

5 files changed

+164
-2
lines changed

backend/helpers/pluginhelper/api/connection_helper.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,20 @@ func (c *ConnectionApiHelper) Patch(connection interface{}, input *plugin.ApiRes
9999
return c.save(connection, c.db.CreateOrUpdate)
100100
}
101101

102-
// First finds connection from db by parsing request input and decrypt it
102+
// PatchByName (Modify) a connection record based on request body by connection name
103+
func (c *ConnectionApiHelper) PatchByName(connection interface{}, input *plugin.ApiResourceInput) errors.Error {
104+
err := c.FirstByName(connection, input.Params)
105+
if err != nil {
106+
return err
107+
}
108+
err = c.merge(connection, input.Body)
109+
if err != nil {
110+
return err
111+
}
112+
return c.save(connection, c.db.CreateOrUpdate)
113+
}
114+
115+
// First finds connection from db by id, parsing request input and decrypt it
103116
func (c *ConnectionApiHelper) First(connection interface{}, params map[string]string) errors.Error {
104117
connectionId := params["connectionId"]
105118
if connectionId == "" {
@@ -117,6 +130,18 @@ func (c *ConnectionApiHelper) FirstById(connection interface{}, id uint64) error
117130
return CallDB(c.db.First, connection, dal.Where("id = ?", id))
118131
}
119132

133+
// FirstByName finds connection from db by name, parsing request input and decrypting it
134+
func (c *ConnectionApiHelper) FirstByName(connection interface{}, params map[string]string) errors.Error {
135+
connectionName := params["connectionName"]
136+
if connectionName == "" {
137+
return errors.BadInput.New("missing connectionName")
138+
}
139+
if len(connectionName) > 100 {
140+
return errors.BadInput.New("invalid connectionName")
141+
}
142+
return CallDB(c.db.First, connection, dal.Where("name = ?", connectionName))
143+
}
144+
120145
// List returns all connections with password/token decrypted
121146
func (c *ConnectionApiHelper) List(connections interface{}) errors.Error {
122147
return CallDB(c.db.All, connections)

backend/plugins/webhook/api/connection.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,24 @@ func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
9797
return &plugin.ApiResourceOutput{Body: connection}, nil
9898
}
9999

100+
// PatchConnectionByName
101+
// @Summary patch webhook connection by name
102+
// @Description Patch webhook connection
103+
// @Tags plugins/webhook
104+
// @Param body body models.WebhookConnection true "json body"
105+
// @Success 200 {object} models.WebhookConnection
106+
// @Failure 400 {string} errcode.Error "Bad Request"
107+
// @Failure 500 {string} errcode.Error "Internal Error"
108+
// @Router /plugins/webhook/connections/by-name/{connectionName} [PATCH]
109+
func PatchConnectionByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
110+
connection := &models.WebhookConnection{}
111+
err := connectionHelper.PatchByName(connection, input)
112+
if err != nil {
113+
return nil, err
114+
}
115+
return &plugin.ApiResourceOutput{Body: connection}, nil
116+
}
117+
100118
// DeleteConnection
101119
// @Summary delete a webhook connection
102120
// @Description Delete a webhook connection
@@ -107,7 +125,32 @@ func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
107125
// @Failure 500 {string} errcode.Error "Internal Error"
108126
// @Router /plugins/webhook/connections/{connectionId} [DELETE]
109127
func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
110-
connectionId, e := strconv.ParseInt(input.Params["connectionId"], 10, 64)
128+
connectionId, e := strconv.ParseUint(input.Params["connectionId"], 10, 64)
129+
return deleteConnection(e, connectionId)
130+
}
131+
132+
// DeleteConnectionByName
133+
// @Summary delete a webhook connection by name
134+
// @Description Delete a webhook connection
135+
// @Tags plugins/webhook
136+
// @Success 200 {object} models.WebhookConnection
137+
// @Failure 400 {string} errcode.Error "Bad Request"
138+
// @Failure 409 {object} services.BlueprintProjectPairs "References exist to this connection"
139+
// @Failure 500 {string} errcode.Error "Internal Error"
140+
// @Router /plugins/webhook/connections/by-name/{connectionName} [DELETE]
141+
func DeleteConnectionByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
142+
connection := &models.WebhookConnection{}
143+
err := connectionHelper.FirstByName(connection, input.Params)
144+
145+
if err != nil {
146+
logger.Error(err, "query connection")
147+
return nil, err
148+
}
149+
150+
return deleteConnection(nil, connection.ConnectionId())
151+
}
152+
153+
func deleteConnection(e error, connectionId uint64) (*plugin.ApiResourceOutput, errors.Error) {
111154
if e != nil {
112155
return nil, errors.BadInput.WrapRaw(e)
113156
}
@@ -183,6 +226,24 @@ func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
183226
func GetConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
184227
connection := &models.WebhookConnection{}
185228
err := connectionHelper.First(connection, input.Params)
229+
return getConnection(err, connection)
230+
}
231+
232+
// GetConnectionByName
233+
// @Summary get webhook connection detail by name
234+
// @Description Get webhook connection detail
235+
// @Tags plugins/webhook
236+
// @Success 200 {object} WebhookConnectionResponse
237+
// @Failure 400 {string} errcode.Error "Bad Request"
238+
// @Failure 500 {string} errcode.Error "Internal Error"
239+
// @Router /plugins/webhook/connections/by-name/{connectionName} [GET]
240+
func GetConnectionByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
241+
connection := &models.WebhookConnection{}
242+
err := connectionHelper.FirstByName(connection, input.Params)
243+
return getConnection(err, connection)
244+
}
245+
246+
func getConnection(err errors.Error, connection *models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
186247
if err != nil {
187248
logger.Error(err, "query connection")
188249
return nil, err

backend/plugins/webhook/api/deployments.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,31 @@ type WebhookDeploymentCommitReq struct {
8484
func PostDeployments(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
8585
connection := &models.WebhookConnection{}
8686
err := connectionHelper.First(connection, input.Params)
87+
88+
return postDeployments(input, connection, err)
89+
}
90+
91+
// PostDeploymentsByName
92+
// @Summary create deployment by webhook name
93+
// @Description Create deployment pipeline by webhook name.<br/>
94+
// @Description example1: {"repo_url":"devlake","commit_sha":"015e3d3b480e417aede5a1293bd61de9b0fd051d","start_time":"2020-01-01T12:00:00+00:00","end_time":"2020-01-01T12:59:59+00:00","environment":"PRODUCTION"}<br/>
95+
// @Description So we suggest request before task after deployment pipeline finish.
96+
// @Description Both cicd_pipeline and cicd_task will be created
97+
// @Tags plugins/webhook
98+
// @Param body body WebhookDeploymentReq true "json body"
99+
// @Success 200
100+
// @Failure 400 {string} errcode.Error "Bad Request"
101+
// @Failure 403 {string} errcode.Error "Forbidden"
102+
// @Failure 500 {string} errcode.Error "Internal Error"
103+
// @Router /plugins/webhook/connections/by-name/:connectionName/deployments [POST]
104+
func PostDeploymentsByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
105+
connection := &models.WebhookConnection{}
106+
err := connectionHelper.FirstByName(connection, input.Params)
107+
108+
return postDeployments(input, connection, err)
109+
}
110+
111+
func postDeployments(input *plugin.ApiResourceInput, connection *models.WebhookConnection, err errors.Error) (*plugin.ApiResourceOutput, errors.Error) {
87112
if err != nil {
88113
return nil, err
89114
}

backend/plugins/webhook/api/issues.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,25 @@ func saveIncidentRelatedRecordsFromIssue(db dal.Transaction, logger log.Logger,
9494
func PostIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
9595
connection := &models.WebhookConnection{}
9696
err := connectionHelper.First(connection, input.Params)
97+
return postIssue(input, err, connection)
98+
}
99+
100+
// PostIssueByName
101+
// @Summary receive a record as defined and save it
102+
// @Description receive a record as follow and save it, example: {"url":"","issue_key":"DLK-1234","title":"a feature from DLK","description":"","epic_key":"","type":"BUG","status":"TODO","original_status":"created","story_point":0,"resolution_date":null,"created_date":"2020-01-01T12:00:00+00:00","updated_date":null,"lead_time_minutes":0,"parent_issue_key":"DLK-1200","priority":"","original_estimate_minutes":0,"time_spent_minutes":0,"time_remaining_minutes":0,"creator_id":"user1131","creator_name":"Nick name 1","assignee_id":"user1132","assignee_name":"Nick name 2","severity":"","component":""}
103+
// @Tags plugins/webhook
104+
// @Param body body WebhookIssueRequest true "json body"
105+
// @Success 200 {string} noResponse ""
106+
// @Failure 400 {string} errcode.Error "Bad Request"
107+
// @Failure 500 {string} errcode.Error "Internal Error"
108+
// @Router /plugins/webhook/by-name/:connectionName/issues [POST]
109+
func PostIssueByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
110+
connection := &models.WebhookConnection{}
111+
err := connectionHelper.FirstByName(connection, input.Params)
112+
return postIssue(input, err, connection)
113+
}
114+
115+
func postIssue(input *plugin.ApiResourceInput, err errors.Error, connection *models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
97116
if err != nil {
98117
return nil, err
99118
}
@@ -212,6 +231,24 @@ func PostIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, error
212231
func CloseIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
213232
connection := &models.WebhookConnection{}
214233
err := connectionHelper.First(connection, input.Params)
234+
return closeIssue(input, err, connection)
235+
}
236+
237+
// CloseIssueByName
238+
// @Summary set issue's status to DONE
239+
// @Description set issue's status to DONE
240+
// @Tags plugins/webhook
241+
// @Success 200 {string} noResponse ""
242+
// @Failure 400 {string} errcode.Error "Bad Request"
243+
// @Failure 500 {string} errcode.Error "Internal Error"
244+
// @Router /plugins/webhook/by-name/:connectionName/issue/:issueKey/close [POST]
245+
func CloseIssueByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
246+
connection := &models.WebhookConnection{}
247+
err := connectionHelper.FirstByName(connection, input.Params)
248+
return closeIssue(input, err, connection)
249+
}
250+
251+
func closeIssue(input *plugin.ApiResourceInput, err errors.Error, connection *models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
215252
if err != nil {
216253
return nil, err
217254
}

backend/plugins/webhook/impl/impl.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,19 @@ func (p Webhook) ApiResources() map[string]map[string]plugin.ApiResourceHandler
110110
":connectionId/issue/:issueKey/close": {
111111
"POST": api.CloseIssue,
112112
},
113+
"connections/by-name/:connectionName": {
114+
"GET": api.GetConnectionByName,
115+
"PATCH": api.PatchConnectionByName,
116+
"DELETE": api.DeleteConnectionByName,
117+
},
118+
"connections/by-name/:connectionName/deployments": {
119+
"POST": api.PostDeploymentsByName,
120+
},
121+
"connections/by-name/:connectionName/issues": {
122+
"POST": api.PostIssueByName,
123+
},
124+
"connections/by-name/:connectionName/issue/:issueKey/close": {
125+
"POST": api.CloseIssueByName,
126+
},
113127
}
114128
}

0 commit comments

Comments
 (0)