Skip to content

Commit beec4da

Browse files
Merge pull request #261 from adivishy1/master
[feat]: [CDS-73572]: Support List Repo Live Search for all git providers
2 parents 3947016 + 70cccf8 commit beec4da

39 files changed

+757
-30
lines changed

scm/client.go

+13
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@ type (
7575
PageListOptions ListOptions
7676
}
7777

78+
// RepoListOptions specifies optional repo search term and pagination
79+
// parameters.
80+
RepoListOptions struct {
81+
ListOptions
82+
RepoSearchTerm
83+
}
84+
85+
// RepoSearchTerm specifies searchable parameters.
86+
RepoSearchTerm struct {
87+
RepoName string
88+
User string
89+
}
90+
7891
// ListOptions specifies optional pagination
7992
// parameters.
8093
ListOptions struct {

scm/driver/azure/git_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func TestGitListBranchesV2(t *testing.T) {
130130
Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
131131
Reply(200).
132132
Type("application/json").
133-
File("testdata/branchesFilter.json")
133+
File("testdata/branches_filter.json")
134134

135135
client := NewDefault("ORG", "PROJ")
136136
got, _, err := client.Git.ListBranchesV2(context.Background(), "REPOID", scm.BranchListOptions{SearchTerm: "main"})
@@ -140,7 +140,7 @@ func TestGitListBranchesV2(t *testing.T) {
140140
}
141141

142142
want := []*scm.Reference{}
143-
raw, _ := ioutil.ReadFile("testdata/branchesFilter.json.golden")
143+
raw, _ := ioutil.ReadFile("testdata/branches_filter.json.golden")
144144
_ = json.Unmarshal(raw, &want)
145145

146146
if diff := cmp.Diff(got, want); diff != "" {

scm/driver/azure/repo.go

+6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ func (s *RepositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
5656
return convertRepositoryList(out), res, err
5757
}
5858

59+
// ListV2 returns the user repository list.
60+
func (s *RepositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
61+
// Azure does not support search filters, hence calling List api without search filtering
62+
return s.List(ctx, opts.ListOptions)
63+
}
64+
5965
// ListHooks returns a list or repository hooks.
6066
func (s *RepositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
6167
// https://docs.microsoft.com/en-us/rest/api/azure/devops/hooks/subscriptions/list?view=azure-devops-rest-6.0

scm/driver/bitbucket/git_test.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -216,20 +216,19 @@ func TestGitListBranchesV2(t *testing.T) {
216216
MatchParam("pagelen", "30").
217217
Reply(200).
218218
Type("application/json").
219-
File("testdata/branchesFilter.json")
219+
File("testdata/branches_filter.json")
220220

221221
client, _ := New("https://api.bitbucket.org")
222-
got, res, err := client.Git.ListBranchesV2(context.Background(), "atlassian/stash-example-plugin", scm.BranchListOptions{SearchTerm: "mast", PageListOptions: struct {
223-
URL string
224-
Page int
225-
Size int
226-
}{Page: 1, Size: 30}})
222+
got, res, err := client.Git.ListBranchesV2(context.Background(), "atlassian/stash-example-plugin", scm.BranchListOptions{
223+
SearchTerm: "mast",
224+
PageListOptions: scm.ListOptions{Page: 1, Size: 30},
225+
})
227226
if err != nil {
228227
t.Error(err)
229228
}
230229

231230
want := []*scm.Reference{}
232-
raw, _ := ioutil.ReadFile("testdata/branchesFilter.json.golden")
231+
raw, _ := ioutil.ReadFile("testdata/branches_filter.json.golden")
233232
json.Unmarshal(raw, &want)
234233

235234
if diff := cmp.Diff(got, want); diff != "" {

scm/driver/bitbucket/repo.go

+12
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
105105
return convertRepositoryList(out), res, err
106106
}
107107

108+
// ListV2 returns the user repository list based on the searchTerm passed.
109+
func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
110+
path := fmt.Sprintf("2.0/repositories?%s", encodeRepoListOptions(opts))
111+
if opts.ListOptions.URL != "" {
112+
path = opts.ListOptions.URL
113+
}
114+
out := new(repositories)
115+
res, err := s.client.do(ctx, "GET", path, nil, &out)
116+
copyPagination(out.pagination, res)
117+
return convertRepositoryList(out), res, err
118+
}
119+
108120
// ListHooks returns a list or repository hooks.
109121
func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
110122
path := fmt.Sprintf("2.0/repositories/%s/hooks?%s", repo, encodeListOptions(opts))

scm/driver/bitbucket/repo_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,37 @@ func TestRepositoryList(t *testing.T) {
136136
}
137137
}
138138

139+
func TestRepositoryListV2(t *testing.T) {
140+
defer gock.Off()
141+
142+
gock.New("https://api.bitbucket.org").
143+
Get("/2.0/repositories").
144+
MatchParam("q", "name~\\\"plugin1\\\"").
145+
MatchParam("role", "member").
146+
Reply(200).
147+
Type("application/json").
148+
File("testdata/repos_filter.json")
149+
150+
got := []*scm.Repository{}
151+
opts := scm.RepoListOptions{RepoSearchTerm: scm.RepoSearchTerm{RepoName: "plugin1"}}
152+
client, _ := New("https://api.bitbucket.org")
153+
154+
repos, _, err := client.Repositories.ListV2(context.Background(), opts)
155+
if err != nil {
156+
t.Error(err)
157+
}
158+
got = append(got, repos...)
159+
160+
want := []*scm.Repository{}
161+
raw, _ := ioutil.ReadFile("testdata/repos_filter.json.golden")
162+
json.Unmarshal(raw, &want)
163+
164+
if diff := cmp.Diff(got, want); diff != "" {
165+
t.Errorf("Unexpected Results")
166+
t.Log(diff)
167+
}
168+
}
169+
139170
func TestStatusList(t *testing.T) {
140171
defer gock.Off()
141172

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"pagelen": 1,
3+
"values": [
4+
{
5+
"scm": "git",
6+
"website": "",
7+
"has_wiki": false,
8+
"uuid": "{7dd600e6-0d9c-4801-b967-cb4cc17359ff}",
9+
"links": {
10+
"watchers": {
11+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/watchers"
12+
},
13+
"branches": {
14+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/refs\/branches"
15+
},
16+
"tags": {
17+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/refs\/tags"
18+
},
19+
"commits": {
20+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/commits"
21+
},
22+
"clone": [
23+
{
24+
"href": "https:\/\/[email protected]\/atlassian\/stash-example-plugin1.git",
25+
"name": "https"
26+
},
27+
{
28+
"href": "[email protected]:atlassian\/stash-example-plugin1.git",
29+
"name": "ssh"
30+
}
31+
],
32+
"self": {
33+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1"
34+
},
35+
"source": {
36+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/src"
37+
},
38+
"html": {
39+
"href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin1"
40+
},
41+
"avatar": {
42+
"href": "https:\/\/bytebucket.org\/ravatar\/%7B7dd600e6-0d9c-4801-b967-cb4cc17359ff%7D?ts=default"
43+
},
44+
"hooks": {
45+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/hooks"
46+
},
47+
"forks": {
48+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/forks"
49+
},
50+
"downloads": {
51+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/downloads"
52+
},
53+
"pullrequests": {
54+
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/pullrequests"
55+
}
56+
},
57+
"fork_policy": "allow_forks",
58+
"name": "stash-example-plugin1",
59+
"project": {
60+
"key": "PROJ",
61+
"type": "project",
62+
"uuid": "{8b56daff-dbc7-4cae-a7a3-1228c526906b}",
63+
"links": {
64+
"self": {
65+
"href": "https:\/\/api.bitbucket.org\/2.0\/teams\/atlassian\/projects\/PROJ"
66+
},
67+
"html": {
68+
"href": "https:\/\/bitbucket.org\/account\/user\/atlassian\/projects\/PROJ"
69+
},
70+
"avatar": {
71+
"href": "https:\/\/bitbucket.org\/account\/user\/atlassian\/projects\/PROJ\/avatar\/32"
72+
}
73+
},
74+
"name": "Project: Atlassian"
75+
},
76+
"language": "",
77+
"created_on": "2013-04-15T03:05:05.595458+00:00",
78+
"mainbranch": {
79+
"type": "branch",
80+
"name": "master"
81+
},
82+
"full_name": "atlassian\/stash-example-plugin1",
83+
"has_issues": false,
84+
"owner": {
85+
"username": "atlassian",
86+
"display_name": "Atlassian",
87+
"type": "team",
88+
"uuid": "{02b941e3-cfaa-40f9-9a58-cec53e20bdc3}",
89+
"links": {
90+
"self": {
91+
"href": "https:\/\/api.bitbucket.org\/2.0\/teams\/atlassian"
92+
},
93+
"html": {
94+
"href": "https:\/\/bitbucket.org\/atlassian\/"
95+
},
96+
"avatar": {
97+
"href": "https:\/\/bitbucket.org\/account\/atlassian\/avatar\/32\/"
98+
}
99+
}
100+
},
101+
"updated_on": "2018-04-01T16:36:35.970175+00:00",
102+
"size": 1116345,
103+
"type": "repository",
104+
"slug": "stash-example-plugin1",
105+
"is_private": true,
106+
"description": "Examples on how to decorate various pages around Stash."
107+
}
108+
],
109+
"next": "https:\/\/api.bitbucket.org\/2.0\/repositories?pagelen=1&after=PLACEHOLDER&role=member"
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[
2+
{
3+
"ID": "{7dd600e6-0d9c-4801-b967-cb4cc17359ff}",
4+
"Namespace": "atlassian",
5+
"Name": "stash-example-plugin1",
6+
"Perm": null,
7+
"Branch": "master",
8+
"Private": true,
9+
"Clone": "https://bitbucket.org/atlassian/stash-example-plugin1.git",
10+
"CloneSSH": "[email protected]:atlassian/stash-example-plugin1.git",
11+
"Link": "https://bitbucket.org/atlassian/stash-example-plugin1",
12+
"Created": "2013-04-15T03:05:05.595458Z",
13+
"Updated": "2018-04-01T16:36:35.970175Z"
14+
}
15+
]

scm/driver/bitbucket/util.go

+28-5
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ func encodeBranchListOptions(opts scm.BranchListOptions) string {
3434
sb.WriteString("\"")
3535
params.Set("q", sb.String())
3636
}
37-
if opts.PageListOptions.Page != 0 {
38-
params.Set("page", strconv.Itoa(opts.PageListOptions.Page))
39-
}
40-
if opts.PageListOptions.Size != 0 {
41-
params.Set("pagelen", strconv.Itoa(opts.PageListOptions.Size))
37+
if opts.PageListOptions != (scm.ListOptions{}) {
38+
if opts.PageListOptions.Page != 0 {
39+
params.Set("page", strconv.Itoa(opts.PageListOptions.Page))
40+
}
41+
if opts.PageListOptions.Size != 0 {
42+
params.Set("pagelen", strconv.Itoa(opts.PageListOptions.Size))
43+
}
4244
}
4345
return params.Encode()
4446
}
@@ -66,6 +68,27 @@ func encodeListRoleOptions(opts scm.ListOptions) string {
6668
return params.Encode()
6769
}
6870

71+
func encodeRepoListOptions(opts scm.RepoListOptions) string {
72+
params := url.Values{}
73+
if opts.RepoSearchTerm.RepoName != "" {
74+
var sb strings.Builder
75+
sb.WriteString("name~\"")
76+
sb.WriteString(opts.RepoSearchTerm.RepoName)
77+
sb.WriteString("\"")
78+
params.Set("q", sb.String())
79+
}
80+
if opts.ListOptions != (scm.ListOptions{}) {
81+
if opts.ListOptions.Page != 0 {
82+
params.Set("page", strconv.Itoa(opts.ListOptions.Page))
83+
}
84+
if opts.ListOptions.Size != 0 {
85+
params.Set("pagelen", strconv.Itoa(opts.ListOptions.Size))
86+
}
87+
}
88+
params.Set("role", "member")
89+
return params.Encode()
90+
}
91+
6992
func encodeCommitListOptions(opts scm.CommitListOptions) string {
7093
params := url.Values{}
7194
if opts.Page != 0 {

scm/driver/gitea/repo.go

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
4646
return convertRepositoryList(out), res, err
4747
}
4848

49+
func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
50+
// gitea does not support search filters, hence calling List api without search filtering
51+
return s.List(ctx, opts.ListOptions)
52+
}
53+
4954
func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
5055
path := fmt.Sprintf("api/v1/repos/%s/hooks?%s", repo, encodeListOptions(opts))
5156
out := []*hook{}

scm/driver/gitee/repo.go

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ func (s *RepositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
4545
res, err := s.client.do(ctx, "GET", path, nil, &out)
4646
return convertRepositoryList(out), res, err
4747
}
48+
func (s *RepositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
49+
// gitee does not support search filters, hence calling List api without search filtering
50+
return s.List(ctx, opts.ListOptions)
51+
}
4852

4953
func (s *RepositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
5054
path := fmt.Sprintf("repos/%s/hooks?%s", repo, encodeListOptions(opts))

scm/driver/github/repo.go

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ type repository struct {
4040
} `json:"permissions"`
4141
}
4242

43+
type searchRepositoryList struct {
44+
Repositories []*repository `json:"items"`
45+
}
46+
4347
type hook struct {
4448
ID int `json:"id,omitempty"`
4549
Name string `json:"name"`
@@ -110,6 +114,14 @@ func (s *RepositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
110114
return convertRepositoryList(out), res, err
111115
}
112116

117+
// ListV2 returns the user repository list based on the searchTerm passed.
118+
func (s *RepositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
119+
path := fmt.Sprintf("search/repositories?%s", encodeRepoListOptions(opts))
120+
out := new(searchRepositoryList)
121+
res, err := s.client.do(ctx, "GET", path, nil, &out)
122+
return convertRepositoryList(out.Repositories), res, err
123+
}
124+
113125
// List returns the github app installation repository list.
114126
func (s *RepositoryService) ListByInstallation(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
115127
path := fmt.Sprintf("installation/repositories?%s", encodeListOptions(opts))

0 commit comments

Comments
 (0)