diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 04898039bc..9e6bb3e1a7 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -25,8 +25,12 @@ import java.io.IOException; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.List; +import javax.annotation.CheckForNull; /** * A pull request. @@ -223,6 +227,27 @@ protected void wrapUp(GHPullRequestFileDetail[] page) { }; } + /** + * Retrieves all the reviews associated to this pull request. + */ + public PagedIterable listReviews() { + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator(root.retrieve() + .withPreview("application/vnd.github.black-cat-preview+json") + .asIterator(String.format("%s/reviews", getApiRoute()), + GHPullRequestReview[].class, pageSize)) { + @Override + protected void wrapUp(GHPullRequestReview[] page) { + for (GHPullRequestReview r: page) { + r.wrapUp(GHPullRequest.this); + } + } + }; + } + }; + } + /** * Obtains all the review comments associated with this pull request. */ @@ -259,6 +284,34 @@ protected void wrapUp(GHPullRequestCommitDetail[] page) { }; } + @Preview + @Deprecated + public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event, + GHPullRequestReviewComment... comments) + throws IOException { + return createReview(body, event, Arrays.asList(comments)); + } + + @Preview + @Deprecated + public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event, + List comments) + throws IOException { + if (event == null) { + event = GHPullRequestReviewState.PENDING; + } + List draftComments = new ArrayList(comments.size()); + for (GHPullRequestReviewComment c : comments) { + draftComments.add(new DraftReviewComment(c.getBody(), c.getPath(), c.getPosition())); + } + return new Requester(root).method("POST") + .with("body", body) + //.with("event", event.name()) + ._with("comments", draftComments) + .withPreview("application/vnd.github.black-cat-preview+json") + .to(getApiRoute() + "/reviews", GHPullRequestReview.class).wrapUp(this); + } + public GHPullRequestReviewComment createReviewComment(String body, String sha, String path, int position) throws IOException { return new Requester(root).method("POST") .with("body", body) @@ -300,4 +353,28 @@ private void fetchIssue() throws IOException { fetchedIssueDetails = true; } } + + private static class DraftReviewComment { + private String body; + private String path; + private int position; + + public DraftReviewComment(String body, String path, int position) { + this.body = body; + this.path = path; + this.position = position; + } + + public String getBody() { + return body; + } + + public String getPath() { + return path; + } + + public int getPosition() { + return position; + } + } } diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReview.java b/src/main/java/org/kohsuke/github/GHPullRequestReview.java new file mode 100644 index 0000000000..1fb57d4bf9 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHPullRequestReview.java @@ -0,0 +1,147 @@ +/* + * The MIT License + * + * Copyright (c) 2017, CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.kohsuke.github; + +import java.io.IOException; +import java.net.URL; + +/** + * Review to the pull request + * + * @see GHPullRequest#listReviews() + * @see GHPullRequest#createReview(String, GHPullRequestReviewState, GHPullRequestReviewComment...) + */ +public class GHPullRequestReview extends GHObject { + GHPullRequest owner; + + private String body; + private GHUser user; + private String commit_id; + private GHPullRequestReviewState state; + + /*package*/ GHPullRequestReview wrapUp(GHPullRequest owner) { + this.owner = owner; + return this; + } + + /** + * Gets the pull request to which this review is associated. + */ + public GHPullRequest getParent() { + return owner; + } + + /** + * The comment itself. + */ + public String getBody() { + return body; + } + + /** + * Gets the user who posted this review. + */ + public GHUser getUser() throws IOException { + return owner.root.getUser(user.getLogin()); + } + + public String getCommitId() { + return commit_id; + } + + public GHPullRequestReviewState getState() { + return state; + } + + @Override + public URL getHtmlUrl() { + return null; + } + + protected String getApiRoute() { + return owner.getApiRoute()+"/reviews/"+id; + } + + /** + * Updates the comment. + */ + @Preview + @Deprecated + public void submit(String body, GHPullRequestReviewState event) throws IOException { + new Requester(owner.root).method("POST") + .with("body", body) + .with("event", event.action()) + .withPreview("application/vnd.github.black-cat-preview+json") + .to(getApiRoute()+"/events",this); + this.body = body; + this.state = event; + } + + /** + * Deletes this review. + */ + @Preview + @Deprecated + public void delete() throws IOException { + new Requester(owner.root).method("DELETE") + .withPreview("application/vnd.github.black-cat-preview+json") + .to(getApiRoute()); + } + + /** + * Dismisses this review. + */ + @Preview + @Deprecated + public void dismiss(String message) throws IOException { + new Requester(owner.root).method("PUT") + .with("message", message) + .withPreview("application/vnd.github.black-cat-preview+json") + .to(getApiRoute()+"/dismissals"); + state = GHPullRequestReviewState.DISMISSED; + } + + /** + * Obtains all the review comments associated with this pull request review. + */ + @Preview + @Deprecated + public PagedIterable listReviewComments() throws IOException { + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator( + owner.root.retrieve() + .withPreview("application/vnd.github.black-cat-preview+json") + .asIterator(getApiRoute() + "/comments", + GHPullRequestReviewComment[].class, pageSize)) { + protected void wrapUp(GHPullRequestReviewComment[] page) { + for (GHPullRequestReviewComment c : page) + c.wrapUp(owner); + } + }; + } + }; + } + +} diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java index 05784b5078..33097bc29d 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java @@ -44,6 +44,14 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable { private int position; private int originalPosition; + public static GHPullRequestReviewComment draft(String body, String path, int position) { + GHPullRequestReviewComment result = new GHPullRequestReviewComment(); + result.body = body; + result.path = path; + result.position = position; + return result; + } + /*package*/ GHPullRequestReviewComment wrapUp(GHPullRequest owner) { this.owner = owner; return this; diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java new file mode 100644 index 0000000000..fca3fabfb3 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java @@ -0,0 +1,19 @@ +package org.kohsuke.github; + +public enum GHPullRequestReviewState { + PENDING(null), + APPROVED("APPROVE"), + REQUEST_CHANGES("REQUEST_CHANGES"), + COMMENTED("COMMENT"), + DISMISSED(null); + + private final String _action; + + GHPullRequestReviewState(String action) { + _action = action; + } + + public String action() { + return _action; + } +} diff --git a/src/test/java/org/kohsuke/github/AbstractGitHubApiTestBase.java b/src/test/java/org/kohsuke/github/AbstractGitHubApiTestBase.java index f32087c0d1..a7afb38fd7 100644 --- a/src/test/java/org/kohsuke/github/AbstractGitHubApiTestBase.java +++ b/src/test/java/org/kohsuke/github/AbstractGitHubApiTestBase.java @@ -1,5 +1,8 @@ package org.kohsuke.github; +import java.io.FileInputStream; +import java.util.Properties; +import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; @@ -19,9 +22,17 @@ public abstract class AbstractGitHubApiTestBase extends Assert { public void setUp() throws Exception { File f = new File(System.getProperty("user.home"), ".github.kohsuke2"); if (f.exists()) { + Properties props = new Properties(); + FileInputStream in = null; + try { + in = new FileInputStream(f); + props.load(in); + } finally { + IOUtils.closeQuietly(in); + } // use the non-standard credential preferentially, so that developers of this library do not have // to clutter their event stream. - gitHub = GitHubBuilder.fromPropertyFile(f.getPath()).withRateLimitHandler(RateLimitHandler.FAIL).build(); + gitHub = GitHubBuilder.fromProperties(props).withRateLimitHandler(RateLimitHandler.FAIL).build(); } else { gitHub = GitHubBuilder.fromCredentials().withRateLimitHandler(RateLimitHandler.FAIL).build(); } diff --git a/src/test/java/org/kohsuke/github/PullRequestTest.java b/src/test/java/org/kohsuke/github/PullRequestTest.java index c7ce77d289..ce09b2ab13 100644 --- a/src/test/java/org/kohsuke/github/PullRequestTest.java +++ b/src/test/java/org/kohsuke/github/PullRequestTest.java @@ -7,6 +7,9 @@ import java.util.Collection; import java.util.List; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; + /** * @author Kohsuke Kawaguchi */ @@ -26,6 +29,33 @@ public void createPullRequestComment() throws Exception { p.comment("Some comment"); } + @Test + public void testPullRequestReviews() throws Exception { + String name = rnd.next(); + GHPullRequest p = getRepository().createPullRequest(name, "stable", "master", "## test"); + GHPullRequestReview draftReview = p.createReview("Some draft review", null, + GHPullRequestReviewComment.draft("Some niggle", "changelog.html", 1) + ); + assertThat(draftReview.getState(), is(GHPullRequestReviewState.PENDING)); + assertThat(draftReview.getBody(), is("Some draft review")); + assertThat(draftReview.getCommitId(), notNullValue()); + List reviews = p.listReviews().asList(); + assertThat(reviews.size(), is(1)); + GHPullRequestReview review = reviews.get(0); + assertThat(review.getState(), is(GHPullRequestReviewState.PENDING)); + assertThat(review.getBody(), is("Some draft review")); + assertThat(review.getCommitId(), notNullValue()); + review.submit("Some review comment", GHPullRequestReviewState.COMMENTED); + List comments = review.listReviewComments().asList(); + assertEquals(1, comments.size()); + GHPullRequestReviewComment comment = comments.get(0); + assertEquals("Some niggle", comment.getBody()); + review = p.createReview("Some new review", null, + GHPullRequestReviewComment.draft("Some niggle", "changelog.html", 1) + ); + review.delete(); + } + @Test public void testPullRequestReviewComments() throws Exception { String name = rnd.next();