Add filter to list repository pipelines API (#4416)

This commit is contained in:
6543 2024-11-28 22:33:52 +01:00 committed by GitHub
parent 9fedc88a9c
commit 52fb493495
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 95 additions and 15 deletions

View file

@ -2966,6 +2966,24 @@ const docTemplate = `{
"description": "only return pipelines after this RFC3339 date",
"name": "after",
"in": "query"
},
{
"type": "string",
"description": "filter pipelines by branch",
"name": "branch",
"in": "query"
},
{
"type": "string",
"description": "filter pipelines by webhook events (comma separated)",
"name": "event",
"in": "query"
},
{
"type": "string",
"description": "filter pipelines by strings contained in ref",
"name": "ref",
"in": "query"
}
],
"responses": {

View file

@ -22,6 +22,7 @@ import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
@ -116,14 +117,32 @@ func createTmpPipeline(event model.WebhookEvent, commit *model.Commit, user *mod
// @Param perPage query int false "for response pagination, max items per page" default(50)
// @Param before query string false "only return pipelines before this RFC3339 date"
// @Param after query string false "only return pipelines after this RFC3339 date"
// @Param branch query string false "filter pipelines by branch"
// @Param event query string false "filter pipelines by webhook events (comma separated)"
// @Param ref query string false "filter pipelines by strings contained in ref"
func GetPipelines(c *gin.Context) {
repo := session.Repo(c)
before := c.Query("before")
after := c.Query("after")
filter := new(model.PipelineFilter)
filter := &model.PipelineFilter{
Branch: c.Query("branch"),
RefContains: c.Query("ref"),
}
if before != "" {
if events := c.Query("event"); events != "" {
eventList := strings.Split(events, ",")
wel := make(model.WebhookEventList, 0, len(eventList))
for _, event := range events {
we := model.WebhookEvent(event)
if err := we.Validate(); err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}
wel = append(wel, we)
}
filter.Events = wel
}
if before := c.Query("before"); before != "" {
beforeDt, err := time.Parse(time.RFC3339, before)
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
@ -132,7 +151,7 @@ func GetPipelines(c *gin.Context) {
filter.Before = beforeDt.Unix()
}
if after != "" {
if after := c.Query("after"); after != "" {
afterDt, err := time.Parse(time.RFC3339, after)
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)

View file

@ -61,8 +61,11 @@ func (Pipeline) TableName() string {
}
type PipelineFilter struct {
Before int64
After int64
Before int64
After int64
Branch string
Events []WebhookEvent
RefContains string
}
// IsMultiPipeline checks if step list contain more than one parent step.

View file

@ -52,7 +52,7 @@ func wrapDelete(c int64, err error) error {
}
func (s storage) paginate(p *model.ListOptions) *xorm.Session {
if p.All {
if p == nil || p.All {
return s.engine.NewSession()
}
if p.PerPage < 1 {

View file

@ -65,6 +65,18 @@ func (s storage) GetPipelineList(repo *model.Repo, p *model.ListOptions, f *mode
if f.Before != 0 {
cond = cond.And(builder.Lt{"created": f.Before})
}
if f.Branch != "" {
cond = cond.And(builder.Eq{"branch": f.Branch})
}
if len(f.Events) != 0 {
cond = cond.And(builder.In("event", f.Events))
}
if f.RefContains != "" {
cond = cond.And(builder.Like{"ref", f.RefContains})
}
}
return pipelines, s.paginate(p).Where(cond).

View file

@ -213,21 +213,49 @@ func TestPipelines(t *testing.T) {
pipeline1 := &model.Pipeline{
RepoID: repo.ID,
Status: model.StatusFailure,
Event: model.EventCron,
Ref: "refs/heads/some-branch",
Branch: "some-branch",
}
pipeline2 := &model.Pipeline{
RepoID: repo.ID,
Status: model.StatusSuccess,
Event: model.EventPull,
Ref: "refs/pull/32",
Branch: "main",
}
err1 := store.CreatePipeline(pipeline1, []*model.Step{}...)
g.Assert(err1).IsNil()
err2 := store.CreatePipeline(pipeline2, []*model.Step{}...)
g.Assert(err2).IsNil()
pipelines, err3 := store.GetPipelineList(&model.Repo{ID: 1}, &model.ListOptions{Page: 1, PerPage: 50}, nil)
g.Assert(err3).IsNil()
err := store.CreatePipeline(pipeline1, []*model.Step{}...)
g.Assert(err).IsNil()
err = store.CreatePipeline(pipeline2, []*model.Step{}...)
g.Assert(err).IsNil()
pipelines, err := store.GetPipelineList(&model.Repo{ID: 1}, &model.ListOptions{Page: 1, PerPage: 50}, nil)
g.Assert(err).IsNil()
g.Assert(len(pipelines)).Equal(2)
g.Assert(pipelines[0].ID).Equal(pipeline2.ID)
g.Assert(pipelines[0].RepoID).Equal(pipeline2.RepoID)
g.Assert(pipelines[0].Status).Equal(pipeline2.Status)
pipelines, err = store.GetPipelineList(&model.Repo{ID: 1}, nil, &model.PipelineFilter{
Branch: "main",
})
g.Assert(err).IsNil()
g.Assert(len(pipelines)).Equal(1)
g.Assert(pipelines[0].ID).Equal(pipeline2.ID)
pipelines, err = store.GetPipelineList(&model.Repo{ID: 1}, nil, &model.PipelineFilter{
Events: []model.WebhookEvent{model.EventCron},
})
g.Assert(err).IsNil()
g.Assert(len(pipelines)).Equal(1)
g.Assert(pipelines[0].ID).Equal(pipeline1.ID)
pipelines, err = store.GetPipelineList(&model.Repo{ID: 1}, nil, &model.PipelineFilter{
Events: []model.WebhookEvent{model.EventCron, model.EventPull},
RefContains: "32",
})
g.Assert(err).IsNil()
g.Assert(len(pipelines)).Equal(1)
g.Assert(pipelines[0].ID).Equal(pipeline2.ID)
})
g.It("Should get filtered pipelines", func() {

View file

@ -105,7 +105,7 @@ export default class WoodpeckerClient extends ApiClient {
async getPipelineList(
repoId: number,
opts?: PaginationOptions & { before?: string; after?: string },
opts?: PaginationOptions & { before?: string; after?: string; ref?: string; branch?: string; events?: string },
): Promise<Pipeline[]> {
const query = encodeQueryString(opts);
return this._get(`/api/repos/${repoId}/pipelines?${query}`) as Promise<Pipeline[]>;