Verified Commit 075e443b authored by Zubeen's avatar Zubeen 🤖 Committed by GitLab
Browse files

feat(pipelines): add list pipelines of the current user

Changelog: Improvements
parent 348f250b
Loading
Loading
Loading
Loading
+74
Changes for pipelines.go: 74 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import (
)

var (
	routePipelines                              = route("pipelines")
	routeProjectsIDPipeline                     = route("projects/%s/pipeline")
	routeProjectsIDPipelines                    = route("projects/%s/pipelines")
	routeProjectsIDPipelinesID                  = route("projects/%s/pipelines/%d")
@@ -59,6 +60,13 @@ const (

type (
	PipelinesServiceInterface interface {
		// ListUserPipelines gets the pipelines across all projects that were
		// triggered by the authenticated user. Only keyset pagination is
		// supported.
		//
		// GitLab API docs:
		// https://docs.gitlab.com/api/pipelines/#list-pipelines
		ListUserPipelines(opt *ListUserPipelinesOptions, options ...RequestOptionFunc) ([]*UserPipeline, *Response, error)
		ListProjectPipelines(pid any, opt *ListProjectPipelinesOptions, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error)
		GetPipeline(pid any, pipeline int64, options ...RequestOptionFunc) (*Pipeline, *Response, error)
		GetPipelineVariables(pid any, pipeline int64, options ...RequestOptionFunc) ([]*PipelineVariable, *Response, error)
@@ -257,6 +265,72 @@ func (p PipelineInfo) String() string {
	return Stringify(p)
}

// UserPipeline represents a pipeline triggered by the authenticated user, as
// returned by ListUserPipelines.
//
// GitLab API docs:
// https://docs.gitlab.com/api/pipelines/#list-pipelines
type UserPipeline struct {
	ID             int64                     `json:"id"`
	IID            int64                     `json:"iid"`
	ProjectID      int64                     `json:"project_id"`
	Status         string                    `json:"status"`
	Source         PipelineSource            `json:"source"`
	Ref            string                    `json:"ref"`
	SHA            string                    `json:"sha"`
	Name           string                    `json:"name"`
	WebURL         string                    `json:"web_url"`
	CreatedAt      *time.Time                `json:"created_at"`
	UpdatedAt      *time.Time                `json:"updated_at"`
	StartedAt      *time.Time                `json:"started_at"`
	FinishedAt     *time.Time                `json:"finished_at"`
	Duration       int64                     `json:"duration"`
	QueuedDuration int64                     `json:"queued_duration"`
	DetailedStatus *DetailedStatus           `json:"detailed_status"`
	MergeRequest   *UserPipelineMergeRequest `json:"merge_request"`
	Project        *BasicProject             `json:"project"`
}

func (p UserPipeline) String() string {
	return Stringify(p)
}

// UserPipelineMergeRequest represents the merge request of a merge request
// pipeline in a UserPipeline. It is only set if the merge request is visible
// to the user.
//
// GitLab API docs:
// https://docs.gitlab.com/api/pipelines/#list-pipelines
type UserPipelineMergeRequest struct {
	IID    int64  `json:"iid"`
	Title  string `json:"title"`
	WebURL string `json:"web_url"`
}

// ListUserPipelinesOptions represents the available ListUserPipelines()
// options. Only keyset pagination is supported, so it does not embed
// ListOptions.
//
// GitLab API docs:
// https://docs.gitlab.com/api/pipelines/#list-pipelines
type ListUserPipelinesOptions struct {
	CreatedAfter  *time.Time      `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore *time.Time      `url:"created_before,omitempty" json:"created_before,omitempty"`
	Cursor        *string         `url:"cursor,omitempty" json:"cursor,omitempty"`
	OrderBy       *string         `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort          *string         `url:"sort,omitempty" json:"sort,omitempty"`
	Source        *PipelineSource `url:"source,omitempty" json:"source,omitempty"`
	PerPage       *int64          `url:"per_page,omitempty" json:"per_page,omitempty"`
}

func (s *PipelinesService) ListUserPipelines(opt *ListUserPipelinesOptions, options ...RequestOptionFunc) ([]*UserPipeline, *Response, error) {
	return do[[]*UserPipeline](s.client,
		withPath(routePipelines),
		withAPIOpts(opt),
		withRequestOpts(options...),
	)
}

// ListProjectPipelinesOptions represents the available ListProjectPipelines()
// options.
//
+107
Changes for pipelines_test.go: 107 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -45,6 +45,113 @@ func TestListProjectPipelines(t *testing.T) {
	assert.Equal(t, want, pipelines)
}

func TestListUserPipelines(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)

	// GIVEN a server that lists the pipelines of the authenticated user
	mux.HandleFunc("/api/v4/pipelines", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testParam(t, r, "source", "merge_request_event")
		testParam(t, r, "created_after", "2016-08-11T00:00:00Z")
		testParam(t, r, "created_before", "2016-08-12T00:00:00Z")
		testParam(t, r, "cursor", "eyJpZCI6IjE0In0")
		testParam(t, r, "order_by", "created_at")
		testParam(t, r, "sort", "desc")
		testParam(t, r, "per_page", "50")
		w.Header().Set("Link", `<https://gitlab.example.com/api/v4/pipelines?cursor=next>; rel="next"`)
		mustWriteJSONResponse(t, w, []map[string]any{{
			"id":              47,
			"iid":             12,
			"project_id":      1,
			"status":          "running",
			"source":          "merge_request_event",
			"ref":             "refs/merge-requests/14/merge",
			"sha":             "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
			"name":            "Build pipeline",
			"web_url":         "https://example.com/foo/bar/pipelines/47",
			"created_at":      "2016-08-11T11:28:34.085Z",
			"updated_at":      "2016-08-11T11:32:35.169Z",
			"started_at":      "2016-08-11T11:28:54.085Z",
			"finished_at":     nil,
			"duration":        nil,
			"queued_duration": 20,
			"detailed_status": map[string]any{"icon": "status_running", "text": "Running", "label": "running", "group": "running", "has_details": true},
			"merge_request":   map[string]any{"iid": 14, "title": "Add rate limiting", "web_url": "https://example.com/foo/bar/-/merge_requests/14"},
			"project":         map[string]any{"id": 1, "name": "Bar", "path_with_namespace": "foo/bar"},
		}})
	})

	// WHEN listing the pipelines with every option set
	opt := &ListUserPipelinesOptions{
		CreatedAfter:  mustParseTime("2016-08-11T00:00:00Z"),
		CreatedBefore: mustParseTime("2016-08-12T00:00:00Z"),
		Cursor:        new("eyJpZCI6IjE0In0"),
		OrderBy:       new("created_at"),
		Sort:          new("desc"),
		Source:        new(PipelineSourceMergeRequestEvent),
		PerPage:       new(int64(50)),
	}
	pipelines, resp, err := client.Pipelines.ListUserPipelines(opt)

	// THEN the pipelines and the next page link are returned
	require.NoError(t, err)
	require.NotNil(t, resp)
	assert.Equal(t, "https://gitlab.example.com/api/v4/pipelines?cursor=next", resp.NextLink)
	want := []*UserPipeline{{
		ID:             47,
		IID:            12,
		ProjectID:      1,
		Status:         "running",
		Source:         PipelineSourceMergeRequestEvent,
		Ref:            "refs/merge-requests/14/merge",
		SHA:            "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
		Name:           "Build pipeline",
		WebURL:         "https://example.com/foo/bar/pipelines/47",
		CreatedAt:      mustParseTime("2016-08-11T11:28:34.085Z"),
		UpdatedAt:      mustParseTime("2016-08-11T11:32:35.169Z"),
		StartedAt:      mustParseTime("2016-08-11T11:28:54.085Z"),
		QueuedDuration: 20,
		DetailedStatus: &DetailedStatus{Icon: "status_running", Text: "Running", Label: "running", Group: "running", HasDetails: true},
		MergeRequest:   &UserPipelineMergeRequest{IID: 14, Title: "Add rate limiting", WebURL: "https://example.com/foo/bar/-/merge_requests/14"},
		Project:        &BasicProject{ID: 1, Name: "Bar", PathWithNamespace: "foo/bar"},
	}}
	assert.Equal(t, want, pipelines)
}

func TestListUserPipelines_RequestOptionError(t *testing.T) {
	t.Parallel()
	_, client := setup(t)

	// GIVEN a request option that fails
	// WHEN listing the pipelines
	pipelines, resp, err := client.Pipelines.ListUserPipelines(nil, errorOption)

	// THEN the error is returned
	require.ErrorIs(t, err, errRequestOptionFunc)
	assert.Nil(t, resp)
	assert.Nil(t, pipelines)
}

func TestListUserPipelines_Unauthorized(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)

	// GIVEN a server that rejects the request
	mux.HandleFunc("/api/v4/pipelines", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusUnauthorized)
	})

	// WHEN listing the pipelines without options
	pipelines, resp, err := client.Pipelines.ListUserPipelines(nil)

	// THEN a 401 error is returned
	require.Error(t, err)
	assert.Nil(t, pipelines)
	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
}

