Skip to content

Commit

Permalink
feat(webhooks): add new /by-name endpoints
Browse files Browse the repository at this point in the history
Introduce new endpoints to the Webhook API that allow users to perform actions using connectionName instead of connectionId. The goal is to improve usability and simplify integration by enabling users to reference webhooks by their human-readable names, eliminating the need to manage and persist connectionId values.
  • Loading branch information
rodrigoluizs committed Nov 20, 2024
1 parent 62b19f5 commit bb21568
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 0 deletions.
61 changes: 61 additions & 0 deletions backend/plugins/webhook/api/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ func PatchConnectionById(input *plugin.ApiResourceInput) (*plugin.ApiResourceOut
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
}

// DeleteConnectionById
// @Summary delete a webhook connection by id
// @Description Delete a webhook connection
Expand All @@ -108,6 +126,31 @@ func PatchConnectionById(input *plugin.ApiResourceInput) (*plugin.ApiResourceOut
// @Router /plugins/webhook/connections/{connectionId} [DELETE]
func DeleteConnectionById(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
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 GetConnectionById(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 PostDeploymentsById(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 PostIssueById(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 PostIssueById(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, e
func CloseIssueById(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.CloseIssueById,
},
"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,
},
}
}

0 comments on commit bb21568

Please sign in to comment.