Skip to content

Commit c52fbbb

Browse files
committed
feat: Add updateTeamPermissions for TeamClient
1 parent 9493ca9 commit c52fbbb

File tree

3 files changed

+108
-20
lines changed

3 files changed

+108
-20
lines changed

src/main/java/com/spotify/github/v3/clients/TeamClient.java

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
* -/-/-
1919
*/
2020

21-
2221
package com.spotify.github.v3.clients;
2322

2423
import static com.spotify.github.v3.clients.GitHubClient.*;
@@ -28,8 +27,10 @@
2827
import com.spotify.github.v3.User;
2928
import com.spotify.github.v3.orgs.Membership;
3029
import com.spotify.github.v3.orgs.TeamInvitation;
30+
import com.spotify.github.v3.orgs.requests.ImmutableTeamRepoPermissionUpdate;
3131
import com.spotify.github.v3.orgs.requests.MembershipCreate;
3232
import com.spotify.github.v3.orgs.requests.TeamCreate;
33+
import com.spotify.github.v3.orgs.requests.TeamRepoPermissionUpdate;
3334
import com.spotify.github.v3.orgs.requests.TeamUpdate;
3435
import java.lang.invoke.MethodHandles;
3536
import java.util.Iterator;
@@ -54,6 +55,8 @@ public class TeamClient {
5455

5556
private static final String INVITATIONS_TEMPLATE = "/orgs/%s/teams/%s/invitations";
5657

58+
private static final String REPO_TEMPLATE = "/orgs/%s/teams/%s/repos/%s/%s";
59+
5760
private final GitHubClient github;
5861

5962
private final String org;
@@ -75,7 +78,7 @@ static TeamClient create(final GitHubClient github, final String org) {
7578
*/
7679
public CompletableFuture<Team> createTeam(final TeamCreate request) {
7780
final String path = String.format(TEAM_TEMPLATE, org);
78-
log.debug("Creating team in: " + path);
81+
log.debug("Creating team in: {}", path);
7982
return github.post(path, github.json().toJsonUnchecked(request), Team.class);
8083
}
8184

@@ -87,7 +90,7 @@ public CompletableFuture<Team> createTeam(final TeamCreate request) {
8790
*/
8891
public CompletableFuture<Team> getTeam(final String slug) {
8992
final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug);
90-
log.debug("Fetching team from " + path);
93+
log.debug("Fetching team from {}", path);
9194
return github.request(path, Team.class);
9295
}
9396

@@ -98,7 +101,7 @@ public CompletableFuture<Team> getTeam(final String slug) {
98101
*/
99102
public CompletableFuture<List<Team>> listTeams() {
100103
final String path = String.format(TEAM_TEMPLATE, org);
101-
log.debug("Fetching teams from " + path);
104+
log.debug("Fetching teams from {}", path);
102105
return github.request(path, LIST_TEAMS);
103106
}
104107

@@ -111,7 +114,7 @@ public CompletableFuture<List<Team>> listTeams() {
111114
*/
112115
public CompletableFuture<Team> updateTeam(final TeamUpdate request, final String slug) {
113116
final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug);
114-
log.debug("Updating team in: " + path);
117+
log.debug("Updating team in: {}", path);
115118
return github.patch(path, github.json().toJsonUnchecked(request), Team.class);
116119
}
117120

@@ -123,7 +126,7 @@ public CompletableFuture<Team> updateTeam(final TeamUpdate request, final String
123126
*/
124127
public CompletableFuture<Void> deleteTeam(final String slug) {
125128
final String path = String.format(TEAM_SLUG_TEMPLATE, org, slug);
126-
log.debug("Deleting team from: " + path);
129+
log.debug("Deleting team from: {}", path);
127130
return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER);
128131
}
129132

@@ -133,9 +136,10 @@ public CompletableFuture<Void> deleteTeam(final String slug) {
133136
* @param request update membership request
134137
* @return membership
135138
*/
136-
public CompletableFuture<Membership> updateMembership(final MembershipCreate request, final String slug, final String username) {
139+
public CompletableFuture<Membership> updateMembership(
140+
final MembershipCreate request, final String slug, final String username) {
137141
final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username);
138-
log.debug("Updating membership in: " + path);
142+
log.debug("Updating membership in: {}", path);
139143
return github.put(path, github.json().toJsonUnchecked(request), Membership.class);
140144
}
141145

@@ -148,7 +152,7 @@ public CompletableFuture<Membership> updateMembership(final MembershipCreate req
148152
*/
149153
public CompletableFuture<Membership> getMembership(final String slug, final String username) {
150154
final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username);
151-
log.debug("Fetching membership for: " + path);
155+
log.debug("Fetching membership for: {}", path);
152156
return github.request(path, Membership.class);
153157
}
154158

@@ -160,7 +164,7 @@ public CompletableFuture<Membership> getMembership(final String slug, final Stri
160164
*/
161165
public CompletableFuture<List<User>> listTeamMembers(final String slug) {
162166
final String path = String.format(MEMBERS_TEMPLATE, org, slug);
163-
log.debug("Fetching members for: " + path);
167+
log.debug("Fetching members for: {}", path);
164168
return github.request(path, LIST_TEAM_MEMBERS);
165169
}
166170

@@ -173,7 +177,7 @@ public CompletableFuture<List<User>> listTeamMembers(final String slug) {
173177
*/
174178
public Iterator<AsyncPage<User>> listTeamMembers(final String slug, final int pageSize) {
175179
final String path = String.format(PAGED_MEMBERS_TEMPLATE, org, slug, pageSize);
176-
log.debug("Fetching members for: " + path);
180+
log.debug("Fetching members for: {}", path);
177181
return new GithubPageIterator<>(new GithubPage<>(github, path, LIST_TEAM_MEMBERS));
178182
}
179183

@@ -185,7 +189,7 @@ public Iterator<AsyncPage<User>> listTeamMembers(final String slug, final int pa
185189
*/
186190
public CompletableFuture<Void> deleteMembership(final String slug, final String username) {
187191
final String path = String.format(MEMBERSHIP_TEMPLATE, org, slug, username);
188-
log.debug("Deleting membership from: " + path);
192+
log.debug("Deleting membership from: {}", path);
189193
return github.delete(path).thenAccept(IGNORE_RESPONSE_CONSUMER);
190194
}
191195

@@ -197,7 +201,31 @@ public CompletableFuture<Void> deleteMembership(final String slug, final String
197201
*/
198202
public CompletableFuture<List<TeamInvitation>> listPendingTeamInvitations(final String slug) {
199203
final String path = String.format(INVITATIONS_TEMPLATE, org, slug);
200-
log.debug("Fetching pending invitations for: " + path);
204+
log.debug("Fetching pending invitations for: {}", path);
201205
return github.request(path, LIST_PENDING_TEAM_INVITATIONS);
202206
}
207+
208+
/**
209+
* Update permissions for a team on a specific repository.
210+
*
211+
* @param slug the team slug
212+
* @param repo the repository name
213+
* @param permission the permission level (pull, push, maintain, triage, admin, or a custom repo defined role name)
214+
* @return void status code 204 if successful
215+
*/
216+
public CompletableFuture<Void> updateTeamPermissions(
217+
final String slug, final String repo, final String permission) {
218+
final String path = String.format(REPO_TEMPLATE, org, slug, org, repo);
219+
final TeamRepoPermissionUpdate request =
220+
ImmutableTeamRepoPermissionUpdate.builder()
221+
.org(org)
222+
.repo(repo)
223+
.teamSlug(slug)
224+
.permission(permission)
225+
.build();
226+
log.debug("Updating team permissions for: {}", path);
227+
return github
228+
.put(path, github.json().toJsonUnchecked(request))
229+
.thenAccept(IGNORE_RESPONSE_CONSUMER);
230+
}
203231
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2016 - 2020 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github.v3.orgs.requests;
22+
23+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
25+
import com.spotify.github.GithubStyle;
26+
import org.immutables.value.Value;
27+
28+
/** Request to update permissions of a team for a specific repo */
29+
@Value.Immutable
30+
@GithubStyle
31+
@JsonSerialize(as = ImmutableTeamRepoPermissionUpdate.class)
32+
@JsonDeserialize(as = ImmutableTeamRepoPermissionUpdate.class)
33+
public interface TeamRepoPermissionUpdate {
34+
String org();
35+
String repo();
36+
String teamSlug();
37+
String permission();
38+
}

src/test/java/com/spotify/github/v3/clients/TeamClientTest.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package com.spotify.github.v3.clients;
2222

2323
import static com.google.common.io.Resources.getResource;
24+
import static com.spotify.github.MockHelper.createMockHttpResponse;
2425
import static com.spotify.github.v3.clients.GitHubClient.LIST_PENDING_TEAM_INVITATIONS;
2526
import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAMS;
2627
import static com.spotify.github.v3.clients.GitHubClient.LIST_TEAM_MEMBERS;
@@ -48,6 +49,7 @@
4849
import com.spotify.github.v3.orgs.requests.TeamUpdate;
4950
import java.io.IOException;
5051
import java.util.List;
52+
import java.util.Map;
5153
import java.util.concurrent.CompletableFuture;
5254

5355
import org.junit.jupiter.api.BeforeEach;
@@ -135,7 +137,10 @@ public void updateTeam() throws Exception {
135137

136138
assertThat(actualResponse.get().name(), is("Justice League2"));
137139
verify(github, times(1))
138-
.patch(eq("/orgs/github/teams/justice-league"), eq("{\"name\":\"Justice League2\"}"), eq(Team.class));
140+
.patch(
141+
eq("/orgs/github/teams/justice-league"),
142+
eq("{\"name\":\"Justice League2\"}"),
143+
eq(Team.class));
139144
}
140145

141146
@Test
@@ -165,22 +170,24 @@ public void listTeamMembers() throws Exception {
165170
@Test
166171
public void listTeamMembersPaged() throws Exception {
167172
final String firstPageLink =
168-
"<https://github.com/api/v3/orgs/github/teams/1/members?page=2>; rel=\"next\", <https://github.com/api/v3/orgs/github/teams/1/members?page=2>; rel=\"last\"";
173+
"<https://github.com/api/v3/orgs/github/teams/1/members?page=2>; rel=\"next\", <https://github.com/api/v3/orgs/github/teams/1/members?page=2>; rel=\"last\"";
169174
final String firstPageBody =
170-
Resources.toString(getResource(this.getClass(), "list_members_page1.json"), defaultCharset());
175+
Resources.toString(
176+
getResource(this.getClass(), "list_members_page1.json"), defaultCharset());
171177
final HttpResponse firstPageResponse = createMockResponse(firstPageLink, firstPageBody);
172178

173179
final String lastPageLink =
174-
"<https://github.com/api/v3/orgs/github/teams/1/members>; rel=\"first\", <https://github.com/api/v3/orgs/github/teams/1/members>; rel=\"prev\"";
180+
"<https://github.com/api/v3/orgs/github/teams/1/members>; rel=\"first\", <https://github.com/api/v3/orgs/github/teams/1/members>; rel=\"prev\"";
175181
final String lastPageBody =
176-
Resources.toString(getResource(this.getClass(), "list_members_page2.json"), defaultCharset());
182+
Resources.toString(
183+
getResource(this.getClass(), "list_members_page2.json"), defaultCharset());
177184

178185
final HttpResponse lastPageResponse = createMockResponse(lastPageLink, lastPageBody);
179186

180187
when(github.request(endsWith("/orgs/github/teams/1/members?per_page=1")))
181-
.thenReturn(completedFuture(firstPageResponse));
188+
.thenReturn(completedFuture(firstPageResponse));
182189
when(github.request(endsWith("/orgs/github/teams/1/members?page=2")))
183-
.thenReturn(completedFuture(lastPageResponse));
190+
.thenReturn(completedFuture(lastPageResponse));
184191

185192
final Iterable<AsyncPage<User>> pageIterator = () -> teamClient.listTeamMembers("1", 1);
186193
final List<User> users = Async.streamFromPaginatingIterable(pageIterator).collect(toList());
@@ -229,4 +236,19 @@ public void listPendingTeamInvitations() throws Exception {
229236
assertThat(pendingInvitations.get(1).id(), is(2));
230237
assertThat(pendingInvitations.size(), is(2));
231238
}
239+
240+
@Test
241+
void updateTeamPermissions() {
242+
String apiUrl = "/orgs/github/teams/cat-squad/repos/github/octocat";
243+
HttpResponse response = createMockHttpResponse(apiUrl, 204, "", Map.of());
244+
when(github.put(eq(apiUrl), any())).thenReturn(completedFuture(response));
245+
246+
teamClient.updateTeamPermissions("cat-squad", "octocat", "pull").join();
247+
248+
verify(github, times(1))
249+
.put(
250+
eq(apiUrl),
251+
eq(
252+
"{\"org\":\"github\",\"repo\":\"octocat\",\"team_slug\":\"cat-squad\",\"permission\":\"pull\"}"));
253+
}
232254
}

0 commit comments

Comments
 (0)