func TestGetPipeline(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)
+45
Changes for testing/pipelines_mock.go: 45 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -443,6 +443,51 @@ func (c *MockPipelinesServiceInterfaceListProjectPipelinesCall) DoAndReturn(f fu
	return c
}

// ListUserPipelines mocks base method.
func (m *MockPipelinesServiceInterface) ListUserPipelines(opt *gitlab.ListUserPipelinesOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.UserPipeline, *gitlab.Response, error) {
	m.ctrl.T.Helper()
	varargs := []any{opt}
	for _, a := range options {
		varargs = append(varargs, a)
	}
	ret := m.ctrl.Call(m, "ListUserPipelines", varargs...)
	ret0, _ := ret[0].([]*gitlab.UserPipeline)
	ret1, _ := ret[1].(*gitlab.Response)
	ret2, _ := ret[2].(error)
	return ret0, ret1, ret2
}

// ListUserPipelines indicates an expected call of ListUserPipelines.
func (mr *MockPipelinesServiceInterfaceMockRecorder) ListUserPipelines(opt any, options ...any) *MockPipelinesServiceInterfaceListUserPipelinesCall {
	mr.mock.ctrl.T.Helper()
	varargs := append([]any{opt}, options...)
	call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUserPipelines", reflect.TypeOf((*MockPipelinesServiceInterface)(nil).ListUserPipelines), varargs...)
	return &MockPipelinesServiceInterfaceListUserPipelinesCall{Call: call}
}

