Skip to content

Commit 48df39c

Browse files
authored
Merge pull request #1188 from t0m4uk1991/repo_search_by_fork_and_visibility
Add ability to search repos by fork and visibility
2 parents f7c170c + 1994f47 commit 48df39c

File tree

11 files changed

+2067
-1
lines changed

11 files changed

+2067
-1
lines changed

src/main/java/org/kohsuke/github/GHRepository.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,20 @@ public boolean isPrivate() {
727727
* Visibility of a repository.
728728
*/
729729
public enum Visibility {
730-
PUBLIC, INTERNAL, PRIVATE, UNKNOWN;
730+
PUBLIC,
731+
INTERNAL,
732+
PRIVATE,
733+
734+
/**
735+
* Placeholder for unexpected data values.
736+
*
737+
* This avoids throwing exceptions during data binding or reading when the list of allowed values returned from
738+
* GitHub is expanded.
739+
*
740+
* Do not pass this value to any methods. If this value is returned during a request, check the log output and
741+
* report an issue for the missing value.
742+
*/
743+
UNKNOWN;
731744

732745
public static Visibility from(String value) {
733746
return EnumUtils.getNullableEnumOrDefault(Visibility.class, value, Visibility.UNKNOWN);

src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,62 @@ public GHRepositorySearchBuilder forks(String v) {
5252
return q("forks:" + v);
5353
}
5454

55+
/**
56+
* Searching in forks
57+
*
58+
* The default search mode is {@link Fork#PARENT_ONLY}. In that mode, forks are not included in search results.
59+
*
60+
* <p>
61+
* Passing {@link Fork#PARENT_AND_FORKS} or {@link Fork#FORKS_ONLY} will show results from forks, but only if they
62+
* have more stars than the parent repository.
63+
*
64+
* <p>
65+
* IMPORTANT: Regardless of this setting, no search results will ever be returned for forks with equal or fewer
66+
* stars than the parent repository. Forks with less stars than the parent repository are not included in the index
67+
* for code searching.
68+
*
69+
* @param fork
70+
* search mode for forks
71+
*
72+
* @return the gh repository search builder
73+
*
74+
* @see <a href=
75+
* "https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-in-forks">Searching
76+
* in forks</a>
77+
*
78+
*/
79+
public GHRepositorySearchBuilder fork(Fork fork) {
80+
if (Fork.PARENT_ONLY.equals(fork)) {
81+
this.terms.removeIf(term -> term.contains("fork:"));
82+
return this;
83+
}
84+
85+
return q("fork:" + fork);
86+
}
87+
88+
/**
89+
* Search by repository visibility
90+
*
91+
* @param visibility
92+
* repository visibility
93+
*
94+
* @return the gh repository search builder
95+
* @throws GHException
96+
* if {@link GHRepository.Visibility#UNKNOWN} is passed. UNKNOWN is a placeholder for unexpected values
97+
* encountered when reading data.
98+
* @see <a href=
99+
* "https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-for-repositories#search-by-repository-visibility">Search
100+
* by repository visibility</a>
101+
*/
102+
public GHRepositorySearchBuilder visibility(GHRepository.Visibility visibility) {
103+
if (visibility == GHRepository.Visibility.UNKNOWN) {
104+
throw new GHException(
105+
"UNKNOWN is a placeholder for unexpected values encountered when reading data. It cannot be passed as a search parameter.");
106+
}
107+
108+
return q("is:" + visibility);
109+
}
110+
55111
/**
56112
* Created gh repository search builder.
57113
*
@@ -160,6 +216,42 @@ public enum Sort {
160216
STARS, FORKS, UPDATED
161217
}
162218

219+
/**
220+
* The enum for Fork search mode
221+
*/
222+
public enum Fork {
223+
224+
/**
225+
* Search in the parent repository and in forks with more stars than the parent repository.
226+
*
227+
* Forks with the same or fewer stars than the parent repository are still ignored.
228+
*/
229+
PARENT_AND_FORKS("true"),
230+
231+
/**
232+
* Search only in forks with more stars than the parent repository.
233+
*
234+
* The parent repository is ignored. If no forks have more stars than the parent, no results will be returned.
235+
*/
236+
FORKS_ONLY("only"),
237+
238+
/**
239+
* (Default) Search only the parent repository.
240+
*
241+
* Forks are ignored.
242+
*/
243+
PARENT_ONLY("");
244+
245+
private String filterMode;
246+
Fork(String mode) {
247+
this.filterMode = mode;
248+
}
249+
250+
public String toString() {
251+
return filterMode;
252+
}
253+
}
254+
163255
private static class RepositorySearchResult extends SearchResult<GHRepository> {
164256
private GHRepository[] items;
165257

src/test/java/org/kohsuke/github/GHRepositoryTest.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static org.hamcrest.Matchers.*;
2222
import static org.hamcrest.core.IsInstanceOf.instanceOf;
23+
import static org.junit.Assert.assertThrows;
2324
import static org.kohsuke.github.GHVerification.Reason.*;
2425

2526
/**
@@ -417,6 +418,66 @@ public void listCommitCommentsNoComments() throws IOException {
417418
assertThat("Commit has no comments", commitComments.isEmpty());
418419
}
419420

421+
@Test
422+
public void searchAllPublicAndForkedRepos() throws IOException {
423+
PagedSearchIterable<GHRepository> list = gitHub.searchRepositories()
424+
.user("t0m4uk1991")
425+
.visibility(GHRepository.Visibility.PUBLIC)
426+
.fork(GHRepositorySearchBuilder.Fork.PARENT_AND_FORKS)
427+
.list();
428+
List<GHRepository> u = list.toList();
429+
assertThat(u.size(), is(14));
430+
assertThat(u.stream().filter(item -> item.getName().equals("github-api")).count(), is(1L));
431+
assertThat(u.stream().filter(item -> item.getName().equals("Complete-Python-3-Bootcamp")).count(), is(1L));
432+
}
433+
434+
@Test
435+
public void searchForPublicForkedOnlyRepos() throws IOException {
436+
PagedSearchIterable<GHRepository> list = gitHub.searchRepositories()
437+
.user("t0m4uk1991")
438+
.visibility(GHRepository.Visibility.PUBLIC)
439+
.fork(GHRepositorySearchBuilder.Fork.FORKS_ONLY)
440+
.list();
441+
List<GHRepository> u = list.toList();
442+
assertThat(u.size(), is(2));
443+
assertThat(u.get(0).getName(), is("github-api"));
444+
assertThat(u.get(1).getName(), is("Complete-Python-3-Bootcamp"));
445+
}
446+
447+
@Test
448+
public void ghRepositorySearchBuilderIgnoresUnknownVisibility() {
449+
GHRepositorySearchBuilder ghRepositorySearchBuilder;
450+
451+
GHException exception = assertThrows(GHException.class,
452+
() -> new GHRepositorySearchBuilder(gitHub).visibility(Visibility.UNKNOWN));
453+
assertThat(exception.getMessage(),
454+
startsWith("UNKNOWN is a placeholder for unexpected values encountered when reading data."));
455+
456+
ghRepositorySearchBuilder = new GHRepositorySearchBuilder(gitHub).visibility(Visibility.PUBLIC);
457+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("is:")).count(), is(1L));
458+
459+
ghRepositorySearchBuilder = new GHRepositorySearchBuilder(gitHub).visibility(Visibility.PRIVATE);
460+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("is:")).count(), is(1L));
461+
462+
ghRepositorySearchBuilder = new GHRepositorySearchBuilder(gitHub).visibility(Visibility.INTERNAL);
463+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("is:")).count(), is(1L));
464+
}
465+
466+
@Test
467+
public void ghRepositorySearchBuilderForkDefaultResetForksSearchTerms() {
468+
GHRepositorySearchBuilder ghRepositorySearchBuilder = new GHRepositorySearchBuilder(gitHub);
469+
ghRepositorySearchBuilder = ghRepositorySearchBuilder.fork(GHRepositorySearchBuilder.Fork.PARENT_AND_FORKS);
470+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("fork:true")).count(), is(1L));
471+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("fork:")).count(), is(1L));
472+
473+
ghRepositorySearchBuilder = ghRepositorySearchBuilder.fork(GHRepositorySearchBuilder.Fork.FORKS_ONLY);
474+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("fork:only")).count(), is(1L));
475+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("fork:")).count(), is(2L));
476+
477+
ghRepositorySearchBuilder = ghRepositorySearchBuilder.fork(GHRepositorySearchBuilder.Fork.PARENT_ONLY);
478+
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("fork:")).count(), is(0L));
479+
}
480+
420481
@Test
421482
public void listCommitCommentsSomeComments() throws IOException {
422483
List<GHCommitComment> commitComments = getRepository()

0 commit comments

Comments
 (0)