Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webhook): Support for Webhook Name in Webhook Endpoints #8203

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion backend/helpers/pluginhelper/api/connection_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,20 @@ func (c *ConnectionApiHelper) Patch(connection interface{}, input *plugin.ApiRes
return c.save(connection, c.db.CreateOrUpdate)
}

// First finds connection from db by parsing request input and decrypt it
// PatchByName (Modify) a connection record based on request body by connection name
func (c *ConnectionApiHelper) PatchByName(connection interface{}, input *plugin.ApiResourceInput) errors.Error {
err := c.FirstByName(connection, input.Params)
if err != nil {
return err
}
err = c.merge(connection, input.Body)
if err != nil {
return err
}
return c.save(connection, c.db.CreateOrUpdate)
}

// First finds connection from db by id, parsing request input and decrypt it
func (c *ConnectionApiHelper) First(connection interface{}, params map[string]string) errors.Error {
connectionId := params["connectionId"]
if connectionId == "" {
Expand All @@ -117,6 +130,18 @@ func (c *ConnectionApiHelper) FirstById(connection interface{}, id uint64) error
return CallDB(c.db.First, connection, dal.Where("id = ?", id))
}

// FirstByName finds connection from db by name, parsing request input and decrypting it
func (c *ConnectionApiHelper) FirstByName(connection interface{}, params map[string]string) errors.Error {
connectionName := params["connectionName"]
if connectionName == "" {
return errors.BadInput.New("missing connectionName")
}
if len(connectionName) > 100 {
return errors.BadInput.New("invalid connectionName")
}
return CallDB(c.db.First, connection, dal.Where("name = ?", connectionName))
}

// List returns all connections with password/token decrypted
func (c *ConnectionApiHelper) List(connections interface{}) errors.Error {
return CallDB(c.db.All, connections)
Expand Down
63 changes: 62 additions & 1 deletion backend/plugins/webhook/api/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
return &plugin.ApiResourceOutput{Body: connection}, nil
}

// PatchConnectionByName
// @Summary patch webhook connection by name
// @Description Patch webhook connection
// @Tags plugins/webhook
// @Param body body models.WebhookConnection true "json body"
// @Success 200 {object} models.WebhookConnection
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/connections/by-name/{connectionName} [PATCH]
func PatchConnectionByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.PatchByName(connection, input)
if err != nil {
return nil, err
}
return &plugin.ApiResourceOutput{Body: connection}, nil
}

// DeleteConnection
// @Summary delete a webhook connection
// @Description Delete a webhook connection
Expand All @@ -107,7 +125,32 @@ func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/connections/{connectionId} [DELETE]
func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connectionId, e := strconv.ParseInt(input.Params["connectionId"], 10, 64)
connectionId, e := strconv.ParseUint(input.Params["connectionId"], 10, 64)
return deleteConnection(e, connectionId)
}

// DeleteConnectionByName
// @Summary delete a webhook connection by name
// @Description Delete a webhook connection
// @Tags plugins/webhook
// @Success 200 {object} models.WebhookConnection
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 409 {object} services.BlueprintProjectPairs "References exist to this connection"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/connections/by-name/{connectionName} [DELETE]
func DeleteConnectionByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.FirstByName(connection, input.Params)

if err != nil {
logger.Error(err, "query connection")
return nil, err
}

return deleteConnection(nil, connection.ConnectionId())
}

func deleteConnection(e error, connectionId uint64) (*plugin.ApiResourceOutput, errors.Error) {
if e != nil {
return nil, errors.BadInput.WrapRaw(e)
}
Expand Down Expand Up @@ -183,6 +226,24 @@ func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
func GetConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.First(connection, input.Params)
return getConnection(err, connection)
}

// GetConnectionByName
// @Summary get webhook connection detail by name
// @Description Get webhook connection detail
// @Tags plugins/webhook
// @Success 200 {object} WebhookConnectionResponse
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/connections/by-name/{connectionName} [GET]
func GetConnectionByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.FirstByName(connection, input.Params)
return getConnection(err, connection)
}

func getConnection(err errors.Error, connection *models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
if err != nil {
logger.Error(err, "query connection")
return nil, err
Expand Down
25 changes: 25 additions & 0 deletions backend/plugins/webhook/api/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ type WebhookDeploymentCommitReq struct {
func PostDeployments(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.First(connection, input.Params)

return postDeployments(input, connection, err)
}

// PostDeploymentsByName
// @Summary create deployment by webhook name
// @Description Create deployment pipeline by webhook name.<br/>
// @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/>
// @Description So we suggest request before task after deployment pipeline finish.
// @Description Both cicd_pipeline and cicd_task will be created
// @Tags plugins/webhook
// @Param body body WebhookDeploymentReq true "json body"
// @Success 200
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 403 {string} errcode.Error "Forbidden"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/connections/by-name/:connectionName/deployments [POST]
func PostDeploymentsByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.FirstByName(connection, input.Params)

return postDeployments(input, connection, err)
}

func postDeployments(input *plugin.ApiResourceInput, connection *models.WebhookConnection, err errors.Error) (*plugin.ApiResourceOutput, errors.Error) {
if err != nil {
return nil, err
}
Expand Down
37 changes: 37 additions & 0 deletions backend/plugins/webhook/api/issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@ func saveIncidentRelatedRecordsFromIssue(db dal.Transaction, logger log.Logger,
func PostIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.First(connection, input.Params)
return postIssue(input, err, connection)
}

// PostIssueByName
// @Summary receive a record as defined and save it
// @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":""}
// @Tags plugins/webhook
// @Param body body WebhookIssueRequest true "json body"
// @Success 200 {string} noResponse ""
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/by-name/:connectionName/issues [POST]
func PostIssueByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.FirstByName(connection, input.Params)
return postIssue(input, err, connection)
}

func postIssue(input *plugin.ApiResourceInput, err errors.Error, connection *models.WebhookConnection) (*plugin.ApiResourceOutput, errors.Error) {
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -212,6 +231,24 @@ func PostIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, error
func CloseIssue(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.First(connection, input.Params)
return closeIssue(input, err, connection)
}

// CloseIssueByName
// @Summary set issue's status to DONE
// @Description set issue's status to DONE
// @Tags plugins/webhook
// @Success 200 {string} noResponse ""
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/webhook/by-name/:connectionName/issue/:issueKey/close [POST]
func CloseIssueByName(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.FirstByName(connection, input.Params)
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
}
Expand Down
14 changes: 14 additions & 0 deletions backend/plugins/webhook/impl/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,19 @@ func (p Webhook) ApiResources() map[string]map[string]plugin.ApiResourceHandler
":connectionId/issue/:issueKey/close": {
"POST": api.CloseIssue,
},
"connections/by-name/:connectionName": {
"GET": api.GetConnectionByName,
"PATCH": api.PatchConnectionByName,
"DELETE": api.DeleteConnectionByName,
},
"connections/by-name/:connectionName/deployments": {
"POST": api.PostDeploymentsByName,
},
"connections/by-name/:connectionName/issues": {
"POST": api.PostIssueByName,
},
"connections/by-name/:connectionName/issue/:issueKey/close": {
"POST": api.CloseIssueByName,
},
}
}
Loading