From 87c36a0aeb324295676565ded44b864fb7fc4350 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Mon, 10 Jul 2023 15:46:37 +0100 Subject: [PATCH 01/14] feat: create a membership client --- .../github/v3/clients/MembershipClient.java | 78 +++++++++++++++++++ .../github/v3/clients/OrganisationClient.java | 10 +++ .../spotify/github/v3/orgs/Membership.java | 50 ++++++++++++ .../v3/clients/MembershipClientTest.java | 78 +++++++++++++++++++ .../spotify/github/v3/clients/membership.json | 5 ++ 5 files changed, 221 insertions(+) create mode 100644 src/main/java/com/spotify/github/v3/clients/MembershipClient.java create mode 100644 src/main/java/com/spotify/github/v3/orgs/Membership.java create mode 100644 src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java create mode 100644 src/test/resources/com/spotify/github/v3/clients/membership.json diff --git a/src/main/java/com/spotify/github/v3/clients/MembershipClient.java b/src/main/java/com/spotify/github/v3/clients/MembershipClient.java new file mode 100644 index 00000000..048f35d5 --- /dev/null +++ b/src/main/java/com/spotify/github/v3/clients/MembershipClient.java @@ -0,0 +1,78 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + +package com.spotify.github.v3.clients; + +import com.spotify.github.v3.orgs.Membership; +import java.lang.invoke.MethodHandles; +import java.util.concurrent.CompletableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MembershipClient { + + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String MEMBERS_TEMPLATE = "/orgs/%s/teams/%s/members"; + + private static final String MEMBERSHIP_TEMPLATE = "/orgs/%s/teams/%s/memberships/%s"; + + private static final String INVITATIONS_TEMPLATE = "/orgs/%s/teams/%s/invitations"; + + private final GitHubClient github; + + private final String org; + + private final String team; + + MembershipClient(final GitHubClient github, final String org, final String team) { + this.github = github; + this.org = org; + this.team = team; + } + + static MembershipClient create(final GitHubClient github, final String org, final String team) { + return new MembershipClient(github, org, team); + } + +// /** +// * Update a users membership within a team. +// * +// * @param request update membership request +// * @return membership +// */ +// public CompletableFuture createMember(final MemberCreate request) { +// final String path = String.format(TEAM_TEMPLATE, org); +// log.debug("Creating team in: " + path); +// return github.post(path, github.json().toJsonUnchecked(request), Team.class); +// } + + /** + * Get team membership of a user. + * + * @param username username of the team member + * @return membership + */ + public CompletableFuture getMembership(final String username) { + final String path = String.format(MEMBERSHIP_TEMPLATE, org, team, username); + log.debug("Fetching membership for: " + path); + return github.request(path, Membership.class); + } +} diff --git a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java index ea5f9197..3a4e535f 100644 --- a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java +++ b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java @@ -52,6 +52,16 @@ static OrganisationClient create(final GitHubClient github, final String org) { return new OrganisationClient(github, org); } + + /** + * Create a membership API client. + * + * @return membership API client + */ + public MembershipClient createMembershipClient(final GitHubClient github, final String org, final String team) { + return MembershipClient.create(github, org, team); + } + /** * Create a team in an organisation. * diff --git a/src/main/java/com/spotify/github/v3/orgs/Membership.java b/src/main/java/com/spotify/github/v3/orgs/Membership.java new file mode 100644 index 00000000..1ae8688e --- /dev/null +++ b/src/main/java/com/spotify/github/v3/orgs/Membership.java @@ -0,0 +1,50 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + +package com.spotify.github.v3.orgs; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.spotify.github.GithubStyle; +import java.net.URI; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** + * Membership resource represents data returned by a single Membership get operation. + */ +@Value.Immutable +@GithubStyle +@JsonSerialize(as = ImmutableMembership.class) +@JsonDeserialize(as = ImmutableMembership.class) +public interface Membership { + + /** URL */ + @Nullable + URI url(); + + /** ROLE */ + @Nullable + String role(); + + /** STATE */ + @Nullable + String state(); +} \ No newline at end of file diff --git a/src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java b/src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java new file mode 100644 index 00000000..0a23a565 --- /dev/null +++ b/src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java @@ -0,0 +1,78 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + + +package com.spotify.github.v3.clients; + +import static com.google.common.io.Resources.getResource; +import static java.nio.charset.Charset.defaultCharset; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.io.Resources; +import com.spotify.github.jackson.Json; +import com.spotify.github.v3.orgs.Membership; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import okhttp3.Headers; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Headers.class, ResponseBody.class, Response.class}) +public class MembershipClientTest { + + private GitHubClient github; + + private MembershipClient membershipClient; + + private Json json; + + private static String getFixture(String resource) throws IOException { + return Resources.toString(getResource(MembershipClientTest.class, resource), defaultCharset()); + } + + @Before + public void setUp() { + github = mock(GitHubClient.class); + membershipClient = new MembershipClient(github, "github", "1"); + json = Json.create(); + when(github.json()).thenReturn(json); + } + + @Test + public void getMembership() throws Exception { + final CompletableFuture fixture = + completedFuture(json.fromJson(getFixture("membership.json"), Membership.class)); + when(github.request("/orgs/github/teams/1/memberships/octocat", Membership.class)).thenReturn(fixture); + final Membership membership = membershipClient.getMembership("octocat").get(); + assertThat(membership.url().toString(), is("https://api.github.com/teams/1/memberships/octocat")); + assertThat(membership.role(), is("maintainer")); + assertThat(membership.state(), is("active")); + } +} diff --git a/src/test/resources/com/spotify/github/v3/clients/membership.json b/src/test/resources/com/spotify/github/v3/clients/membership.json new file mode 100644 index 00000000..fce6fa69 --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/membership.json @@ -0,0 +1,5 @@ +{ + "url": "https://api.github.com/teams/1/memberships/octocat", + "role": "maintainer", + "state": "active" +} \ No newline at end of file From 1b8dfc08a425b2fabb4bdc0f85ae58c15ca4cc5c Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Mon, 10 Jul 2023 17:10:21 +0100 Subject: [PATCH 02/14] feat: refactor into teamclient --- .../github/v3/clients/MembershipClient.java | 78 --------- .../github/v3/clients/OrganisationClient.java | 64 +------- .../spotify/github/v3/clients/TeamClient.java | 115 ++++++++++++++ .../v3/clients/MembershipClientTest.java | 78 --------- .../v3/clients/OrganisationClientTest.java | 64 -------- .../github/v3/clients/TeamClientTest.java | 149 ++++++++++++++++++ 6 files changed, 266 insertions(+), 282 deletions(-) delete mode 100644 src/main/java/com/spotify/github/v3/clients/MembershipClient.java create mode 100644 src/main/java/com/spotify/github/v3/clients/TeamClient.java delete mode 100644 src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java create mode 100644 src/test/java/com/spotify/github/v3/clients/TeamClientTest.java diff --git a/src/main/java/com/spotify/github/v3/clients/MembershipClient.java b/src/main/java/com/spotify/github/v3/clients/MembershipClient.java deleted file mode 100644 index 048f35d5..00000000 --- a/src/main/java/com/spotify/github/v3/clients/MembershipClient.java +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * -\-\- - * github-api - * -- - * Copyright (C) 2016 - 2020 Spotify AB - * -- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -/-/- - */ - -package com.spotify.github.v3.clients; - -import com.spotify.github.v3.orgs.Membership; -import java.lang.invoke.MethodHandles; -import java.util.concurrent.CompletableFuture; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MembershipClient { - - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private static final String MEMBERS_TEMPLATE = "/orgs/%s/teams/%s/members"; - - private static final String MEMBERSHIP_TEMPLATE = "/orgs/%s/teams/%s/memberships/%s"; - - private static final String INVITATIONS_TEMPLATE = "/orgs/%s/teams/%s/invitations"; - - private final GitHubClient github; - - private final String org; - - private final String team; - - MembershipClient(final GitHubClient github, final String org, final String team) { - this.github = github; - this.org = org; - this.team = team; - } - - static MembershipClient create(final GitHubClient github, final String org, final String team) { - return new MembershipClient(github, org, team); - } - -// /** -// * Update a users membership within a team. -// * -// * @param request update membership request -// * @return membership -// */ -// public CompletableFuture createMember(final MemberCreate request) { -// final String path = String.format(TEAM_TEMPLATE, org); -// log.debug("Creating team in: " + path); -// return github.post(path, github.json().toJsonUnchecked(request), Team.class); -// } - - /** - * Get team membership of a user. - * - * @param username username of the team member - * @return membership - */ - public CompletableFuture getMembership(final String username) { - final String path = String.format(MEMBERSHIP_TEMPLATE, org, team, username); - log.debug("Fetching membership for: " + path); - return github.request(path, Membership.class); - } -} diff --git a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java index 3a4e535f..38981ffb 100644 --- a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java +++ b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java @@ -58,67 +58,7 @@ static OrganisationClient create(final GitHubClient github, final String org) { * * @return membership API client */ - public MembershipClient createMembershipClient(final GitHubClient github, final String org, final String team) { - return MembershipClient.create(github, org, team); - } - - /** - * Create a team in an organisation. - * - * @param request create team request - * @return team - */ - public CompletableFuture createTeam(final TeamCreate request) { - final String path = String.format(TEAM_TEMPLATE, org); - log.debug("Creating team in: " + path); - return github.post(path, github.json().toJsonUnchecked(request), Team.class); - } - - /** - * Get a specific team in an organisation. - * - * @param slug slug of the team name - * @return team - */ - public CompletableFuture getTeam(final String slug) { - final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug); - log.debug("Fetching team from " + path); - return github.request(path, Team.class); - } - - /** - * List teams within an organisation. - * - * @return list of all teams in an organisation - */ - public CompletableFuture> listTeams() { - final String path = String.format(TEAM_TEMPLATE, org); - log.debug("Fetching teams from " + path); - return github.request(path, LIST_TEAMS); - } - - /** - * Update a team in an organisation. - * - * @param request update team request - * @param slug slug of the team name - * @return team - */ - public CompletableFuture updateTeam(final TeamCreate request, final String slug) { - final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug); - log.debug("Updating team in: " + path); - return github.patch(path, github.json().toJsonUnchecked(request), Team.class); - } - - /** - * Delete a specific team in an organisation. - * - * @param slug slug of the team name - * @return team - */ - public CompletableFuture deleteTeam(final String slug) { - final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug); - log.debug("Deleting team from: " + path); - return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER); + public TeamClient createTeamClient(final GitHubClient github, final String org) { + return TeamClient.create(github, org); } } diff --git a/src/main/java/com/spotify/github/v3/clients/TeamClient.java b/src/main/java/com/spotify/github/v3/clients/TeamClient.java new file mode 100644 index 00000000..96a8b112 --- /dev/null +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -0,0 +1,115 @@ +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_TEAMS; + +import com.spotify.github.v3.Team; +import com.spotify.github.v3.orgs.Membership; +import com.spotify.github.v3.orgs.requests.TeamCreate; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TeamClient { + + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String TEAM_TEMPLATE = "/orgs/%s/teams"; + + private static final String TEAM_SLUG_TEMPLATE = "/orgs/%s/teams/%s"; + + private static final String MEMBERS_TEMPLATE = "/orgs/%s/teams/%s/members"; + + private static final String MEMBERSHIP_TEMPLATE = "/orgs/%s/teams/%s/memberships/%s"; + + private static final String INVITATIONS_TEMPLATE = "/orgs/%s/teams/%s/invitations"; + + private final GitHubClient github; + + private final String org; + + TeamClient(final GitHubClient github, final String org) { + this.github = github; + this.org = org; + } + + static TeamClient create(final GitHubClient github, final String org) { + return new TeamClient(github, org); + } + + + /** + * Create a team in an organisation. + * + * @param request create team request + * @return team + */ + public CompletableFuture createTeam(final TeamCreate request) { + final String path = String.format(TEAM_TEMPLATE, org); + log.debug("Creating team in: " + path); + return github.post(path, github.json().toJsonUnchecked(request), Team.class); + } + + /** + * Get a specific team in an organisation. + * + * @param slug slug of the team name + * @return team + */ + public CompletableFuture getTeam(final String slug) { + final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug); + log.debug("Fetching team from " + path); + return github.request(path, Team.class); + } + + /** + * List teams within an organisation. + * + * @return list of all teams in an organisation + */ + public CompletableFuture> listTeams() { + final String path = String.format(TEAM_TEMPLATE, org); + log.debug("Fetching teams from " + path); + return github.request(path, LIST_TEAMS); + } + + /** + * Update a team in an organisation. + * + * @param request update team request + * @param slug slug of the team name + * @return team + */ + public CompletableFuture updateTeam(final TeamCreate request, final String slug) { + final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug); + log.debug("Updating team in: " + path); + return github.patch(path, github.json().toJsonUnchecked(request), Team.class); + } + + /** + * Delete a specific team in an organisation. + * + * @param slug slug of the team name + * @return team + */ + public CompletableFuture deleteTeam(final String slug) { + final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug); + log.debug("Deleting team from: " + path); + return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER); + } + + /** + * Get team membership of a user. + * + * @param slug the team slug + * @param username username of the team member + * @return membership + */ + public CompletableFuture getMembership(final String slug, final String username) { + final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username); + log.debug("Fetching membership for: " + path); + return github.request(path, Membership.class); + } +} diff --git a/src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java b/src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java deleted file mode 100644 index 0a23a565..00000000 --- a/src/test/java/com/spotify/github/v3/clients/MembershipClientTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * -\-\- - * github-api - * -- - * Copyright (C) 2016 - 2020 Spotify AB - * -- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -/-/- - */ - - -package com.spotify.github.v3.clients; - -import static com.google.common.io.Resources.getResource; -import static java.nio.charset.Charset.defaultCharset; -import static java.util.concurrent.CompletableFuture.completedFuture; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.common.io.Resources; -import com.spotify.github.jackson.Json; -import com.spotify.github.v3.orgs.Membership; -import java.io.IOException; -import java.util.concurrent.CompletableFuture; -import okhttp3.Headers; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({ Headers.class, ResponseBody.class, Response.class}) -public class MembershipClientTest { - - private GitHubClient github; - - private MembershipClient membershipClient; - - private Json json; - - private static String getFixture(String resource) throws IOException { - return Resources.toString(getResource(MembershipClientTest.class, resource), defaultCharset()); - } - - @Before - public void setUp() { - github = mock(GitHubClient.class); - membershipClient = new MembershipClient(github, "github", "1"); - json = Json.create(); - when(github.json()).thenReturn(json); - } - - @Test - public void getMembership() throws Exception { - final CompletableFuture fixture = - completedFuture(json.fromJson(getFixture("membership.json"), Membership.class)); - when(github.request("/orgs/github/teams/1/memberships/octocat", Membership.class)).thenReturn(fixture); - final Membership membership = membershipClient.getMembership("octocat").get(); - assertThat(membership.url().toString(), is("https://api.github.com/teams/1/memberships/octocat")); - assertThat(membership.role(), is("maintainer")); - assertThat(membership.state(), is("active")); - } -} diff --git a/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java b/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java index 6779179e..2fabc1b9 100644 --- a/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java @@ -69,68 +69,4 @@ public void setUp() { json = Json.create(); when(github.json()).thenReturn(json); } - - @Test - public void getTeam() throws Exception { - final CompletableFuture fixture = - completedFuture(json.fromJson(getFixture("team_get.json"), Team.class)); - when(github.request("/orgs/github/teams/justice-league", Team.class)).thenReturn(fixture); - final Team team = organisationClient.getTeam("justice-league").get(); - assertThat(team.id(), is(1)); - assertThat(team.name(), is("Justice League")); - } - - @Test - public void listTeams() throws Exception { - final CompletableFuture> fixture = - completedFuture(json.fromJson(getFixture("teams_list.json"), LIST_TEAMS)); - when(github.request("/orgs/github/teams", LIST_TEAMS)).thenReturn(fixture); - final List teams = organisationClient.listTeams().get(); - assertThat(teams.get(0).slug(), is("justice-league")); - assertThat(teams.get(1).slug(), is("x-men")); - assertThat(teams.size(), is(2)); - } - - @Test - public void deleteTeam() throws Exception { - final CompletableFuture response = completedFuture(mock(Response.class)); - final ArgumentCaptor capture = ArgumentCaptor.forClass(String.class); - when(github.delete(capture.capture())).thenReturn(response); - - CompletableFuture deleteResponse = organisationClient.deleteTeam("justice-league"); - deleteResponse.get(); - assertThat(capture.getValue(), is("/orgs/github/teams/justice-league")); - } - - @Test - public void createTeam() throws Exception { - final TeamCreate teamCreateRequest = - json.fromJson( - getFixture("teams_request.json"), - TeamCreate.class); - - final CompletableFuture fixtureResponse = completedFuture(json.fromJson( - getFixture("team_get.json"), - Team.class)); - when(github.post(any(), any(), eq(Team.class))).thenReturn(fixtureResponse); - final CompletableFuture actualResponse = organisationClient.createTeam(teamCreateRequest); - - assertThat(actualResponse.get().name(), is("Justice League")); - } - - @Test - public void updateTeam() throws Exception { - final TeamCreate teamCreateRequest = - json.fromJson( - getFixture("teams_patch.json"), - TeamCreate.class); - - final CompletableFuture fixtureResponse = completedFuture(json.fromJson( - getFixture("teams_patch_response.json"), - Team.class)); - when(github.patch(any(), any(), eq(Team.class))).thenReturn(fixtureResponse); - final CompletableFuture actualResponse = organisationClient.updateTeam(teamCreateRequest, "justice-league"); - - assertThat(actualResponse.get().name(), is("Justice League2")); - } } diff --git a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java new file mode 100644 index 00000000..5b05c688 --- /dev/null +++ b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java @@ -0,0 +1,149 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + + +package com.spotify.github.v3.clients; + +import static com.google.common.io.Resources.getResource; +import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS; +import static java.nio.charset.Charset.defaultCharset; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.io.Resources; +import com.spotify.github.jackson.Json; +import com.spotify.github.v3.Team; +import com.spotify.github.v3.orgs.Membership; +import com.spotify.github.v3.orgs.requests.TeamCreate; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import okhttp3.Headers; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Headers.class, ResponseBody.class, Response.class}) +public class TeamClientTest { + + private GitHubClient github; + + private TeamClient teamClient; + + private Json json; + + private static String getFixture(String resource) throws IOException { + return Resources.toString(getResource(TeamClientTest.class, resource), defaultCharset()); + } + + @Before + public void setUp() { + github = mock(GitHubClient.class); + teamClient = new TeamClient(github, "github"); + json = Json.create(); + when(github.json()).thenReturn(json); + } + + @Test + public void getTeam() throws Exception { + final CompletableFuture fixture = + completedFuture(json.fromJson(getFixture("team_get.json"), Team.class)); + when(github.request("/orgs/github/teams/justice-league", Team.class)).thenReturn(fixture); + final Team team = teamClient.getTeam("justice-league").get(); + assertThat(team.id(), is(1)); + assertThat(team.name(), is("Justice League")); + } + + @Test + public void listTeams() throws Exception { + final CompletableFuture> fixture = + completedFuture(json.fromJson(getFixture("teams_list.json"), LIST_TEAMS)); + when(github.request("/orgs/github/teams", LIST_TEAMS)).thenReturn(fixture); + final List teams = teamClient.listTeams().get(); + assertThat(teams.get(0).slug(), is("justice-league")); + assertThat(teams.get(1).slug(), is("x-men")); + assertThat(teams.size(), is(2)); + } + + @Test + public void deleteTeam() throws Exception { + final CompletableFuture response = completedFuture(mock(Response.class)); + final ArgumentCaptor capture = ArgumentCaptor.forClass(String.class); + when(github.delete(capture.capture())).thenReturn(response); + + CompletableFuture deleteResponse = teamClient.deleteTeam("justice-league"); + deleteResponse.get(); + assertThat(capture.getValue(), is("/orgs/github/teams/justice-league")); + } + + @Test + public void createTeam() throws Exception { + final TeamCreate teamCreateRequest = + json.fromJson( + getFixture("teams_request.json"), + TeamCreate.class); + + final CompletableFuture fixtureResponse = completedFuture(json.fromJson( + getFixture("team_get.json"), + Team.class)); + when(github.post(any(), any(), eq(Team.class))).thenReturn(fixtureResponse); + final CompletableFuture actualResponse = teamClient.createTeam(teamCreateRequest); + + assertThat(actualResponse.get().name(), is("Justice League")); + } + + @Test + public void updateTeam() throws Exception { + final TeamCreate teamCreateRequest = + json.fromJson( + getFixture("teams_patch.json"), + TeamCreate.class); + + final CompletableFuture fixtureResponse = completedFuture(json.fromJson( + getFixture("teams_patch_response.json"), + Team.class)); + when(github.patch(any(), any(), eq(Team.class))).thenReturn(fixtureResponse); + final CompletableFuture actualResponse = teamClient.updateTeam(teamCreateRequest, "justice-league"); + + assertThat(actualResponse.get().name(), is("Justice League2")); + } + + @Test + public void getMembership() throws Exception { + final CompletableFuture fixture = + completedFuture(json.fromJson(getFixture("membership.json"), Membership.class)); + when(github.request("/orgs/github/teams/1/memberships/octocat", Membership.class)).thenReturn(fixture); + final Membership membership = teamClient.getMembership("1", "octocat").get(); + assertThat(membership.url().toString(), is("https://api.github.com/teams/1/memberships/octocat")); + assertThat(membership.role(), is("maintainer")); + assertThat(membership.state(), is("active")); + } +} From 490aeb30dfbc0a927f259f95c15476073ec903c8 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Mon, 10 Jul 2023 17:48:23 +0100 Subject: [PATCH 03/14] feat: add ability to list team members --- .../github/v3/clients/GitHubClient.java | 4 ++ .../spotify/github/v3/clients/TeamClient.java | 17 +++++++- .../github/v3/clients/TeamClientTest.java | 15 ++++++- .../github/v3/clients/list_members.json | 42 +++++++++++++++++++ 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/com/spotify/github/v3/clients/list_members.json diff --git a/src/main/java/com/spotify/github/v3/clients/GitHubClient.java b/src/main/java/com/spotify/github/v3/clients/GitHubClient.java index 0bd9283a..03b094b3 100644 --- a/src/main/java/com/spotify/github/v3/clients/GitHubClient.java +++ b/src/main/java/com/spotify/github/v3/clients/GitHubClient.java @@ -27,6 +27,7 @@ import com.spotify.github.Tracer; import com.spotify.github.jackson.Json; import com.spotify.github.v3.Team; +import com.spotify.github.v3.User; import com.spotify.github.v3.checks.AccessToken; import com.spotify.github.v3.comment.Comment; import com.spotify.github.v3.exceptions.ReadOnlyRepositoryException; @@ -101,6 +102,9 @@ public class GitHubClient { static final TypeReference> LIST_TEAMS = new TypeReference<>() {}; + static final TypeReference> LIST_TEAM_MEMBERS = + new TypeReference<>() {}; + private static final String GET_ACCESS_TOKEN_URL = "app/installations/%s/access_tokens"; private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 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 96a8b112..748b946e 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -2,11 +2,14 @@ import static com.spotify.github.v3.clients.GitHubClient.IGNORE_RESPONSE_CONSUMER; import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS; +import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAM_MEMBERS; import com.spotify.github.v3.Team; +import com.spotify.github.v3.User; import com.spotify.github.v3.orgs.Membership; import com.spotify.github.v3.orgs.requests.TeamCreate; import java.lang.invoke.MethodHandles; +import java.lang.reflect.Member; import java.util.List; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; @@ -107,9 +110,21 @@ public CompletableFuture deleteTeam(final String slug) { * @param username username of the team member * @return membership */ - public CompletableFuture getMembership(final String slug, final String username) { + public CompletableFuture getTeamMembership(final String slug, final String username) { final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username); log.debug("Fetching membership for: " + path); return github.request(path, Membership.class); } + + /** + * List team members. + * + * @param slug the team slug + * @return list of all members in a team + */ + public CompletableFuture> listTeamMembers(final String slug) { + final String path = String.format(MEMBERS_TEMPLATE, org, slug); + log.debug("Fetching members for: " + path); + return github.request(path, LIST_TEAM_MEMBERS); + } } 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 5b05c688..35de15d4 100644 --- a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java @@ -23,6 +23,7 @@ import static com.google.common.io.Resources.getResource; import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS; +import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAM_MEMBERS; import static java.nio.charset.Charset.defaultCharset; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.hamcrest.MatcherAssert.assertThat; @@ -35,6 +36,7 @@ import com.google.common.io.Resources; import com.spotify.github.jackson.Json; import com.spotify.github.v3.Team; +import com.spotify.github.v3.User; import com.spotify.github.v3.orgs.Membership; import com.spotify.github.v3.orgs.requests.TeamCreate; import java.io.IOException; @@ -141,9 +143,20 @@ public void getMembership() throws Exception { final CompletableFuture fixture = completedFuture(json.fromJson(getFixture("membership.json"), Membership.class)); when(github.request("/orgs/github/teams/1/memberships/octocat", Membership.class)).thenReturn(fixture); - final Membership membership = teamClient.getMembership("1", "octocat").get(); + final Membership membership = teamClient.getTeamMembership("1", "octocat").get(); assertThat(membership.url().toString(), is("https://api.github.com/teams/1/memberships/octocat")); assertThat(membership.role(), is("maintainer")); assertThat(membership.state(), is("active")); } + + @Test + public void listTeamMembers() throws Exception { + final CompletableFuture> fixture = + completedFuture(json.fromJson(getFixture("list_members.json"), LIST_TEAM_MEMBERS)); + when(github.request("/orgs/github/teams/1/members", LIST_TEAM_MEMBERS)).thenReturn(fixture); + final List teamMembers = teamClient.listTeamMembers("1").get(); + assertThat(teamMembers.get(0).login(), is("octocat")); + assertThat(teamMembers.get(1).id(), is(2)); + assertThat(teamMembers.size(), is(2)); + } } diff --git a/src/test/resources/com/spotify/github/v3/clients/list_members.json b/src/test/resources/com/spotify/github/v3/clients/list_members.json new file mode 100644 index 00000000..f8563088 --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/list_members.json @@ -0,0 +1,42 @@ +[ + { + "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 + }, + { + "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 From 75e342065d1b494a4b33d9c8afe98b89c2961dd1 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 09:12:19 +0100 Subject: [PATCH 04/14] docs: update readme to address nested APIs --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d7b15091..a7980434 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,10 @@ final RepositoryClient repositoryClient = githubClient.createRepositoryClient("m log.info(repositoryClient.getCommit("sha").get().htmlUrl()); ``` -Some APIs, such as Checks API are nested in the Repository API. Endpoints such as `POST /repos/:owner/:repo/check-runs` live in the ChecksClient: +Another way the structure is mirrored is that some of the APIs are nested under a parent API. +For example: +Endpoints related to check runs or issues are nested under the Repository client: ```java final ChecksClient checksClient = repositoryClient.createChecksApiClient(); checksClient.createCheckRun(CHECK_RUN_REQUEST); @@ -76,8 +78,15 @@ checksClient.createCheckRun(CHECK_RUN_REQUEST); final IssueClient issueClient = repositoryClient.createIssueClient(); issueClient.createComment(ISSUE_ID, "comment body") .thenAccept(comment -> log.info("created comment " + comment.htmlUrl())); + ``` +Endpoints related to teams and memberships are nested under the Organisation client: +```java +final TeamClient teamClient = organisationClient.createTeamClient(); + teamClient.getMembership("username"); +``` + ## Contributing This project uses Maven. To run the tests locally, just run: From c0e39e9352cf5e18fbbf9110ee43a9556bc45a73 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 13:31:00 +0100 Subject: [PATCH 05/14] feat: add update membership functionality --- .../spotify/github/v3/clients/TeamClient.java | 13 ++++++ .../v3/orgs/requests/MembershipCreate.java | 42 +++++++++++++++++++ .../github/v3/clients/TeamClientTest.java | 17 ++++++++ .../github/v3/clients/membership_update.json | 3 ++ .../clients/membership_update_response.json | 5 +++ 5 files changed, 80 insertions(+) create mode 100644 src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java create mode 100644 src/test/resources/com/spotify/github/v3/clients/membership_update.json create mode 100644 src/test/resources/com/spotify/github/v3/clients/membership_update_response.json 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 748b946e..7d458828 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -7,6 +7,7 @@ import com.spotify.github.v3.Team; import com.spotify.github.v3.User; import com.spotify.github.v3.orgs.Membership; +import com.spotify.github.v3.orgs.requests.MembershipCreate; import com.spotify.github.v3.orgs.requests.TeamCreate; import java.lang.invoke.MethodHandles; import java.lang.reflect.Member; @@ -103,6 +104,18 @@ public CompletableFuture deleteTeam(final String slug) { return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER); } + /** + * Add or update team membership for a user + * + * @param request update membership request + * @return membership + */ + public CompletableFuture updateMembership(final MembershipCreate request, final String slug, final String username) { + final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username); + log.debug("Updating membership in: " + path); + return github.put(path, github.json().toJsonUnchecked(request), Membership.class); + } + /** * Get team membership of a user. * diff --git a/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java b/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java new file mode 100644 index 00000000..d17486e4 --- /dev/null +++ b/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java @@ -0,0 +1,42 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + +package com.spotify.github.v3.orgs.requests; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.spotify.github.GithubStyle; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** Request to create a team within a given organisation */ +@Value.Immutable +@GithubStyle +@JsonSerialize(as = ImmutableMembershipCreate.class) +@JsonDeserialize(as = ImmutableMembershipCreate.class) +public interface MembershipCreate { + + /** + * The role that this user should have in the team. + * Defaults to 'member' + */ + @Nullable + String role(); +} \ No newline at end of file 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 35de15d4..c19ab6e5 100644 --- a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java @@ -38,6 +38,7 @@ import com.spotify.github.v3.Team; import com.spotify.github.v3.User; import com.spotify.github.v3.orgs.Membership; +import com.spotify.github.v3.orgs.requests.MembershipCreate; import com.spotify.github.v3.orgs.requests.TeamCreate; import java.io.IOException; import java.util.List; @@ -159,4 +160,20 @@ public void listTeamMembers() throws Exception { assertThat(teamMembers.get(1).id(), is(2)); assertThat(teamMembers.size(), is(2)); } + + @Test + public void updateMembership() throws Exception { + final MembershipCreate membershipCreateRequest = + json.fromJson( + getFixture("membership_update.json"), + MembershipCreate.class); + + final CompletableFuture fixtureResponse = completedFuture(json.fromJson( + getFixture("membership_update_response.json"), + Membership.class)); + when(github.put(any(), any(), eq(Membership.class))).thenReturn(fixtureResponse); + final CompletableFuture actualResponse = teamClient.updateMembership(membershipCreateRequest, "1", "octocat"); + + assertThat(actualResponse.get().role(), is("member")); + } } diff --git a/src/test/resources/com/spotify/github/v3/clients/membership_update.json b/src/test/resources/com/spotify/github/v3/clients/membership_update.json new file mode 100644 index 00000000..0efabc38 --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/membership_update.json @@ -0,0 +1,3 @@ +{ + "role": "member" +} \ No newline at end of file diff --git a/src/test/resources/com/spotify/github/v3/clients/membership_update_response.json b/src/test/resources/com/spotify/github/v3/clients/membership_update_response.json new file mode 100644 index 00000000..0f2fd957 --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/membership_update_response.json @@ -0,0 +1,5 @@ +{ + "url": "https://api.github.com/teams/1/memberships/octocat", + "role": "member", + "state": "active" +} \ No newline at end of file From 7b6a00b9728d1a13de27930c842f6310f0cd1617 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 14:27:22 +0100 Subject: [PATCH 06/14] feat: add delete membership functionality --- .../com/spotify/github/v3/clients/TeamClient.java | 14 +++++++++++++- .../spotify/github/v3/clients/TeamClientTest.java | 13 ++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) 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 7d458828..7cc8ea95 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -123,7 +123,7 @@ public CompletableFuture updateMembership(final MembershipCreate req * @param username username of the team member * @return membership */ - public CompletableFuture getTeamMembership(final String slug, final String username) { + public CompletableFuture getMembership(final String slug, final String username) { final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username); log.debug("Fetching membership for: " + path); return github.request(path, Membership.class); @@ -140,4 +140,16 @@ public CompletableFuture> listTeamMembers(final String slug) { log.debug("Fetching members for: " + path); return github.request(path, LIST_TEAM_MEMBERS); } + + /** + * Delete a specific team in an organisation. + * + * @param slug slug of the team name + * @return team + */ + public CompletableFuture deleteMembership(final String slug, final String username) { + final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username); + log.debug("Deleting membership from: " + path); + return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER); + } } 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 c19ab6e5..0d307f0b 100644 --- a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java @@ -144,7 +144,7 @@ public void getMembership() throws Exception { final CompletableFuture fixture = completedFuture(json.fromJson(getFixture("membership.json"), Membership.class)); when(github.request("/orgs/github/teams/1/memberships/octocat", Membership.class)).thenReturn(fixture); - final Membership membership = teamClient.getTeamMembership("1", "octocat").get(); + final Membership membership = teamClient.getMembership("1", "octocat").get(); assertThat(membership.url().toString(), is("https://api.github.com/teams/1/memberships/octocat")); assertThat(membership.role(), is("maintainer")); assertThat(membership.state(), is("active")); @@ -176,4 +176,15 @@ public void updateMembership() throws Exception { assertThat(actualResponse.get().role(), is("member")); } + + @Test + public void deleteMembership() throws Exception { + final CompletableFuture response = completedFuture(mock(Response.class)); + final ArgumentCaptor capture = ArgumentCaptor.forClass(String.class); + when(github.delete(capture.capture())).thenReturn(response); + + CompletableFuture deleteResponse = teamClient.deleteMembership("1", "octocat"); + deleteResponse.get(); + assertThat(capture.getValue(), is("/orgs/github/teams/1/memberships/octocat")); + } } From 341d5f2137a484bf29410818303040836ca1996f Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 15:55:24 +0100 Subject: [PATCH 07/14] feat: add list pending team invitations logic --- .../github/v3/clients/GitHubClient.java | 4 + .../spotify/github/v3/clients/TeamClient.java | 16 +++- .../github/v3/orgs/TeamInvitation.java | 77 +++++++++++++++++++ .../github/v3/clients/TeamClientTest.java | 13 ++++ .../v3/clients/list_team_invitations.json | 68 ++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java create mode 100644 src/test/resources/com/spotify/github/v3/clients/list_team_invitations.json diff --git a/src/main/java/com/spotify/github/v3/clients/GitHubClient.java b/src/main/java/com/spotify/github/v3/clients/GitHubClient.java index 03b094b3..28ac3eb2 100644 --- a/src/main/java/com/spotify/github/v3/clients/GitHubClient.java +++ b/src/main/java/com/spotify/github/v3/clients/GitHubClient.java @@ -33,6 +33,7 @@ import com.spotify.github.v3.exceptions.ReadOnlyRepositoryException; import com.spotify.github.v3.exceptions.RequestNotOkException; import com.spotify.github.v3.git.Reference; +import com.spotify.github.v3.orgs.TeamInvitation; import com.spotify.github.v3.prs.PullRequestItem; import com.spotify.github.v3.prs.Review; import com.spotify.github.v3.prs.ReviewRequests; @@ -105,6 +106,9 @@ public class GitHubClient { static final TypeReference> LIST_TEAM_MEMBERS = new TypeReference<>() {}; + static final TypeReference> LIST_PENDING_TEAM_INVITATIONS = + new TypeReference<>() {}; + private static final String GET_ACCESS_TOKEN_URL = "app/installations/%s/access_tokens"; private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 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 7cc8ea95..5c27a3c4 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -1,12 +1,14 @@ 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 com.spotify.github.v3.Team; import com.spotify.github.v3.User; 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 java.lang.invoke.MethodHandles; @@ -142,7 +144,7 @@ public CompletableFuture> listTeamMembers(final String slug) { } /** - * Delete a specific team in an organisation. + * Delete a membership for a specific user. * * @param slug slug of the team name * @return team @@ -152,4 +154,16 @@ public CompletableFuture deleteMembership(final String slug, final String log.debug("Deleting membership from: " + path); return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER); } + + /** + * List pending invitations for a team. + * + * @param slug the team slug + * @return list of all members in a team + */ + public CompletableFuture> listPendingTeamInvitations(final String slug) { + final String path = String.format(INVITATIONS_TEMPLATE, org, slug); + log.debug("Fetching pending invitations for: " + path); + return github.request(path, LIST_PENDING_TEAM_INVITATIONS); + } } diff --git a/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java b/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java new file mode 100644 index 00000000..ea705bc8 --- /dev/null +++ b/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java @@ -0,0 +1,77 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + +package com.spotify.github.v3.orgs; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.spotify.github.GithubStyle; +import com.spotify.github.v3.User; +import java.net.URI; +import java.util.Map; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +@Value.Immutable +@GithubStyle +@JsonSerialize(as = ImmutableTeamInvitation.class) +@JsonDeserialize(as = ImmutableTeamInvitation.class) +public interface TeamInvitation { + /** ID */ + @Nullable + Integer id(); + + /** login username */ + @Nullable + String login(); + + /** Node ID */ + @Nullable + String nodeId(); + + /** Email address */ + @Nullable + String email(); + + /** Role */ + @Nullable + String role(); + + /** Failed reason */ + @Nullable + String failedReason(); + + /** Inviter */ + + @Nullable + User inviter(); + + /** Team Count */ + @Nullable + Integer teamCount(); + + /** Invitation Teams URL */ + @Nullable + URI invitationTeamsUrl(); + + /** Invitation Source */ + @Nullable + String invitationSource(); +} 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 0d307f0b..bb9bcf9c 100644 --- a/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/TeamClientTest.java @@ -22,6 +22,7 @@ package com.spotify.github.v3.clients; import static com.google.common.io.Resources.getResource; +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 java.nio.charset.Charset.defaultCharset; @@ -38,6 +39,7 @@ import com.spotify.github.v3.Team; import com.spotify.github.v3.User; 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 java.io.IOException; @@ -187,4 +189,15 @@ public void deleteMembership() throws Exception { deleteResponse.get(); assertThat(capture.getValue(), is("/orgs/github/teams/1/memberships/octocat")); } + + @Test + public void listPendingTeamInvitations() throws Exception { + final CompletableFuture> fixture = + completedFuture(json.fromJson(getFixture("list_team_invitations.json"), LIST_PENDING_TEAM_INVITATIONS)); + when(github.request("/orgs/github/teams/1/invitations", LIST_PENDING_TEAM_INVITATIONS)).thenReturn(fixture); + final List pendingInvitations = teamClient.listPendingTeamInvitations("1").get(); + assertThat(pendingInvitations.get(0).login(), is("octocat")); + assertThat(pendingInvitations.get(1).id(), is(2)); + assertThat(pendingInvitations.size(), is(2)); + } } diff --git a/src/test/resources/com/spotify/github/v3/clients/list_team_invitations.json b/src/test/resources/com/spotify/github/v3/clients/list_team_invitations.json new file mode 100644 index 00000000..768256c7 --- /dev/null +++ b/src/test/resources/com/spotify/github/v3/clients/list_team_invitations.json @@ -0,0 +1,68 @@ +[ + { + "id": 1, + "login": "octocat", + "node_id": "MDQ6VXNlcjE=", + "email": "octocat@github.com", + "role": "direct_member", + "created_at": "2016-11-30T06:46:10-08:00", + "failed_at": "", + "failed_reason": "", + "inviter": { + "login": "other_user", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/other_user_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/other_user", + "html_url": "https://github.com/other_user", + "followers_url": "https://api.github.com/users/other_user/followers", + "following_url": "https://api.github.com/users/other_user/following{/other_user}", + "gists_url": "https://api.github.com/users/other_user/gists{/gist_id}", + "starred_url": "https://api.github.com/users/other_user/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", + "organizations_url": "https://api.github.com/users/other_user/orgs", + "repos_url": "https://api.github.com/users/other_user/repos", + "events_url": "https://api.github.com/users/other_user/events{/privacy}", + "received_events_url": "https://api.github.com/users/other_user/received_events", + "type": "User", + "site_admin": false + }, + "team_count": 2, + "invitation_teams_url": "https://api.github.com/organizations/2/invitations/1/teams", + "invitation_source": "member" + }, + { + "id": 2, + "login": "monalisa", + "node_id": "MDQ3VXNlcjE=", + "email": "octocat2@github.com", + "role": "direct_member", + "created_at": "2016-11-30T06:46:10-08:00", + "failed_at": "", + "failed_reason": "", + "inviter": { + "login": "other_user", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/other_user_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/other_user", + "html_url": "https://github.com/other_user", + "followers_url": "https://api.github.com/users/other_user/followers", + "following_url": "https://api.github.com/users/other_user/following{/other_user}", + "gists_url": "https://api.github.com/users/other_user/gists{/gist_id}", + "starred_url": "https://api.github.com/users/other_user/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", + "organizations_url": "https://api.github.com/users/other_user/orgs", + "repos_url": "https://api.github.com/users/other_user/repos", + "events_url": "https://api.github.com/users/other_user/events{/privacy}", + "received_events_url": "https://api.github.com/users/other_user/received_events", + "type": "User", + "site_admin": false + }, + "team_count": 2, + "invitation_teams_url": "https://api.github.com/organizations/2/invitations/1/teams", + "invitation_source": "member" + } +] \ No newline at end of file From f39960aafd35f57d0c6047f1248529bb15e31843 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 16:06:29 +0100 Subject: [PATCH 08/14] chore: cleanup --- .../github/v3/clients/OrganisationClient.java | 7 ------ .../spotify/github/v3/clients/TeamClient.java | 22 ++++++++++++++++++- .../spotify/github/v3/orgs/Membership.java | 2 +- .../github/v3/orgs/TeamInvitation.java | 1 - .../v3/orgs/requests/MembershipCreate.java | 2 +- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java index 38981ffb..e5933e16 100644 --- a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java +++ b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java @@ -20,14 +20,7 @@ // 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_TEAMS; - -import com.spotify.github.v3.Team; -import com.spotify.github.v3.orgs.requests.TeamCreate; import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 5c27a3c4..10694383 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -1,3 +1,24 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + + package com.spotify.github.v3.clients; import static com.spotify.github.v3.clients.GitHubClient.IGNORE_RESPONSE_CONSUMER; @@ -12,7 +33,6 @@ import com.spotify.github.v3.orgs.requests.MembershipCreate; import com.spotify.github.v3.orgs.requests.TeamCreate; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Member; import java.util.List; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; diff --git a/src/main/java/com/spotify/github/v3/orgs/Membership.java b/src/main/java/com/spotify/github/v3/orgs/Membership.java index 1ae8688e..17c190c1 100644 --- a/src/main/java/com/spotify/github/v3/orgs/Membership.java +++ b/src/main/java/com/spotify/github/v3/orgs/Membership.java @@ -47,4 +47,4 @@ public interface Membership { /** STATE */ @Nullable String state(); -} \ No newline at end of file +} diff --git a/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java b/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java index ea705bc8..a58c12b6 100644 --- a/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java +++ b/src/main/java/com/spotify/github/v3/orgs/TeamInvitation.java @@ -25,7 +25,6 @@ import com.spotify.github.GithubStyle; import com.spotify.github.v3.User; import java.net.URI; -import java.util.Map; import javax.annotation.Nullable; import org.immutables.value.Value; diff --git a/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java b/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java index d17486e4..cc6fc100 100644 --- a/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java +++ b/src/main/java/com/spotify/github/v3/orgs/requests/MembershipCreate.java @@ -39,4 +39,4 @@ public interface MembershipCreate { */ @Nullable String role(); -} \ No newline at end of file +} From 26519a0588350fe2a1b002400bc4e55391f284cb Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 16:40:06 +0100 Subject: [PATCH 09/14] feat: cleanup --- .../v3/clients/OrganisationClientTest.java | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java diff --git a/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java b/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java deleted file mode 100644 index 2fabc1b9..00000000 --- a/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * -\-\- - * github-api - * -- - * Copyright (C) 2016 - 2020 Spotify AB - * -- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -/-/- - */ - -package com.spotify.github.v3.clients; - -import static com.google.common.io.Resources.getResource; -import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS; -import static java.nio.charset.Charset.defaultCharset; -import static java.util.concurrent.CompletableFuture.completedFuture; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.common.io.Resources; -import com.spotify.github.jackson.Json; -import com.spotify.github.v3.Team; -import com.spotify.github.v3.orgs.requests.TeamCreate; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import okhttp3.Headers; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({ Headers.class, ResponseBody.class, Response.class}) -public class OrganisationClientTest { - - private GitHubClient github; - - private OrganisationClient organisationClient; - - private Json json; - - private static String getFixture(String resource) throws IOException { - return Resources.toString(getResource(OrganisationClientTest.class, resource), defaultCharset()); - } - - @Before - public void setUp() { - github = mock(GitHubClient.class); - organisationClient = new OrganisationClient(github, "github"); - json = Json.create(); - when(github.json()).thenReturn(json); - } -} From 29f2532f2c2a84b7561cb53d8a63460c25277233 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 16:45:19 +0100 Subject: [PATCH 10/14] docs: update wording in readme --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a7980434..da5f3c24 100644 --- a/README.md +++ b/README.md @@ -67,10 +67,8 @@ final RepositoryClient repositoryClient = githubClient.createRepositoryClient("m log.info(repositoryClient.getCommit("sha").get().htmlUrl()); ``` -Another way the structure is mirrored is that some of the APIs are nested under a parent API. -For example: - -Endpoints related to check runs or issues are nested under the Repository client: +Another example of the mirrored structure is that some of the APIs are nested under a parent API. +For example, endpoints related to check runs or issues are nested under the Repository client: ```java final ChecksClient checksClient = repositoryClient.createChecksApiClient(); checksClient.createCheckRun(CHECK_RUN_REQUEST); @@ -81,7 +79,7 @@ issueClient.createComment(ISSUE_ID, "comment body") ``` -Endpoints related to teams and memberships are nested under the Organisation client: +And endpoints related to teams and memberships are nested under the Organisation client: ```java final TeamClient teamClient = organisationClient.createTeamClient(); teamClient.getMembership("username"); From 7f38976315635a2c8d6b1f7ac69278328b8f6ba5 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 17:06:51 +0100 Subject: [PATCH 11/14] fix: naming --- .../com/spotify/github/v3/clients/OrganisationClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java index e5933e16..af5e9078 100644 --- a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java +++ b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java @@ -47,9 +47,9 @@ static OrganisationClient create(final GitHubClient github, final String org) { /** - * Create a membership API client. + * Create a Teams API client. * - * @return membership API client + * @return Teams API client */ public TeamClient createTeamClient(final GitHubClient github, final String org) { return TeamClient.create(github, org); From 00341cfd9b5fecdbe47fdc280ba42224d3c79874 Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Tue, 11 Jul 2023 17:11:21 +0100 Subject: [PATCH 12/14] fix: update wording --- .../com/spotify/github/v3/clients/TeamClient.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 10694383..f52d0c85 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -127,7 +127,7 @@ public CompletableFuture deleteTeam(final String slug) { } /** - * Add or update team membership for a user + * Add or update a team membership for a user. * * @param request update membership request * @return membership @@ -139,7 +139,7 @@ public CompletableFuture updateMembership(final MembershipCreate req } /** - * Get team membership of a user. + * Get a team membership of a user. * * @param slug the team slug * @param username username of the team member @@ -152,10 +152,10 @@ public CompletableFuture getMembership(final String slug, final Stri } /** - * List team members. + * List members of a specific team. * * @param slug the team slug - * @return list of all members in a team + * @return list of all users in a team */ public CompletableFuture> listTeamMembers(final String slug) { final String path = String.format(MEMBERS_TEMPLATE, org, slug); @@ -164,10 +164,10 @@ public CompletableFuture> listTeamMembers(final String slug) { } /** - * Delete a membership for a specific user. + * Delete a membership for a user. * * @param slug slug of the team name - * @return team + * @return membership */ public CompletableFuture deleteMembership(final String slug, final String username) { final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username); @@ -179,7 +179,7 @@ public CompletableFuture deleteMembership(final String slug, final String * List pending invitations for a team. * * @param slug the team slug - * @return list of all members in a team + * @return list of pending invitations for a team */ public CompletableFuture> listPendingTeamInvitations(final String slug) { final String path = String.format(INVITATIONS_TEMPLATE, org, slug); From 36d152126bdace874b158be1b1a3267574e0297e Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Wed, 12 Jul 2023 08:48:04 +0100 Subject: [PATCH 13/14] chore: formatting --- .../java/com/spotify/github/v3/clients/OrganisationClient.java | 1 - src/main/java/com/spotify/github/v3/clients/TeamClient.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java index af5e9078..84620411 100644 --- a/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java +++ b/src/main/java/com/spotify/github/v3/clients/OrganisationClient.java @@ -45,7 +45,6 @@ static OrganisationClient create(final GitHubClient github, final String org) { return new OrganisationClient(github, org); } - /** * Create a Teams API client. * 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 f52d0c85..6b7d1699 100644 --- a/src/main/java/com/spotify/github/v3/clients/TeamClient.java +++ b/src/main/java/com/spotify/github/v3/clients/TeamClient.java @@ -65,7 +65,6 @@ static TeamClient create(final GitHubClient github, final String org) { return new TeamClient(github, org); } - /** * Create a team in an organisation. * From b40ca623066749591cbd546fc7d1cc14f541391a Mon Sep 17 00:00:00 2001 From: Ellie Kelsch Date: Wed, 12 Jul 2023 14:05:04 +0100 Subject: [PATCH 14/14] feat: add test to test team client creation --- .../v3/clients/OrganisationClientTest.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java diff --git a/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java b/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java new file mode 100644 index 00000000..9f0645ca --- /dev/null +++ b/src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java @@ -0,0 +1,69 @@ +/*- + * -\-\- + * github-api + * -- + * Copyright (C) 2016 - 2020 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ + +package com.spotify.github.v3.clients; + +import static com.google.common.io.Resources.getResource; +import static java.nio.charset.Charset.defaultCharset; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.io.Resources; +import com.spotify.github.jackson.Json; +import com.spotify.github.v3.Team; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import org.junit.Before; +import org.junit.Test; + +public class OrganisationClientTest { + + private GitHubClient github; + + private OrganisationClient organisationClient; + + private Json json; + + private static String getFixture(String resource) throws IOException { + return Resources.toString(getResource(TeamClientTest.class, resource), defaultCharset()); + } + + @Before + public void setUp() { + github = mock(GitHubClient.class); + organisationClient = new OrganisationClient(github, "github"); + json = Json.create(); + when(github.json()).thenReturn(json); + } + + @Test + public void testTeamClient() throws Exception { + final TeamClient teamClient = organisationClient.createTeamClient(github, "github"); + final CompletableFuture fixture = + completedFuture(json.fromJson(getFixture("team_get.json"), Team.class)); + when(github.request("/orgs/github/teams/justice-league", Team.class)).thenReturn(fixture); + final Team team = teamClient.getTeam("justice-league").get(); + assertThat(team.id(), is(1)); + assertThat(team.name(), is("Justice League")); + } +}