diff --git a/src/main/java/com/spotify/github/v3/clients/TeamClient.java b/src/main/java/com/spotify/github/v3/clients/TeamClient.java index d5380bb1..630c86de 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -21,11 +21,9 @@ package com.spotify.github.v3.clients; -import static com.spotify.github.v3.clients.GitHubClient.IGNORE_RESPONSE_CONSUMER; -import static com.spotify.github.v3.clients.GitHubClient.LIST_PENDING_TEAM_INVITATIONS; -import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS; -import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAM_MEMBERS; +import static com.spotify.github.v3.clients.GitHubClient.*; +import com.spotify.github.async.AsyncPage; import com.spotify.github.v3.Team; import com.spotify.github.v3.User; import com.spotify.github.v3.orgs.Membership; @@ -34,6 +32,7 @@ import com.spotify.github.v3.orgs.requests.TeamCreate; import com.spotify.github.v3.orgs.requests.TeamUpdate; import java.lang.invoke.MethodHandles; +import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; @@ -49,6 +48,8 @@ public class TeamClient { private static final String MEMBERS_TEMPLATE = "/orgs/%s/teams/%s/members"; + private static final String PAGED_MEMBERS_TEMPLATE = "/orgs/%s/teams/%s/members?per_page=%d"; + private static final String MEMBERSHIP_TEMPLATE = "/orgs/%s/teams/%s/memberships/%s"; private static final String INVITATIONS_TEMPLATE = "/orgs/%s/teams/%s/invitations"; @@ -163,6 +164,19 @@ public CompletableFuture> listTeamMembers(final String slug) { return github.request(path, LIST_TEAM_MEMBERS); } + /** + * List members of a specific team. + * + * @param slug the team slug + * @param pageSize the number of users to fetch per page + * @return list of all users in a team + */ + public Iterator> listTeamMembers(final String slug, final int pageSize) { + final String path = String.format(PAGED_MEMBERS_TEMPLATE, org, slug, pageSize); + log.debug("Fetching members for: " + path); + return new GithubPageIterator<>(new GithubPage<>(github, path, LIST_TEAM_MEMBERS)); + } + /** * Delete a membership for a user. * diff --git a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java index 8ceb0f17..9435f1b8 100644 --- a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java @@ -24,8 +24,11 @@ import static com.spotify.github.v3.clients.GitHubClient.LIST_PENDING_TEAM_INVITATIONS; import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS; import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAM_MEMBERS; +import static com.spotify.github.v3.clients.MockHelper.createMockResponse; import static java.nio.charset.Charset.defaultCharset; import static java.util.concurrent.CompletableFuture.completedFuture; +import static java.util.stream.Collectors.toList; +import static java.util.stream.StreamSupport.stream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.mockito.ArgumentMatchers.any; @@ -33,15 +36,18 @@ import static org.mockito.Mockito.*; import com.google.common.io.Resources; +import com.spotify.github.async.AsyncPage; import com.spotify.github.jackson.Json; import com.spotify.github.v3.Team; import com.spotify.github.v3.User; +import com.spotify.github.v3.comment.Comment; import com.spotify.github.v3.orgs.Membership; import com.spotify.github.v3.orgs.TeamInvitation; import com.spotify.github.v3.orgs.requests.MembershipCreate; import com.spotify.github.v3.orgs.requests.TeamCreate; import com.spotify.github.v3.orgs.requests.TeamUpdate; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; import okhttp3.Response; @@ -67,6 +73,7 @@ public void setUp() { teamClient = new TeamClient(github, "github"); json = Json.create(); when(github.json()).thenReturn(json); + when(github.urlFor("")).thenReturn("https://github.com/api/v3"); } @Test @@ -156,6 +163,37 @@ public void listTeamMembers() throws Exception { assertThat(teamMembers.size(), is(2)); } + @Test + public void listTeamMembersPaged() throws Exception { + final String firstPageLink = + "; rel=\"next\", ; rel=\"last\""; + final String firstPageBody = + Resources.toString(getResource(this.getClass(), "list_members_page1.json"), defaultCharset()); + final Response firstPageResponse = createMockResponse(firstPageLink, firstPageBody); + + final String lastPageLink = + "; rel=\"first\", ; rel=\"prev\""; + final String lastPageBody = + Resources.toString(getResource(this.getClass(), "list_members_page2.json"), defaultCharset()); + + final Response lastPageResponse = createMockResponse(lastPageLink, lastPageBody); + + when(github.request(endsWith("/orgs/github/teams/1/members?per_page=1"))) + .thenReturn(completedFuture(firstPageResponse)); + when(github.request(endsWith("/orgs/github/teams/1/members?page=2"))) + .thenReturn(completedFuture(lastPageResponse)); + + final Iterable> pageIterator = () -> teamClient.listTeamMembers("1", 1); + final List users = + stream(pageIterator.spliterator(), false) + .flatMap(page -> stream(page.spliterator(), false)) + .collect(toList()); + + assertThat(users.size(), is(2)); + assertThat(users.get(0).login(), is("octocat")); + assertThat(users.get(1).id(), is(2)); + } + @Test public void updateMembership() throws Exception { final MembershipCreate membershipCreateRequest = diff --git a/src/test/resources/com/spotify/github/v3/clients/list_members_page1.json b/src/test/resources/com/spotify/github/v3/clients/list_members_page1.json new file mode 100644 index 00000000..2da63ac2 --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/list_members_page1.json @@ -0,0 +1,22 @@ +[ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } +] \ No newline at end of file diff --git a/src/test/resources/com/spotify/github/v3/clients/list_members_page2.json b/src/test/resources/com/spotify/github/v3/clients/list_members_page2.json new file mode 100644 index 00000000..25fc4c6d --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/list_members_page2.json @@ -0,0 +1,22 @@ +[ + { + "login": "octocat2", + "id": 2, + "node_id": "MDQ2VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat2_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat2", + "html_url": "https://github.com/octocat2", + "followers_url": "https://api.github.com/users/octocat2/followers", + "following_url": "https://api.github.com/users/octoca2t/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat2/subscriptions", + "organizations_url": "https://api.github.com/users/octocat2/orgs", + "repos_url": "https://api.github.com/users/octocat2/repos", + "events_url": "https://api.github.com/users/octocat2/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat2/received_events", + "type": "User", + "site_admin": false + } +] \ No newline at end of file