// MockPipelinesServiceInterfaceListUserPipelinesCall wrap *gomock.Call
type MockPipelinesServiceInterfaceListUserPipelinesCall struct {
	*gomock.Call
}

// Return rewrite *gomock.Call.Return
func (c *MockPipelinesServiceInterfaceListUserPipelinesCall) Return(arg0 []*gitlab.UserPipeline, arg1 *gitlab.Response, arg2 error) *MockPipelinesServiceInterfaceListUserPipelinesCall {
	c.Call = c.Call.Return(arg0, arg1, arg2)
	return c
}

// Do rewrite *gomock.Call.Do
func (c *MockPipelinesServiceInterfaceListUserPipelinesCall) Do(f func(*gitlab.ListUserPipelinesOptions, ...gitlab.RequestOptionFunc) ([]*gitlab.UserPipeline, *gitlab.Response, error)) *MockPipelinesServiceInterfaceListUserPipelinesCall {
	c.Call = c.Call.Do(f)
	return c
}

// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockPipelinesServiceInterfaceListUserPipelinesCall) DoAndReturn(f func(*gitlab.ListUserPipelinesOptions, ...gitlab.RequestOptionFunc) ([]*gitlab.UserPipeline, *gitlab.Response, error)) *MockPipelinesServiceInterfaceListUserPipelinesCall {
	c.Call = c.Call.DoAndReturn(f)
	return c
}

// RetryPipelineBuild mocks base method.
func (m *MockPipelinesServiceInterface) RetryPipelineBuild(pid any, pipeline int64, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error) {
	m.ctrl.T.Helper()