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

Add DeletePipeline API #3506

Merged
merged 14 commits into from
Apr 25, 2024
38 changes: 38 additions & 0 deletions cmd/server/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,44 @@ const docTemplate = `{
}
}
}
},
"delete": {
"produces": [
"text/plain"
],
"tags": [
"Pipelines"
],
"summary": "Delete pipeline",
"parameters": [
{
"type": "string",
"default": "Bearer \u003cpersonal access token\u003e",
"description": "Insert your personal access token",
"name": "Authorization",
"in": "header",
"required": true
},
{
"type": "integer",
"description": "the repository id",
"name": "repo_id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "the number of the pipeline",
"name": "number",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
}
}
}
},
"/repos/{repo_id}/pipelines/{number}/approve": {
Expand Down
11 changes: 11 additions & 0 deletions server/api/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,14 @@ func refreshUserToken(c *gin.Context, user *model.User) {
}
forge.Refresh(c, _forge, _store, user)
}

// pipelineDeleteAllowed checks if the given pipeline can be deleted based on its status.
// It returns a bool indicating if delete is allowed, and the pipeline's status.
func pipelineDeleteAllowed(pl *model.Pipeline) bool {
switch pl.Status {
case model.StatusRunning, model.StatusPending, model.StatusBlocked:
return false
}

return true
}
47 changes: 43 additions & 4 deletions server/api/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,46 @@ func GetPipelines(c *gin.Context) {
c.JSON(http.StatusOK, pipelines)
}

// DeletePipeline
//
// @Summary Delete pipeline
// @Router /repos/{repo_id}/pipelines/{number} [delete]
// @Produce plain
// @Success 204
// @Tags Pipelines
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
// @Param repo_id path int true "the repository id"
// @Param number path int true "the number of the pipeline"
func DeletePipeline(c *gin.Context) {
_store := store.FromContext(c)

repo := session.Repo(c)
num, err := strconv.ParseInt(c.Param("number"), 10, 64)
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}

pl, err := _store.GetPipelineNumber(repo, num)
if err != nil {
handleDBError(c, err)
return
}

if ok := pipelineDeleteAllowed(pl); !ok {
c.String(http.StatusUnprocessableEntity, "Cannot delete pipeline with status %s", pl.Status)
return
}

err = store.FromContext(c).DeletePipeline(pl)
if err != nil {
c.String(http.StatusInternalServerError, "Error deleting pipelines. %s", err)
return
}

c.Status(http.StatusNoContent)
}

// GetPipeline
//
// @Summary Pipeline information by number
Expand Down Expand Up @@ -574,9 +614,8 @@ func DeletePipelineLogs(c *gin.Context) {
return
}

switch pl.Status {
case model.StatusRunning, model.StatusPending:
c.String(http.StatusUnprocessableEntity, "Cannot delete logs for a pending or running pipeline")
if ok := pipelineDeleteAllowed(pl); !ok {
c.String(http.StatusUnprocessableEntity, "Cannot delete logs for pipeline with status %s", pl.Status)
return
}

Expand All @@ -586,7 +625,7 @@ func DeletePipelineLogs(c *gin.Context) {
}
}
if err != nil {
c.String(http.StatusInternalServerError, "There was a problem deleting your logs. %s", err)
c.String(http.StatusInternalServerError, "Error deleting pipeline logs. %s", err)
return
}

Expand Down
48 changes: 48 additions & 0 deletions server/api/pipeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,51 @@ func TestGetPipelines(t *testing.T) {
})
})
}

func TestDeletePipeline(t *testing.T) {
gin.SetMode(gin.TestMode)

g := goblin.Goblin(t)
g.Describe("Pipeline", func() {
g.It("should delete pipeline", func() {
mockStore := mocks.NewStore(t)
mockStore.On("GetPipelineNumber", mock.Anything, mock.Anything).Return(fakePipeline, nil)
mockStore.On("DeletePipeline", mock.Anything).Return(nil)

c, _ := gin.CreateTestContext(httptest.NewRecorder())
c.Set("store", mockStore)
c.Params = gin.Params{{Key: "number", Value: "1"}}

DeletePipeline(c)

mockStore.AssertCalled(t, "GetPipelineNumber", mock.Anything, mock.Anything)
mockStore.AssertCalled(t, "DeletePipeline", mock.Anything)
assert.Equal(t, http.StatusNoContent, c.Writer.Status())
})

g.It("should not delete without pipeline number", func() {
c, _ := gin.CreateTestContext(httptest.NewRecorder())

DeletePipeline(c)

assert.Equal(t, http.StatusBadRequest, c.Writer.Status())
})

g.It("should not delete pending", func() {
fakePipeline.Status = model.StatusPending

mockStore := mocks.NewStore(t)
mockStore.On("GetPipelineNumber", mock.Anything, mock.Anything).Return(fakePipeline, nil)

c, _ := gin.CreateTestContext(httptest.NewRecorder())
c.Set("store", mockStore)
c.Params = gin.Params{{Key: "number", Value: "1"}}

DeletePipeline(c)

mockStore.AssertCalled(t, "GetPipelineNumber", mock.Anything, mock.Anything)
mockStore.AssertNotCalled(t, "DeletePipeline", mock.Anything)
assert.Equal(t, http.StatusUnprocessableEntity, c.Writer.Status())
})
})
}
1 change: 1 addition & 0 deletions server/router/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func apiRoutes(e *gin.RouterGroup) {

repo.GET("/pipelines", api.GetPipelines)
repo.POST("/pipelines", session.MustPush, api.CreatePipeline)
repo.DELETE("/pipelines/:number", session.MustRepoAdmin(), api.DeletePipeline)
repo.GET("/pipelines/:number", api.GetPipeline)
repo.GET("/pipelines/:number/config", api.GetPipelineConfig)

Expand Down