From 0db23b0a4ccf0eefeb1d065143a43791e9646c13 Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Fri, 26 Apr 2024 21:32:00 +0200 Subject: [PATCH] Add PipelineListsOptions to woodpecker-go --- cli/deploy/deploy.go | 2 +- cli/pipeline/list.go | 3 +- woodpecker-go/woodpecker/interface.go | 2 +- woodpecker-go/woodpecker/list_options.go | 25 ++++++ woodpecker-go/woodpecker/list_options_test.go | 44 ++++++++++ woodpecker-go/woodpecker/repo.go | 33 ++++++- woodpecker-go/woodpecker/repo_test.go | 88 +++++++++++++++++++ 7 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 woodpecker-go/woodpecker/list_options.go create mode 100644 woodpecker-go/woodpecker/list_options_test.go create mode 100644 woodpecker-go/woodpecker/repo_test.go diff --git a/cli/deploy/deploy.go b/cli/deploy/deploy.go index 9a9ffd32c..19d886805 100644 --- a/cli/deploy/deploy.go +++ b/cli/deploy/deploy.go @@ -78,7 +78,7 @@ func deploy(c *cli.Context) error { var number int64 if pipelineArg == "last" { // Fetch the pipeline number from the last pipeline - pipelines, berr := client.PipelineList(repoID) + pipelines, berr := client.PipelineList(repoID, woodpecker.PipelineListsOptions{}) if berr != nil { return berr } diff --git a/cli/pipeline/list.go b/cli/pipeline/list.go index 5cac3aa4d..ab501d24c 100644 --- a/cli/pipeline/list.go +++ b/cli/pipeline/list.go @@ -22,6 +22,7 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/cli/common" "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" ) //nolint:gomnd @@ -63,7 +64,7 @@ func pipelineList(c *cli.Context) error { return err } - pipelines, err := client.PipelineList(repoID) + pipelines, err := client.PipelineList(repoID, woodpecker.PipelineListsOptions{}) if err != nil { return err } diff --git a/woodpecker-go/woodpecker/interface.go b/woodpecker-go/woodpecker/interface.go index 9ad2ee33e..f3d3d67cb 100644 --- a/woodpecker-go/woodpecker/interface.go +++ b/woodpecker-go/woodpecker/interface.go @@ -85,7 +85,7 @@ type Client interface { // PipelineList returns a list of recent pipelines for the // the specified repository. - PipelineList(repoID int64) ([]*Pipeline, error) + PipelineList(repoID int64, opt PipelineListsOptions) ([]*Pipeline, error) // PipelineQueue returns a list of enqueued pipelines. PipelineQueue() ([]*Feed, error) diff --git a/woodpecker-go/woodpecker/list_options.go b/woodpecker-go/woodpecker/list_options.go new file mode 100644 index 000000000..8107fa308 --- /dev/null +++ b/woodpecker-go/woodpecker/list_options.go @@ -0,0 +1,25 @@ +package woodpecker + +import ( + "fmt" + "net/url" +) + +// ListOptions represents the options for the Woodpecker API pagination. +type ListOptions struct { + Page int + PerPage int +} + +// getURLQuery returns the query string for the ListOptions. +func (o ListOptions) getURLQuery() url.Values { + query := make(url.Values) + if o.Page > 0 { + query.Add("page", fmt.Sprintf("%d", o.Page)) + } + if o.PerPage > 0 { + query.Add("perPage", fmt.Sprintf("%d", o.PerPage)) + } + + return query +} diff --git a/woodpecker-go/woodpecker/list_options_test.go b/woodpecker-go/woodpecker/list_options_test.go new file mode 100644 index 000000000..22d45d92e --- /dev/null +++ b/woodpecker-go/woodpecker/list_options_test.go @@ -0,0 +1,44 @@ +package woodpecker + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListOptions_getURLQuery(t *testing.T) { + tests := []struct { + name string + opts ListOptions + expected url.Values + }{ + { + name: "no options", + opts: ListOptions{}, + expected: url.Values{}, + }, + { + name: "with page", + opts: ListOptions{Page: 2}, + expected: url.Values{"page": {"2"}}, + }, + { + name: "with per page", + opts: ListOptions{PerPage: 10}, + expected: url.Values{"perPage": {"10"}}, + }, + { + name: "with page and per page", + opts: ListOptions{Page: 3, PerPage: 20}, + expected: url.Values{"page": {"3"}, "perPage": {"20"}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := tt.opts.getURLQuery() + assert.Equal(t, tt.expected, actual) + }) + } +} diff --git a/woodpecker-go/woodpecker/repo.go b/woodpecker-go/woodpecker/repo.go index 91f488f9e..aacf55274 100644 --- a/woodpecker-go/woodpecker/repo.go +++ b/woodpecker-go/woodpecker/repo.go @@ -1,6 +1,10 @@ package woodpecker -import "fmt" +import ( + "fmt" + "net/url" + "time" +) const ( pathRepoPost = "%s/api/repos?forge_remote_id=%d" @@ -24,6 +28,24 @@ const ( pathRepoCron = "%s/api/repos/%d/cron/%d" ) +type PipelineListsOptions struct { + ListOptions + Before time.Time + After time.Time +} + +// QueryEncode returns the URL query parameters for the PipelineListsOptions. +func (opt *PipelineListsOptions) QueryEncode() string { + query := opt.getURLQuery() + if !opt.Before.IsZero() { + query.Add("before", opt.Before.Format(time.RFC3339)) + } + if !opt.After.IsZero() { + query.Add("after", opt.After.Format(time.RFC3339)) + } + return query.Encode() +} + // Repo returns a repository by id. func (c *client) Repo(repoID int64) (*Repo, error) { out := new(Repo) @@ -215,10 +237,13 @@ func (c *client) PipelineLast(repoID int64, branch string) (*Pipeline, error) { // PipelineList returns a list of recent pipelines for the // the specified repository. -func (c *client) PipelineList(repoID int64) ([]*Pipeline, error) { +func (c *client) PipelineList(repoID int64, opt PipelineListsOptions) ([]*Pipeline, error) { var out []*Pipeline - uri := fmt.Sprintf(pathPipelines, c.addr, repoID) - err := c.get(uri, &out) + + uri, _ := url.Parse(fmt.Sprintf(pathPipelines, c.addr, repoID)) + uri.RawQuery = opt.QueryEncode() + + err := c.get(uri.String(), &out) return out, err } diff --git a/woodpecker-go/woodpecker/repo_test.go b/woodpecker-go/woodpecker/repo_test.go new file mode 100644 index 000000000..58edbc7f5 --- /dev/null +++ b/woodpecker-go/woodpecker/repo_test.go @@ -0,0 +1,88 @@ +package woodpecker + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestPipelineList(t *testing.T) { + tests := []struct { + name string + fixtureHandler http.HandlerFunc + opts PipelineListsOptions + wantErr bool + expectedLength int + expectedIDs []int64 + }{ + { + name: "success", + fixtureHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + assert.Equal(t, "/api/repos/123/pipelines?after=2023-01-15T00%3A00%3A00Z&before=2023-01-16T00%3A00%3A00Z&page=2&perPage=10", r.URL.RequestURI()) + + w.WriteHeader(http.StatusOK) + _, err := fmt.Fprint(w, `[{"id":1},{"id":2}]`) + assert.NoError(t, err) + }, + opts: PipelineListsOptions{ + ListOptions: ListOptions{ + Page: 2, + PerPage: 10, + }, + Before: time.Date(2023, 1, 16, 0, 0, 0, 0, time.UTC), + After: time.Date(2023, 1, 15, 0, 0, 0, 0, time.UTC), + }, + expectedLength: 2, + expectedIDs: []int64{1, 2}, + }, + { + name: "empty ListOptions", + fixtureHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + assert.Equal(t, "/api/repos/123/pipelines", r.URL.RequestURI()) + + w.WriteHeader(http.StatusOK) + _, err := fmt.Fprint(w, `[{"id":1},{"id":2}]`) + assert.NoError(t, err) + }, + opts: PipelineListsOptions{}, + expectedLength: 2, + expectedIDs: []int64{1, 2}, + }, + { + name: "error", + fixtureHandler: func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + }, + opts: PipelineListsOptions{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ts := httptest.NewServer(tt.fixtureHandler) + defer ts.Close() + + client := NewClient(ts.URL, http.DefaultClient) + + pipelines, err := client.PipelineList(123, tt.opts) + if tt.wantErr { + assert.Error(t, err) + assert.Nil(t, pipelines) + return + } + + assert.NoError(t, err) + assert.Len(t, pipelines, tt.expectedLength) + for i, id := range tt.expectedIDs { + assert.Equal(t, id, pipelines[i].ID) + } + }) + } +}