Skip to content

Commit f862545

Browse files
authored
Add Request Review functions (list, create, remove) (#36)
1 parent 74bb1fc commit f862545

File tree

13 files changed

+679
-1
lines changed

13 files changed

+679
-1
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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;
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 java.net.URI;
27+
import javax.annotation.Nullable;
28+
import org.immutables.value.Value;
29+
30+
/**
31+
* Team resource represents data returned by a single Team get operation.
32+
*/
33+
@Value.Immutable
34+
@GithubStyle
35+
@JsonSerialize(as = ImmutableTeam.class)
36+
@JsonDeserialize(as = ImmutableTeam.class)
37+
public interface Team {
38+
39+
/** ID. */
40+
@Nullable
41+
Integer id();
42+
43+
/** Name. */
44+
@Nullable
45+
String name();
46+
47+
/** Slug. */
48+
@Nullable
49+
String slug();
50+
51+
/** Description */
52+
@Nullable
53+
String description();
54+
55+
/** Privacy */
56+
@Nullable
57+
String privacy();
58+
59+
/** Permission */
60+
@Nullable
61+
String permission();
62+
63+
/** Node ID */
64+
@Nullable
65+
String nodeId();
66+
67+
/** URL */
68+
@Nullable
69+
URI url();
70+
71+
/** HTML URL */
72+
@Nullable
73+
URI htmlUrl();
74+
75+
/** Repositories URL */
76+
@Nullable
77+
URI repositoriesUrl();
78+
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.spotify.github.v3.exceptions.RequestNotOkException;
3333
import com.spotify.github.v3.git.Reference;
3434
import com.spotify.github.v3.prs.PullRequestItem;
35+
import com.spotify.github.v3.prs.ReviewRequests;
3536
import com.spotify.github.v3.repos.Branch;
3637
import com.spotify.github.v3.repos.CommitItem;
3738
import com.spotify.github.v3.repos.FolderContent;
@@ -76,6 +77,8 @@ public class GitHubClient {
7677
static final TypeReference<List<CommitItem>> LIST_COMMIT_TYPE_REFERENCE =
7778
new TypeReference<>() {};
7879
static final TypeReference<List<Review>> LIST_REVIEW_TYPE_REFERENCE = new TypeReference<>() {};
80+
static final TypeReference<ReviewRequests> LIST_REVIEW_REQUEST_TYPE_REFERENCE =
81+
new TypeReference<>() {};
7982
static final TypeReference<List<Status>> LIST_STATUS_TYPE_REFERENCE =
8083
new TypeReference<>() {};
8184
static final TypeReference<List<FolderContent>> LIST_FOLDERCONTENT_TYPE_REFERENCE =
@@ -493,6 +496,22 @@ CompletableFuture<Response> delete(final String path) {
493496
return call(request);
494497
}
495498

499+
/**
500+
* Make an http DELETE request for the given path.
501+
*
502+
* @param path relative to the Github base url
503+
* @param data request body as stringified JSON
504+
* @return response body as String
505+
*/
506+
CompletableFuture<Response> delete(final String path, final String data) {
507+
final Request request =
508+
requestBuilder(path)
509+
.method("DELETE", RequestBody.create(parse(MediaType.APPLICATION_JSON), data))
510+
.build();
511+
log.debug("Making DELETE request to {}", request.url().toString());
512+
return call(request);
513+
}
514+
496515
/**
497516
* Create a URL for a given path to this Github server.
498517
*
@@ -636,7 +655,7 @@ public void onResponse(final Call call, final Response response) {
636655

637656
private RequestNotOkException mapException(final Response res, final Request request)
638657
throws IOException {
639-
String bodyString = res.body().string();
658+
String bodyString = res.body() != null ? res.body().string() : "";
640659
if (res.code() == FORBIDDEN) {
641660
if (bodyString.contains("Repository was archived so is read-only")) {
642661
return new ReadOnlyRepositoryException(request.url().encodedPath(), res.code(), bodyString);

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static com.spotify.github.v3.clients.GitHubClient.IGNORE_RESPONSE_CONSUMER;
2424
import static com.spotify.github.v3.clients.GitHubClient.LIST_COMMIT_TYPE_REFERENCE;
2525
import static com.spotify.github.v3.clients.GitHubClient.LIST_PR_TYPE_REFERENCE;
26+
import static com.spotify.github.v3.clients.GitHubClient.LIST_REVIEW_REQUEST_TYPE_REFERENCE;
2627
import static com.spotify.github.v3.clients.GitHubClient.LIST_REVIEW_TYPE_REFERENCE;
2728

2829
import com.google.common.base.Strings;
@@ -47,6 +48,7 @@ public class PullRequestClient {
4748
private static final String PR_NUMBER_TEMPLATE = "/repos/%s/%s/pulls/%s";
4849
private static final String PR_COMMITS_TEMPLATE = "/repos/%s/%s/pulls/%s/commits";
4950
private static final String PR_REVIEWS_TEMPLATE = "/repos/%s/%s/pulls/%s/reviews";
51+
private static final String PR_REVIEW_REQUESTS_TEMPLATE = "/repos/%s/%s/pulls/%s/requested_reviewers";
5052

5153
private final GitHubClient github;
5254
private final String owner;
@@ -173,6 +175,46 @@ public CompletableFuture<Review> createReview(final int number, final ReviewPara
173175
return github.post(path, jsonPayload, Review.class);
174176
}
175177

178+
/**
179+
* List pull request requested reviews.
180+
*
181+
* @param number pull request number
182+
* @return list of reviews
183+
*/
184+
public CompletableFuture<ReviewRequests> listReviewRequests(final int number) {
185+
final String path = String.format(PR_REVIEW_REQUESTS_TEMPLATE, owner, repo, number);
186+
log.debug("Fetching pull request requested reviews from " + path);
187+
return github.request(path, LIST_REVIEW_REQUEST_TYPE_REFERENCE);
188+
}
189+
190+
/**
191+
* Requests a review for a pull request.
192+
*
193+
* @param number pull request number
194+
* @param properties properties for reviewing the PR, such as reviewers and team_reviewers.
195+
* @see "https://docs.github.com/en/rest/reference/pulls#request-reviewers-for-a-pull-request"
196+
*/
197+
public CompletableFuture<PullRequest> requestReview(final int number, final RequestReviewParameters properties) {
198+
final String path = String.format(PR_REVIEW_REQUESTS_TEMPLATE, owner, repo, number);
199+
final String jsonPayload = github.json().toJsonUnchecked(properties);
200+
log.debug("Requesting reviews for PR: " + path);
201+
return github.post(path, jsonPayload, PullRequest.class);
202+
}
203+
204+
/**
205+
* Remove a request for review for a pull request.
206+
*
207+
* @param number pull request number
208+
* @param properties properties for reviewing the PR, such as reviewers and team_reviewers.
209+
* @see "https://docs.github.com/en/rest/reference/pulls#request-reviewers-for-a-pull-request"
210+
*/
211+
public CompletableFuture<Void> removeRequestedReview(final int number, final RequestReviewParameters properties) {
212+
final String path = String.format(PR_REVIEW_REQUESTS_TEMPLATE, owner, repo, number);
213+
final String jsonPayload = github.json().toJsonUnchecked(properties);
214+
log.debug("Removing requested reviews for PR: " + path);
215+
return github.delete(path, jsonPayload).thenAccept(IGNORE_RESPONSE_CONSUMER);
216+
}
217+
176218
/**
177219
* Merges a pull request.
178220
*
@@ -192,4 +234,5 @@ private CompletableFuture<List<PullRequestItem>> list(final String parameterPath
192234
log.debug("Fetching pull requests from " + path);
193235
return github.request(path, LIST_PR_TYPE_REFERENCE);
194236
}
237+
195238
}

src/main/java/com/spotify/github/v3/prs/PullRequestItem.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ public interface PullRequestItem extends CloseTracking {
133133
@JsonProperty("_links")
134134
PullRequestLinks links();
135135

136+
/** Requested reviewers (users) */
137+
@Nullable
138+
@JsonProperty("requested_reviewers")
139+
List<User> requestedReviewers();
140+
141+
/** Requested reviewers (teams) */
142+
@Nullable
143+
@JsonProperty("requested_teams")
144+
List<User> requestedTeams();
145+
136146
/** @Deprecated the merge commit sha. */
137147
Optional<String> mergeCommitSha();
138148
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.prs;
22+
23+
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
26+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
27+
import com.spotify.github.GithubStyle;
28+
import java.util.List;
29+
import java.util.Optional;
30+
import org.immutables.value.Value;
31+
32+
/**
33+
* The parameters for requesting a review or removing review request for a Pull Request.
34+
*
35+
* @see "https://docs.github.com/en/rest/reference/pulls#request-reviewers-for-a-pull-request--parameters"
36+
*/
37+
@Value.Immutable
38+
@GithubStyle
39+
@JsonSerialize(as = ImmutableRequestReviewParameters.class)
40+
@JsonDeserialize(as = ImmutableRequestReviewParameters.class)
41+
@JsonInclude(JsonInclude.Include.NON_ABSENT)
42+
public abstract class RequestReviewParameters {
43+
44+
/** Request reviews from users (list of login) */
45+
public abstract Optional<List<String>> reviewers();
46+
47+
/** Request reviews from teams (list of slugs) */
48+
@JsonProperty("team_reviewers")
49+
public abstract Optional<List<String>> teamReviewers();
50+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.prs;
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 com.spotify.github.v3.User;
27+
import com.spotify.github.v3.Team;
28+
import java.util.List;
29+
import javax.annotation.Nullable;
30+
import org.immutables.value.Value;
31+
32+
/**
33+
* Pull request review resource represents data returned by a single PR review get operation. It
34+
* contains all the fields from {@link Review} entity.
35+
*
36+
* @see https://docs.github.com/en/rest/reference/pulls#list-requested-reviewers-for-a-pull-request
37+
*/
38+
@Value.Immutable
39+
@GithubStyle
40+
@JsonSerialize(as = ImmutableReviewRequests.class)
41+
@JsonDeserialize(as = ImmutableReviewRequests.class)
42+
public interface ReviewRequests {
43+
44+
@Nullable
45+
List<User> users();
46+
47+
@Nullable
48+
List<Team> teams();
49+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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;
22+
23+
import static com.google.common.io.Resources.getResource;
24+
import static java.nio.charset.Charset.defaultCharset;
25+
import static org.hamcrest.MatcherAssert.assertThat;
26+
import static org.hamcrest.core.Is.is;
27+
28+
import com.google.common.io.Resources;
29+
import com.spotify.github.jackson.Json;
30+
import java.io.IOException;
31+
import java.net.URI;
32+
import java.util.Optional;
33+
import org.junit.Before;
34+
import org.junit.Test;
35+
36+
public class TeamTest {
37+
38+
private String fixture;
39+
40+
public static final void assertTeam(final Team team) {
41+
assertThat(team.id(), is(1));
42+
assertThat(team.name(), is("Justice League"));
43+
assertThat(team.slug(), is("justice-league"));
44+
assertThat(team.description(), is("A great team."));
45+
assertThat(team.privacy(), is("closed"));
46+
assertThat(team.permission(), is("admin"));
47+
assertThat(team.nodeId(), is("MDQ6VGVhbTE="));
48+
assertThat(team.url(), is(URI.create("https://api.github.com/teams/" + team.id())));
49+
assertThat(team.htmlUrl(), is(URI.create("https://api.github.com/teams/" + team.slug())));
50+
assertThat(team.repositoriesUrl(), is(URI.create(team.url() + "/repos")));
51+
}
52+
53+
@Before
54+
public void setUp() throws Exception {
55+
fixture = Resources.toString(getResource(this.getClass(), "team.json"), defaultCharset());
56+
}
57+
58+
@Test
59+
public void testDeserialization() throws IOException {
60+
final Team team = Json.create().fromJson(fixture, Team.class);
61+
assertTeam(team);
62+
}
63+
}

0 commit comments

Comments
 (0)