Verified Commit b9beb065 authored by José M. Requena Plens's avatar José M. Requena Plens Committed by GitLab
Browse files

fix(search): accept nil SearchOptions in every search method

Changelog: Improvements
parent b626a602
Loading
Loading
Loading
Loading
+35 −28
Changes for search.go: 35 added lines, 28 removed lines.
Original line number Diff line number Diff line
@@ -77,13 +77,23 @@ type searchOptions struct {
	Search string `url:"search" json:"search"`
}

// newSearchOptions returns the query parameters of a search request. A nil
// opt means no options, so only the scope and the search term are sent.
func newSearchOptions(opt *SearchOptions, scope, query string) *searchOptions {
	o := &searchOptions{Scope: scope, Search: query}
	if opt != nil {
		o.SearchOptions = *opt
	}
	return o
}

// Projects searches the expression within projects
//
// GitLab API docs: https://docs.gitlab.com/api/search/#scope-projects
func (s *SearchService) Projects(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
	return do[[]*Project](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "projects", Search: query}),
		withAPIOpts(newSearchOptions(opt, "projects", query)),
		withRequestOpts(options...),
	)
}
@@ -95,7 +105,7 @@ func (s *SearchService) Projects(query string, opt *SearchOptions, options ...Re
func (s *SearchService) ProjectsByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
	return do[[]*Project](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "projects", Search: query}),
		withAPIOpts(newSearchOptions(opt, "projects", query)),
		withRequestOpts(options...),
	)
}
@@ -106,7 +116,7 @@ func (s *SearchService) ProjectsByGroup(gid any, query string, opt *SearchOption
func (s *SearchService) Issues(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	return do[[]*Issue](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "issues", Search: query}), //nolint:goconst
		withAPIOpts(newSearchOptions(opt, "issues", query)),
		withRequestOpts(options...),
	)
}
@@ -118,7 +128,7 @@ func (s *SearchService) Issues(query string, opt *SearchOptions, options ...Requ
func (s *SearchService) IssuesByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	return do[[]*Issue](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "issues", Search: query}),
		withAPIOpts(newSearchOptions(opt, "issues", query)),
		withRequestOpts(options...),
	)
}
@@ -130,7 +140,7 @@ func (s *SearchService) IssuesByGroup(gid any, query string, opt *SearchOptions,
func (s *SearchService) IssuesByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	return do[[]*Issue](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "issues", Search: query}),
		withAPIOpts(newSearchOptions(opt, "issues", query)),
		withRequestOpts(options...),
	)
}
@@ -142,7 +152,7 @@ func (s *SearchService) IssuesByProject(pid any, query string, opt *SearchOption
func (s *SearchService) MergeRequests(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	return do[[]*MergeRequest](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "merge_requests", Search: query}), //nolint:goconst
		withAPIOpts(newSearchOptions(opt, "merge_requests", query)),
		withRequestOpts(options...),
	)
}
@@ -155,7 +165,7 @@ func (s *SearchService) MergeRequests(query string, opt *SearchOptions, options
func (s *SearchService) MergeRequestsByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	return do[[]*MergeRequest](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "merge_requests", Search: query}),
		withAPIOpts(newSearchOptions(opt, "merge_requests", query)),
		withRequestOpts(options...),
	)
}
@@ -168,7 +178,7 @@ func (s *SearchService) MergeRequestsByGroup(gid any, query string, opt *SearchO
func (s *SearchService) MergeRequestsByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	return do[[]*MergeRequest](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "merge_requests", Search: query}),
		withAPIOpts(newSearchOptions(opt, "merge_requests", query)),
		withRequestOpts(options...),
	)
}
@@ -179,7 +189,7 @@ func (s *SearchService) MergeRequestsByProject(pid any, query string, opt *Searc
func (s *SearchService) Milestones(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) {
	return do[[]*Milestone](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "milestones", Search: query}),
		withAPIOpts(newSearchOptions(opt, "milestones", query)),
		withRequestOpts(options...),
	)
}
@@ -191,7 +201,7 @@ func (s *SearchService) Milestones(query string, opt *SearchOptions, options ...
func (s *SearchService) MilestonesByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) {
	return do[[]*Milestone](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "milestones", Search: query}),
		withAPIOpts(newSearchOptions(opt, "milestones", query)),
		withRequestOpts(options...),
	)
}
@@ -203,7 +213,7 @@ func (s *SearchService) MilestonesByGroup(gid any, query string, opt *SearchOpti
func (s *SearchService) MilestonesByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) {
	return do[[]*Milestone](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "milestones", Search: query}),
		withAPIOpts(newSearchOptions(opt, "milestones", query)),
		withRequestOpts(options...),
	)
}
@@ -215,7 +225,7 @@ func (s *SearchService) MilestonesByProject(pid any, query string, opt *SearchOp
func (s *SearchService) SnippetTitles(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) {
	return do[[]*Snippet](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "snippet_titles", Search: query}),
		withAPIOpts(newSearchOptions(opt, "snippet_titles", query)),
		withRequestOpts(options...),
	)
}
@@ -227,7 +237,7 @@ func (s *SearchService) SnippetTitles(query string, opt *SearchOptions, options
func (s *SearchService) NotesByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) {
	return do[[]*Note](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "notes", Search: query}),
		withAPIOpts(newSearchOptions(opt, "notes", query)),
		withRequestOpts(options...),
	)
}
@@ -239,7 +249,7 @@ func (s *SearchService) NotesByProject(pid any, query string, opt *SearchOptions
func (s *SearchService) WikiBlobs(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) {
	return do[[]*Wiki](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "wiki_blobs", Search: query}),
		withAPIOpts(newSearchOptions(opt, "wiki_blobs", query)),
		withRequestOpts(options...),
	)
}
@@ -252,7 +262,7 @@ func (s *SearchService) WikiBlobs(query string, opt *SearchOptions, options ...R
func (s *SearchService) WikiBlobsByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) {
	return do[[]*Wiki](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "wiki_blobs", Search: query}),
		withAPIOpts(newSearchOptions(opt, "wiki_blobs", query)),
		withRequestOpts(options...),
	)
}
@@ -265,7 +275,7 @@ func (s *SearchService) WikiBlobsByGroup(gid any, query string, opt *SearchOptio
func (s *SearchService) WikiBlobsByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) {
	return do[[]*Wiki](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "wiki_blobs", Search: query}),
		withAPIOpts(newSearchOptions(opt, "wiki_blobs", query)),
		withRequestOpts(options...),
	)
}
@@ -276,7 +286,7 @@ func (s *SearchService) WikiBlobsByProject(pid any, query string, opt *SearchOpt
func (s *SearchService) Commits(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) {
	return do[[]*Commit](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "commits", Search: query}),
		withAPIOpts(newSearchOptions(opt, "commits", query)),
		withRequestOpts(options...),
	)
}
@@ -288,7 +298,7 @@ func (s *SearchService) Commits(query string, opt *SearchOptions, options ...Req
func (s *SearchService) CommitsByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) {
	return do[[]*Commit](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "commits", Search: query}),
		withAPIOpts(newSearchOptions(opt, "commits", query)),
		withRequestOpts(options...),
	)
}
@@ -300,7 +310,7 @@ func (s *SearchService) CommitsByGroup(gid any, query string, opt *SearchOptions
func (s *SearchService) CommitsByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) {
	return do[[]*Commit](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "commits", Search: query}),
		withAPIOpts(newSearchOptions(opt, "commits", query)),
		withRequestOpts(options...),
	)
}
@@ -323,7 +333,7 @@ type Blob struct {
func (s *SearchService) Blobs(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Blob, *Response, error) {
	return do[[]*Blob](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "blobs", Search: query}),
		withAPIOpts(newSearchOptions(opt, "blobs", query)),
		withRequestOpts(options...),
	)
}
@@ -335,7 +345,7 @@ func (s *SearchService) Blobs(query string, opt *SearchOptions, options ...Reque
func (s *SearchService) BlobsByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Blob, *Response, error) {
	return do[[]*Blob](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "blobs", Search: query}),
		withAPIOpts(newSearchOptions(opt, "blobs", query)),
		withRequestOpts(options...),
	)
}
@@ -347,7 +357,7 @@ func (s *SearchService) BlobsByGroup(gid any, query string, opt *SearchOptions,
func (s *SearchService) BlobsByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Blob, *Response, error) {
	return do[[]*Blob](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "blobs", Search: query}),
		withAPIOpts(newSearchOptions(opt, "blobs", query)),
		withRequestOpts(options...),
	)
}
@@ -358,7 +368,7 @@ func (s *SearchService) BlobsByProject(pid any, query string, opt *SearchOptions
func (s *SearchService) Users(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*User, *Response, error) {
	return do[[]*User](s.client,
		withPath(routeSearch),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "users", Search: query}),
		withAPIOpts(newSearchOptions(opt, "users", query)),
		withRequestOpts(options...),
	)
}
@@ -368,12 +378,9 @@ func (s *SearchService) Users(query string, opt *SearchOptions, options ...Reque
//
// GitLab API docs: https://docs.gitlab.com/api/search/#scope-users-1
func (s *SearchService) UsersByGroup(gid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*User, *Response, error) {
	if opt == nil {
		opt = &SearchOptions{}
	}
	return do[[]*User](s.client,
		withPath(routeGroupsIDSearch, GroupID{gid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "users", Search: query}),
		withAPIOpts(newSearchOptions(opt, "users", query)),
		withRequestOpts(options...),
	)
}
@@ -385,7 +392,7 @@ func (s *SearchService) UsersByGroup(gid any, query string, opt *SearchOptions,
func (s *SearchService) UsersByProject(pid any, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*User, *Response, error) {
	return do[[]*User](s.client,
		withPath(routeProjectsIDSearch, ProjectID{pid}),
		withAPIOpts(&searchOptions{SearchOptions: *opt, Scope: "users", Search: query}),
		withAPIOpts(newSearchOptions(opt, "users", query)),
		withRequestOpts(options...),
	)
}
+178 −0
Changes for search_test.go: 178 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -19,9 +19,11 @@ package gitlab
import (
	"fmt"
	"net/http"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestSearchService_Users(t *testing.T) {
@@ -687,3 +689,179 @@ func TestSearchService_BlobsByProject(t *testing.T) {
	assert.NotNil(t, resp)
	assert.Len(t, blobs, 1)
}

func TestSearchService_SearchOptions(t *testing.T) {
	t.Parallel()

	const (
		searchPath        = "/api/v4/search"
		groupSearchPath   = "/api/v4/groups/1/-/search"
		projectSearchPath = "/api/v4/projects/1/-/search"
	)

	tests := []struct {
		name  string
		path  string
		scope string
		call  func(s SearchServiceInterface, opt *SearchOptions) (int, error)
	}{
		{"Projects", searchPath, "projects", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.Projects("fix", opt)
			return len(r), err
		}},
		{"ProjectsByGroup", groupSearchPath, "projects", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.ProjectsByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"Issues", searchPath, "issues", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.Issues("fix", opt)
			return len(r), err
		}},
		{"IssuesByGroup", groupSearchPath, "issues", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.IssuesByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"IssuesByProject", projectSearchPath, "issues", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.IssuesByProject(1, "fix", opt)
			return len(r), err
		}},
		{"MergeRequests", searchPath, "merge_requests", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.MergeRequests("fix", opt)
			return len(r), err
		}},
		{"MergeRequestsByGroup", groupSearchPath, "merge_requests", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.MergeRequestsByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"MergeRequestsByProject", projectSearchPath, "merge_requests", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.MergeRequestsByProject(1, "fix", opt)
			return len(r), err
		}},
		{"Milestones", searchPath, "milestones", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.Milestones("fix", opt)
			return len(r), err
		}},
		{"MilestonesByGroup", groupSearchPath, "milestones", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.MilestonesByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"MilestonesByProject", projectSearchPath, "milestones", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.MilestonesByProject(1, "fix", opt)
			return len(r), err
		}},
		{"SnippetTitles", searchPath, "snippet_titles", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.SnippetTitles("fix", opt)
			return len(r), err
		}},
		{"NotesByProject", projectSearchPath, "notes", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.NotesByProject(1, "fix", opt)
			return len(r), err
		}},
		{"WikiBlobs", searchPath, "wiki_blobs", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.WikiBlobs("fix", opt)
			return len(r), err
		}},
		{"WikiBlobsByGroup", groupSearchPath, "wiki_blobs", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.WikiBlobsByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"WikiBlobsByProject", projectSearchPath, "wiki_blobs", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.WikiBlobsByProject(1, "fix", opt)
			return len(r), err
		}},
		{"Commits", searchPath, "commits", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.Commits("fix", opt)
			return len(r), err
		}},
		{"CommitsByGroup", groupSearchPath, "commits", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.CommitsByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"CommitsByProject", projectSearchPath, "commits", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.CommitsByProject(1, "fix", opt)
			return len(r), err
		}},
		{"Blobs", searchPath, "blobs", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.Blobs("fix", opt)
			return len(r), err
		}},
		{"BlobsByGroup", groupSearchPath, "blobs", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.BlobsByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"BlobsByProject", projectSearchPath, "blobs", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.BlobsByProject(1, "fix", opt)
			return len(r), err
		}},
		{"Users", searchPath, "users", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.Users("fix", opt)
			return len(r), err
		}},
		{"UsersByGroup", groupSearchPath, "users", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.UsersByGroup(1, "fix", opt)
			return len(r), err
		}},
		{"UsersByProject", projectSearchPath, "users", func(s SearchServiceInterface, opt *SearchOptions) (int, error) {
			r, _, err := s.UsersByProject(1, "fix", opt)
			return len(r), err
		}},
	}

	for _, tt := range tests {
		t.Run(tt.name+"/nil options", func(t *testing.T) {
			t.Parallel()
			mux, client := setup(t)

			mux.HandleFunc(tt.path, func(w http.ResponseWriter, r *http.Request) {
				testMethod(t, r, http.MethodGet)
				assert.Equal(t, url.Values{"scope": {tt.scope}, "search": {"fix"}}, r.URL.Query())
				fmt.Fprint(w, `[{}]`)
			})

			// GIVEN nil search options
			// WHEN the search method is called
			var (
				n   int
				err error
			)
			require.NotPanics(t, func() { n, err = tt.call(client.Search, nil) })

			// THEN the request carries only the scope and the search term
			require.NoError(t, err)
			assert.Equal(t, 1, n)
		})

		t.Run(tt.name+"/with options", func(t *testing.T) {
			t.Parallel()
			mux, client := setup(t)

			mux.HandleFunc(tt.path, func(w http.ResponseWriter, r *http.Request) {
				testMethod(t, r, http.MethodGet)
				want := url.Values{
					"scope":       {tt.scope},
					"search":      {"fix"},
					"page":        {"2"},
					"per_page":    {"5"},
					"ref":         {"main"},
					"search_type": {"basic"},
				}
				assert.Equal(t, want, r.URL.Query())
				fmt.Fprint(w, `[{}]`)
			})

			// GIVEN search options
			opt := &SearchOptions{
				ListOptions: ListOptions{Page: 2, PerPage: 5},
				Ref:         new("main"),
				SearchType:  new(BasicSearch),
			}

			// WHEN the search method is called
			n, err := tt.call(client.Search, opt)

			// THEN the options are sent with the scope and the search term
			require.NoError(t, err)
			assert.Equal(t, 1, n)
		})
	}
}