diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 742ab24279..4e9f1e7e93 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -23,26 +23,22 @@ */ package org.kohsuke.github; -import javax.annotation.CheckForNull; 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 static org.kohsuke.github.Previews.*; /** * A pull request. - * + * * @author Kohsuke Kawaguchi * @see GHRepository#getPullRequest(int) */ @SuppressWarnings({"UnusedDeclaration"}) public class GHPullRequest extends GHIssue { + private static final String COMMENTS_ACTION = "/comments"; + private String patch_url, diff_url, issue_url; private GHCommitPointer base; private String merged_at; @@ -91,7 +87,7 @@ protected String getApiRoute() { public URL getPatchUrl() { return GitHub.parseURL(patch_url); } - + /** * The URL of the patch file. * like https://github.com/jenkinsci/jenkins/pull/100.patch @@ -114,7 +110,7 @@ public GHCommitPointer getBase() { public GHCommitPointer getHead() { return head; } - + @Deprecated public Date getIssueUpdatedAt() throws IOException { return super.getUpdatedAt(); @@ -262,7 +258,6 @@ public PagedIterable listReviews() { return new PagedIterable() { public PagedIterator _iterator(int pageSize) { return new PagedIterator(root.retrieve() - .withPreview(BLACK_CAT) .asIterator(String.format("%s/reviews", getApiRoute()), GHPullRequestReview[].class, pageSize)) { @Override @@ -282,7 +277,7 @@ protected void wrapUp(GHPullRequestReview[] page) { public PagedIterable listReviewComments() throws IOException { return new PagedIterable() { public PagedIterator _iterator(int pageSize) { - return new PagedIterator(root.retrieve().asIterator(getApiRoute() + "/comments", + return new PagedIterator(root.retrieve().asIterator(getApiRoute() + COMMENTS_ACTION, GHPullRequestReviewComment[].class, pageSize)) { protected void wrapUp(GHPullRequestReviewComment[] page) { for (GHPullRequestReviewComment c : page) @@ -312,32 +307,8 @@ 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(BLACK_CAT) - .to(getApiRoute() + "/reviews", GHPullRequestReview.class).wrapUp(this); + public GHPullRequestReviewBuilder createReview() { + return new GHPullRequestReviewBuilder(this); } public GHPullRequestReviewComment createReviewComment(String body, String sha, String path, int position) throws IOException { @@ -346,7 +317,7 @@ public GHPullRequestReviewComment createReviewComment(String body, String sha, S .with("commit_id", sha) .with("path", path) .with("position", position) - .to(getApiRoute() + "/comments", GHPullRequestReviewComment.class).wrapUp(this); + .to(getApiRoute() + COMMENTS_ACTION, GHPullRequestReviewComment.class).wrapUp(this); } /** @@ -387,10 +358,10 @@ public void merge(String msg, String sha) throws IOException { */ public void merge(String msg, String sha, MergeMethod method) throws IOException { new Requester(root).method("PUT") - .with("commit_message",msg) - .with("sha",sha) - .with("merge_method",method) - .to(getApiRoute()+"/merge"); + .with("commit_message", msg) + .with("sha", sha) + .with("merge_method", method) + .to(getApiRoute() + "/merge"); } public enum MergeMethod{ MERGE, SQUASH, REBASE } @@ -401,28 +372,4 @@ 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 index d7afe59aad..45b6c044d9 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReview.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReview.java @@ -23,17 +23,19 @@ */ package org.kohsuke.github; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import javax.annotation.CheckForNull; import java.io.IOException; import java.net.URL; -import static org.kohsuke.github.Previews.*; - /** - * Review to the pull request + * Review to a pull request. * * @see GHPullRequest#listReviews() - * @see GHPullRequest#createReview(String, GHPullRequestReviewState, GHPullRequestReviewComment...) + * @see GHPullRequestReviewBuilder */ +@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD"}, justification = "JSON API") public class GHPullRequestReview extends GHObject { GHPullRequest owner; @@ -72,6 +74,7 @@ public String getCommitId() { return commit_id; } + @CheckForNull public GHPullRequestReviewState getState() { return state; } @@ -85,41 +88,41 @@ protected String getApiRoute() { return owner.getApiRoute()+"/reviews/"+id; } + /** + * @deprecated + * Former preview method that changed when it got public. Left here for backward compatibility. + * Use {@link #submit(String, GHPullRequestReviewEvent)} + */ + public void submit(String body, GHPullRequestReviewState state) throws IOException { + submit(body,state.toEvent()); + } + /** * Updates the comment. */ - @Preview - @Deprecated - public void submit(String body, GHPullRequestReviewState event) throws IOException { + public void submit(String body, GHPullRequestReviewEvent 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; + this.state = event.toState(); } /** * Deletes this review. */ - @Preview - @Deprecated public void delete() throws IOException { new Requester(owner.root).method("DELETE") - .withPreview(BLACK_CAT) .to(getApiRoute()); } /** * Dismisses this review. */ - @Preview - @Deprecated public void dismiss(String message) throws IOException { new Requester(owner.root).method("PUT") .with("message", message) - .withPreview(BLACK_CAT) .to(getApiRoute()+"/dismissals"); state = GHPullRequestReviewState.DISMISSED; } @@ -127,14 +130,11 @@ public void dismiss(String message) throws IOException { /** * 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(BLACK_CAT) .asIterator(getApiRoute() + "/comments", GHPullRequestReviewComment[].class, pageSize)) { protected void wrapUp(GHPullRequestReviewComment[] page) { @@ -145,5 +145,4 @@ protected void wrapUp(GHPullRequestReviewComment[] page) { } }; } - } diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewBuilder.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewBuilder.java new file mode 100644 index 0000000000..625f6bd923 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewBuilder.java @@ -0,0 +1,91 @@ +package org.kohsuke.github; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Builds up a creation of new {@link GHPullRequestReview}. + * + * @author Kohsuke Kawaguchi + * @see GHPullRequest#createReview() + */ +public class GHPullRequestReviewBuilder { + private final GHPullRequest pr; + private final Requester builder; + private final List comments = new ArrayList(); + + /*package*/ GHPullRequestReviewBuilder(GHPullRequest pr) { + this.pr = pr; + this.builder = new Requester(pr.root); + } + + // public GHPullRequestReview createReview(@Nullable String commitId, String body, GHPullRequestReviewEvent event, + // List comments) throws IOException + + /** + * The SHA of the commit that needs a review. Not using the latest commit SHA may render your review comment outdated if a subsequent commit modifies the line you specify as the position. Defaults to the most recent commit in the pull request when you do not specify a value. + */ + public GHPullRequestReviewBuilder commitId(String commitId) { + builder.with("commit_id",commitId); + return this; + } + + /** + * Required when using REQUEST_CHANGES or COMMENT for the event parameter. The body text of the pull request review. + */ + public GHPullRequestReviewBuilder body(String body) { + builder.with("body",body); + return this; + } + + /** + * The review action you want to perform. The review actions include: APPROVE, REQUEST_CHANGES, or COMMENT. + * By leaving this blank, you set the review action state to PENDING, + * which means you will need to {@linkplain GHPullRequestReview#submit() submit the pull request review} when you are ready. + */ + public GHPullRequestReviewBuilder event(GHPullRequestReviewEvent event) { + builder.with("event",event.action()); + return this; + } + + /** + * @param body The relative path to the file that necessitates a review comment. + * @param path The position in the diff where you want to add a review comment. Note this value is not the same as the line number in the file. For help finding the position value, read the note below. + * @param position Text of the review comment. + */ + public GHPullRequestReviewBuilder comment(String body, String path, int position) { + comments.add(new DraftReviewComment(body,path,position)); + return this; + } + + public GHPullRequestReview create() throws IOException { + return builder.method("POST")._with("comments",comments) + .to(pr.getApiRoute() + "/reviews", GHPullRequestReview.class) + .wrapUp(pr); + } + + private static class DraftReviewComment { + private String body; + private String path; + private int position; + + 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/GHPullRequestReviewComment.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java index 33097bc29d..811e15a4fc 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.net.URL; +import javax.annotation.CheckForNull; import static org.kohsuke.github.Previews.*; @@ -41,8 +42,10 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable { private String body; private GHUser user; private String path; - private int position; - private int originalPosition; + private int position = -1; + private int original_position = -1; + private long in_reply_to_id = -1L; + public static GHPullRequestReviewComment draft(String body, String path, int position) { GHPullRequestReviewComment result = new GHPullRequestReviewComment(); @@ -82,12 +85,19 @@ public String getPath() { return path; } - public int getPosition() { - return position; + @CheckForNull + public Integer getPosition() { + return position == -1 ? null : position; + } + + @CheckForNull + public Integer getOriginalPosition() { + return original_position == -1 ? null : original_position; } - public int getOriginalPosition() { - return originalPosition; + @CheckForNull + public Long getInReplyToId() { + return in_reply_to_id == -1 ? null : in_reply_to_id; } @Override @@ -114,6 +124,17 @@ public void delete() throws IOException { new Requester(owner.root).method("DELETE").to(getApiRoute()); } + /** + * Create a new comment that replies to this comment. + */ + public GHPullRequestReviewComment reply(String body) throws IOException { + return new Requester(owner.root).method("POST") + .with("body", body) + .with("in_reply_to", getId()) + .to(getApiRoute() + "/comments", GHPullRequestReviewComment.class) + .wrapUp(owner); + } + @Preview @Deprecated public GHReaction createReaction(ReactionContent content) throws IOException { return new Requester(owner.root) @@ -126,7 +147,7 @@ public GHReaction createReaction(ReactionContent content) throws IOException { public PagedIterable listReactions() { return new PagedIterable() { public PagedIterator _iterator(int pageSize) { - return new PagedIterator(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute()+"/reactions", GHReaction[].class, pageSize)) { + return new PagedIterator(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute() + "/reactions", GHReaction[].class, pageSize)) { @Override protected void wrapUp(GHReaction[] page) { for (GHReaction c : page) diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewEvent.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewEvent.java new file mode 100644 index 0000000000..e6537e0f09 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewEvent.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (c) 2011, Eric Maupin + * + * 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; + +/** + * Action to perform on {@link GHPullRequestReview}. + */ +public enum GHPullRequestReviewEvent { + PENDING, + APPROVE, + REQUEST_CHANGES, + COMMENT; + + /*package*/ String action() { + return this==PENDING ? null : name(); + } + + /** + * When a {@link GHPullRequestReview} is submitted with this event, it should transition to this state. + */ + /*package*/ GHPullRequestReviewState toState() { + switch (this) { + case PENDING: return GHPullRequestReviewState.PENDING; + case APPROVE: return GHPullRequestReviewState.APPROVED; + case REQUEST_CHANGES: return GHPullRequestReviewState.CHANGES_REQUESTED; + case COMMENT: return GHPullRequestReviewState.COMMENTED; + } + throw new IllegalStateException(); + } +} diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java index fca3fabfb3..a64a105994 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewState.java @@ -1,19 +1,39 @@ package org.kohsuke.github; +/** + * Current state of {@link GHPullRequestReview} + */ public enum GHPullRequestReviewState { - PENDING(null), - APPROVED("APPROVE"), - REQUEST_CHANGES("REQUEST_CHANGES"), - COMMENTED("COMMENT"), - DISMISSED(null); + PENDING, + APPROVED, + CHANGES_REQUESTED, + /** + * @deprecated + * This was the thing when this API was in preview, but it changed when it became public. + * Use {@link #CHANGES_REQUESTED}. Left here for compatibility. + */ + REQUEST_CHANGES, + COMMENTED, + DISMISSED; - private final String _action; - - GHPullRequestReviewState(String action) { - _action = action; + /** + * @deprecated + * This was an internal method accidentally exposed. + * Left here for compatibility. + */ + public String action() { + GHPullRequestReviewEvent e = toEvent(); + return e==null ? null : e.action(); } - public String action() { - return _action; + /*package*/ GHPullRequestReviewEvent toEvent() { + switch (this) { + case PENDING: return GHPullRequestReviewEvent.PENDING; + case APPROVED: return GHPullRequestReviewEvent.APPROVE; + case CHANGES_REQUESTED: return GHPullRequestReviewEvent.REQUEST_CHANGES; + case REQUEST_CHANGES: return GHPullRequestReviewEvent.REQUEST_CHANGES; + case COMMENTED: return GHPullRequestReviewEvent.COMMENT; + } + return null; } } diff --git a/src/main/java/org/kohsuke/github/Previews.java b/src/main/java/org/kohsuke/github/Previews.java index 2bc6dccbc8..3f98c3d4b9 100644 --- a/src/main/java/org/kohsuke/github/Previews.java +++ b/src/main/java/org/kohsuke/github/Previews.java @@ -8,5 +8,4 @@ static final String DRAX = "application/vnd.github.drax-preview+json"; static final String SQUIRREL_GIRL = "application/vnd.github.squirrel-girl-preview"; static final String CLOAK = "application/vnd.github.cloak-preview"; - static final String BLACK_CAT = "application/vnd.github.black-cat-preview+json"; } diff --git a/src/main/java/org/kohsuke/github/Requester.java b/src/main/java/org/kohsuke/github/Requester.java index f26466bacf..899597b33e 100644 --- a/src/main/java/org/kohsuke/github/Requester.java +++ b/src/main/java/org/kohsuke/github/Requester.java @@ -135,6 +135,10 @@ public Requester with(String key, int value) { return _with(key, value); } + public Requester with(String key, long value) { + return _with(key, value); + } + public Requester with(String key, Integer value) { if (value!=null) _with(key, value); diff --git a/src/test/java/org/kohsuke/github/PullRequestTest.java b/src/test/java/org/kohsuke/github/PullRequestTest.java index 5ea2628303..c2a6a2705e 100644 --- a/src/test/java/org/kohsuke/github/PullRequestTest.java +++ b/src/test/java/org/kohsuke/github/PullRequestTest.java @@ -7,8 +7,7 @@ import java.util.Collection; import java.util.List; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.*; /** * @author Kohsuke Kawaguchi @@ -33,9 +32,10 @@ public void createPullRequestComment() throws Exception { 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) - ); + GHPullRequestReview draftReview = p.createReview() + .body("Some draft review") + .comment("Some niggle", "changelog.html", 1) + .create(); assertThat(draftReview.getState(), is(GHPullRequestReviewState.PENDING)); assertThat(draftReview.getBody(), is("Some draft review")); assertThat(draftReview.getCommitId(), notNullValue()); @@ -45,15 +45,16 @@ public void testPullRequestReviews() throws Exception { assertThat(review.getState(), is(GHPullRequestReviewState.PENDING)); assertThat(review.getBody(), is("Some draft review")); assertThat(review.getCommitId(), notNullValue()); - review.submit("Some review comment", GHPullRequestReviewState.COMMENTED); + draftReview.submit("Some review comment", GHPullRequestReviewEvent.COMMENT); 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(); + draftReview = p.createReview() + .body("Some new review") + .comment("Some niggle", "changelog.html", 1) + .create(); + draftReview.delete(); } @Test