From 65adb2f2b46ea0db20cd532aab9501f86e0ae8e2 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 28 Aug 2012 11:03:11 -0700 Subject: [PATCH 01/20] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7d5af32649..80feba2160 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ github-api - 1.31 + 1.32-SNAPSHOT GitHub API for Java http://github-api.kohsuke.org/ GitHub API for Java From 6aabaea96cca726bbd8b64fca24edec397191ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 12:37:00 +0200 Subject: [PATCH 02/20] Clean up GHIssue and GHPullRequest and make them relevant to api v3. Added SmallUser representing reference to user. Added DetailedPullRequest - when retrieving pull request by id, it has more attributes then pull requests obtained by GHRepository.getPullRequests() --- .../kohsuke/github/GHDetailedPullRequest.java | 77 +++++++++++++++++++ src/main/java/org/kohsuke/github/GHIssue.java | 69 ++++++++++++++++- .../org/kohsuke/github/GHPullRequest.java | 75 ++++++++++-------- .../java/org/kohsuke/github/GHSmallUser.java | 66 ++++++++++++++++ 4 files changed, 249 insertions(+), 38 deletions(-) create mode 100644 src/main/java/org/kohsuke/github/GHDetailedPullRequest.java create mode 100644 src/main/java/org/kohsuke/github/GHSmallUser.java diff --git a/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java b/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java new file mode 100644 index 0000000000..0fa4ebfc76 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java @@ -0,0 +1,77 @@ +/* + * The MIT License + * + * Copyright 2012 Honza Brázdil. + * + * 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; + +/** + * + * @author Honza Brázdil + */ +public class GHDetailedPullRequest extends GHPullRequest { + private GHSmallUser merged_by; + private int review_comments, additions; + private boolean merged; + private Boolean mergeable; + private int deletions; + private String mergeable_state; + private int changed_files; + + @Override + GHDetailedPullRequest wrapUp(GHRepository owner){ + super.wrapUp(owner); + if(merged_by != null) merged_by.wrapUp(root); + return this; + } + + public GHSmallUser getMerged_by() { + return merged_by; + } + + public int getReview_comments() { + return review_comments; + } + + public int getAdditions() { + return additions; + } + + public boolean isMerged() { + return merged; + } + + public Boolean getMergeable() { + return mergeable; + } + + public int getDeletions() { + return deletions; + } + + public String getMergeable_state() { + return mergeable_state; + } + + public int getChanged_files() { + return changed_files; + } +} diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index 3224e76f29..35eb9b9a6d 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -42,15 +42,29 @@ public class GHIssue { GitHub root; GHRepository owner; - - private String gravatar_id,body,title,state,created_at,updated_at,html_url; + + // API v3 + private GHSmallUser assignee; + private String state; + private int number; + private String closed_at; + private int comments; + private String body; private List labels; - private int number,votes,comments; - private int position; + private GHSmallUser user; + private String title, created_at, html_url; + private GHIssue.PullRequest pull_request; + // GHIssue milestone + private String url, updated_at; + private int id; + private GHSmallUser closed_by; /*package*/ GHIssue wrap(GHRepository owner) { this.owner = owner; this.root = owner.root; + if(assignee != null) assignee.wrapUp(root); + if(user != null) user.wrapUp(root); + if(closed_by != null) closed_by.wrapUp(root); return this; } @@ -112,6 +126,10 @@ public Date getUpdatedAt() { return GitHub.parseDate(updated_at); } + public Date getClosedAt() { + return GitHub.parseDate(closed_at); + } + /** * Updates the issue by adding a comment. */ @@ -182,4 +200,47 @@ protected void wrapUp(GHIssueComment[] page) { private String getApiRoute() { return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number; } + + public GHSmallUser getAssignee() { + return assignee; + } + + /** + * User who submitted the issue. + */ + public GHSmallUser getUser() { + return user; + } + + public GHSmallUser getClosedBy() { + if(!"closed".equals(state)) return null; + if(closed_by != null) return closed_by; + + //TODO closed_by = owner.getIssue(number).getClosed_by(); + return closed_by; + } + + public int getCommentsCount(){ + return comments; + } + + public PullRequest getPullRequest() { + return pull_request; + } + + public class PullRequest{ + private String diff_url, patch_url, html_url; + + public URL getDiffUrl() { + return GitHub.parseURL(diff_url); + } + + public URL getPatchUrl() { + return GitHub.parseURL(patch_url); + } + + public URL getUrl() { + return GitHub.parseURL(html_url); + } + } } \ No newline at end of file diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 1f86981e0f..86ae49f7dc 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -24,6 +24,7 @@ package org.kohsuke.github; import java.net.URL; +import java.util.Collection; import java.util.Date; /** @@ -33,12 +34,24 @@ */ @SuppressWarnings({"UnusedDeclaration"}) public class GHPullRequest extends GHIssue { - private String closed_at, patch_url, issue_updated_at; - private GHUser issue_user, user; - // labels?? - private GHCommitPointer base, head; - private String mergeable, diff_url; + + private String patch_url, diff_url, issue_url; + private GHCommitPointer base; + private String merged_at; + private GHCommitPointer head; + GHPullRequest wrapUp(GHRepository owner) { + this.wrap(owner); + return wrapUp(owner.root); + } + + GHPullRequest wrapUp(GitHub root) { + if (owner!=null) owner.wrap(root); + if (base!=null) base.wrapUp(root); + if (head!=null) head.wrapUp(root); + return this; + } + /** * The URL of the patch file. * like https://github.com/jenkinsci/jenkins/pull/100.patch @@ -46,12 +59,13 @@ public class GHPullRequest extends GHIssue { public URL getPatchUrl() { return GitHub.parseURL(patch_url); } - - /** - * User who submitted a pull request. + + /** + * The URL of the patch file. + * like https://github.com/jenkinsci/jenkins/pull/100.patch */ - public GHUser getUser() { - return user; + public URL getIssueUrl() { + return GitHub.parseURL(issue_url); } /** @@ -69,16 +83,9 @@ public GHCommitPointer getHead() { return head; } + @Deprecated public Date getIssueUpdatedAt() { - return GitHub.parseDate(issue_updated_at); - } - - /** - * The HTML page of this pull request, - * like https://github.com/jenkinsci/jenkins/pull/100 - */ - public URL getUrl() { - return super.getUrl(); + return super.getUpdatedAt(); } /** @@ -89,22 +96,22 @@ public URL getDiffUrl() { return GitHub.parseURL(diff_url); } - public Date getClosedAt() { - return GitHub.parseDate(closed_at); + public Date getMergedAt() { + return GitHub.parseDate(merged_at); } - GHPullRequest wrapUp(GHRepository owner) { - this.owner = owner; - return wrapUp(owner.root); - } + @Override + public Collection getLabels() { + return super.getLabels(); + } - GHPullRequest wrapUp(GitHub root) { - this.root = root; - if (owner!=null) owner.wrap(root); - if (issue_user!=null) issue_user.root=root; - if (user!=null) user.root=root; - if (base!=null) base.wrapUp(root); - if (head!=null) head.wrapUp(root); - return this; - } + @Override + public GHSmallUser getClosedBy() { + return null; + } + + @Override + public PullRequest getPullRequest() { + return null; + } } diff --git a/src/main/java/org/kohsuke/github/GHSmallUser.java b/src/main/java/org/kohsuke/github/GHSmallUser.java new file mode 100644 index 0000000000..5725bf4063 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHSmallUser.java @@ -0,0 +1,66 @@ +/* + * The MIT License + * + * Copyright 2012 Honza Brázdil. + * + * 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; + +/** + * + * @author Honza Brázdil + */ +public class GHSmallUser { + private GitHub root; + private String avatar_url, login, url, gravatar_id; + private Long id; + + /*package*/ GHSmallUser wrapUp(GitHub root) { + this.root = root; + return this; + } + + + public String getAvatar_url() { + return avatar_url; + } + + public String getLogin() { + return login; + } + + public String getUrl() { + return url; + } + + public String getGravatar_id() { + return gravatar_id; + } + + public Long getId() { + return id; + } + + public GHUser getUser() throws IOException{ + return root.getUser(login); + } +} From c283c4e595ceafd3fc08cd3bf2a5b7fee027acaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 14:23:48 +0200 Subject: [PATCH 03/20] added method retrieving detailed pull request --- src/main/java/org/kohsuke/github/GHDetailedPullRequest.java | 5 +++++ src/main/java/org/kohsuke/github/GHPullRequest.java | 5 +++++ src/main/java/org/kohsuke/github/GHRepository.java | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java b/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java index 0fa4ebfc76..5a0ca156cf 100644 --- a/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java @@ -74,4 +74,9 @@ public String getMergeable_state() { public int getChanged_files() { return changed_files; } + + @Override + public GHDetailedPullRequest getDetailedPullRequest() { + return this; + } } diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 86ae49f7dc..94699c97a3 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -23,6 +23,7 @@ */ package org.kohsuke.github; +import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.Date; @@ -114,4 +115,8 @@ public GHSmallUser getClosedBy() { public PullRequest getPullRequest() { return null; } + + public GHDetailedPullRequest getDetailedPullRequest() throws IOException{ + return (GHDetailedPullRequest) owner.getPullRequest(this.getNumber()); + } } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 15877e2bd7..5fa3cc0b37 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -353,7 +353,7 @@ public GHRepository forkTo(GHOrganization org) throws IOException { * Retrieves a specified pull request. */ public GHPullRequest getPullRequest(int i) throws IOException { - return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this); + return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHDetailedPullRequest.class).wrapUp(this); } /** From b40677a3ca5fe8f614c55e53ee12d1305e227352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 14:30:00 +0200 Subject: [PATCH 04/20] changed return value of GHRepository.getPullRequest() I'm not sure whether this influence something or not, but I think it should be OK. --- src/main/java/org/kohsuke/github/GHPullRequest.java | 2 +- src/main/java/org/kohsuke/github/GHRepository.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 94699c97a3..9611175734 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -117,6 +117,6 @@ public PullRequest getPullRequest() { } public GHDetailedPullRequest getDetailedPullRequest() throws IOException{ - return (GHDetailedPullRequest) owner.getPullRequest(this.getNumber()); + return owner.getPullRequest(this.getNumber()); } } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 5fa3cc0b37..12b3d3c883 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -352,7 +352,7 @@ public GHRepository forkTo(GHOrganization org) throws IOException { /** * Retrieves a specified pull request. */ - public GHPullRequest getPullRequest(int i) throws IOException { + public GHDetailedPullRequest getPullRequest(int i) throws IOException { return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHDetailedPullRequest.class).wrapUp(this); } From ce47762fbffa654902dd2cae70084e9d3259b493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 14:37:02 +0200 Subject: [PATCH 05/20] Added milestone attribute to GHIssue --- src/main/java/org/kohsuke/github/GHIssue.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index 35eb9b9a6d..fa748203e4 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -54,7 +54,7 @@ public class GHIssue { private GHSmallUser user; private String title, created_at, html_url; private GHIssue.PullRequest pull_request; - // GHIssue milestone + private GHMilestone milestone; private String url, updated_at; private int id; private GHSmallUser closed_by; @@ -62,6 +62,7 @@ public class GHIssue { /*package*/ GHIssue wrap(GHRepository owner) { this.owner = owner; this.root = owner.root; + if(milestone != null) milestone.wrap(owner); if(assignee != null) assignee.wrapUp(root); if(user != null) user.wrapUp(root); if(closed_by != null) closed_by.wrapUp(root); @@ -226,8 +227,12 @@ public int getCommentsCount(){ public PullRequest getPullRequest() { return pull_request; - } - + } + + public GHMilestone getMilestone() { + return milestone; + } + public class PullRequest{ private String diff_url, patch_url, html_url; From 9017fe70d58b55ddd268daf688710176dc1b6c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 14:50:08 +0200 Subject: [PATCH 06/20] We need to preserve api --- src/main/java/org/kohsuke/github/GHIssue.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index fa748203e4..dee5aa0fbb 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.net.URL; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -208,8 +207,23 @@ public GHSmallUser getAssignee() { /** * User who submitted the issue. + * + * @return May return null when IOException occures. Prefered way is getSmallUser().getUser(). + * @see #getSmallUser() + */ + @Deprecated + public GHUser getUser() { + try { + return user.getUser(); + } catch (IOException ex) { + return null; + } + } + + /** + * Shallow user who submitted the issue. */ - public GHSmallUser getUser() { + public GHSmallUser getSmallUser(){ return user; } From 803198620d240a92e0a5dae1c8849ddb9e574d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 15:24:14 +0200 Subject: [PATCH 07/20] Nested GHIssue.PullRequest must be static --- src/main/java/org/kohsuke/github/GHIssue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index dee5aa0fbb..7a5007374c 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -247,7 +247,7 @@ public GHMilestone getMilestone() { return milestone; } - public class PullRequest{ + public static class PullRequest{ private String diff_url, patch_url, html_url; public URL getDiffUrl() { From fff3272e422414f82eab869fbed4cc763fe0f450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 15:27:06 +0200 Subject: [PATCH 08/20] make url methods return URL and add getApiUrl --- src/main/java/org/kohsuke/github/GHIssue.java | 4 ++++ src/main/java/org/kohsuke/github/GHSmallUser.java | 15 ++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index 7a5007374c..11479d6bdb 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -130,6 +130,10 @@ public Date getClosedAt() { return GitHub.parseDate(closed_at); } + public URL getApiURL(){ + return GitHub.parseURL(url); + } + /** * Updates the issue by adding a comment. */ diff --git a/src/main/java/org/kohsuke/github/GHSmallUser.java b/src/main/java/org/kohsuke/github/GHSmallUser.java index 5725bf4063..6c52b2ae12 100644 --- a/src/main/java/org/kohsuke/github/GHSmallUser.java +++ b/src/main/java/org/kohsuke/github/GHSmallUser.java @@ -24,6 +24,7 @@ package org.kohsuke.github; import java.io.IOException; +import java.net.URL; /** * @@ -32,7 +33,7 @@ public class GHSmallUser { private GitHub root; private String avatar_url, login, url, gravatar_id; - private Long id; + private int id; /*package*/ GHSmallUser wrapUp(GitHub root) { this.root = root; @@ -40,25 +41,21 @@ public class GHSmallUser { } - public String getAvatar_url() { - return avatar_url; + public URL getAvatar_url() { + return GitHub.parseURL(avatar_url); } public String getLogin() { return login; } - public String getUrl() { - return url; + public URL getApiUrl() { + return GitHub.parseURL(url); } public String getGravatar_id() { return gravatar_id; } - - public Long getId() { - return id; - } public GHUser getUser() throws IOException{ return root.getUser(login); From 4e27d1b5a0420c59c6786da4c92bc042713950dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Br=C3=A1zdil?= Date: Fri, 31 Aug 2012 15:31:41 +0200 Subject: [PATCH 09/20] method names should by camelCase --- src/main/java/org/kohsuke/github/GHSmallUser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHSmallUser.java b/src/main/java/org/kohsuke/github/GHSmallUser.java index 6c52b2ae12..ca7a1b8523 100644 --- a/src/main/java/org/kohsuke/github/GHSmallUser.java +++ b/src/main/java/org/kohsuke/github/GHSmallUser.java @@ -41,7 +41,7 @@ public class GHSmallUser { } - public URL getAvatar_url() { + public URL getAvatarUrl() { return GitHub.parseURL(avatar_url); } @@ -53,7 +53,7 @@ public URL getApiUrl() { return GitHub.parseURL(url); } - public String getGravatar_id() { + public String getGravatarId() { return gravatar_id; } From 892d2acaa2ca737223c13b2c22705041eeca3917 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 4 Sep 2012 10:40:32 -0700 Subject: [PATCH 10/20] Added the commit status API, first cut. --- .../java/org/kohsuke/github/GHCommit.java | 7 ++ .../org/kohsuke/github/GHCommitState.java | 11 +++ .../org/kohsuke/github/GHCommitStatus.java | 85 +++++++++++++++++++ .../java/org/kohsuke/github/GHRepository.java | 22 +++++ 4 files changed, 125 insertions(+) create mode 100644 src/main/java/org/kohsuke/github/GHCommitState.java create mode 100644 src/main/java/org/kohsuke/github/GHCommitStatus.java diff --git a/src/main/java/org/kohsuke/github/GHCommit.java b/src/main/java/org/kohsuke/github/GHCommit.java index 3cf7f1437c..919160f4e4 100644 --- a/src/main/java/org/kohsuke/github/GHCommit.java +++ b/src/main/java/org/kohsuke/github/GHCommit.java @@ -234,6 +234,13 @@ public GHCommitComment createComment(String body) throws IOException { return createComment(body,null,null,null); } + /** + * Gets the status of this commit. + */ + public GHCommitStatus getStatus() throws IOException { + return owner.getCommitStatus(sha); + } + GHCommit wrapUp(GHRepository owner) { this.owner = owner; return this; diff --git a/src/main/java/org/kohsuke/github/GHCommitState.java b/src/main/java/org/kohsuke/github/GHCommitState.java new file mode 100644 index 0000000000..e716ea02e1 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHCommitState.java @@ -0,0 +1,11 @@ +package org.kohsuke.github; + +/** + * Represents the state of commit + * + * @author Kohsuke Kawaguchi + * @see GHCommitStatus + */ +public enum GHCommitState { + PENDING, SUCCESS, ERROR, FAILURE +} diff --git a/src/main/java/org/kohsuke/github/GHCommitStatus.java b/src/main/java/org/kohsuke/github/GHCommitStatus.java new file mode 100644 index 0000000000..5f56ffeb6f --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHCommitStatus.java @@ -0,0 +1,85 @@ +package org.kohsuke.github; + +import java.io.IOException; +import java.util.Date; + +/** + * Represents a status of a commit. + * + * @author Kohsuke Kawaguchi + * @see GHRepository#getCommitStatus(String) + * @see GHCommit#getStatus() + */ +public class GHCommitStatus { + String created_at, updated_at; + String state; + String target_url,description; + int id; + String url; + GHUser creator; + + private GitHub root; + + /*package*/ GHCommitStatus wrapUp(GitHub root) { + if (creator!=null) creator.wrapUp(root); + this.root = root; + return this; + } + + public Date getCreatedAt() { + return GitHub.parseDate(created_at); + } + + public Date getUpdatedAt() { + return GitHub.parseDate(updated_at); + } + + public GHCommitState getState() { + for (GHCommitState s : GHCommitState.values()) { + if (s.name().equalsIgnoreCase(state)) + return s; + } + throw new IllegalStateException("Unexpected state: "+state); + } + + public String getTargetUrl() { + return target_url; + } + + public String getDescription() { + return description; + } + + public int getId() { + return id; + } + + /** + * API URL of this commit status. + */ + public String getUrl() { + return url; + } + + public GHUser getCreator() { + return creator; + } + + /** + * Updates the description. + * + * TODO: verify if this actually works, and create setTargetUrl, too. + */ + public void setDescription(String description) throws IOException { + new Poster(root) + .with("description",description) + .withCredential() + .to(url,null,"PATCH"); + this.description = description; + } + + // TODO: verify if it works + public void delete() throws IOException { + new Poster(root).withCredential().to(url,null,"DELETE"); + } +} diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 15877e2bd7..5c73b7eca3 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -442,6 +442,28 @@ protected void wrapUp(GHCommitComment[] page) { }; } + public GHCommitStatus getCommitStatus(String sha1) throws IOException { + return root.retrieve(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1), GHCommitStatus.class).wrapUp(root); + } + + /** + * Creates a commit status + * + * @param targetUrl + * Optional parameter that points to the URL that has more details. + * @param description + * Optional short description. + */ + public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException { + return new Poster(root) + .withCredential() + .with("state",state.name().toLowerCase(Locale.ENGLISH)) + .with("target_url", targetUrl) + .with("description", description) + .to(String.format("/repos/%s/%s/statuses/%s",owner.login,this.name,sha1),GHCommitStatus.class).wrapUp(root); + } + + /** * * See https://api.github.com/hooks for possible names and their configuration scheme. From 3f1bb1a2144ebb19936785744b2ad5d4c988b3ff Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 18:00:50 -0700 Subject: [PATCH 11/20] completed the commit status API --- .../java/org/kohsuke/github/GHCommit.java | 13 +++++++--- .../org/kohsuke/github/GHCommitStatus.java | 23 ++++------------- .../java/org/kohsuke/github/GHRepository.java | 25 +++++++++++++++++-- src/main/java/org/kohsuke/github/GitHub.java | 7 ++++-- src/test/java/org/kohsuke/AppTest.java | 17 ++++++++++++- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHCommit.java b/src/main/java/org/kohsuke/github/GHCommit.java index 919160f4e4..f210f00008 100644 --- a/src/main/java/org/kohsuke/github/GHCommit.java +++ b/src/main/java/org/kohsuke/github/GHCommit.java @@ -235,10 +235,17 @@ public GHCommitComment createComment(String body) throws IOException { } /** - * Gets the status of this commit. + * Gets the status of this commit, newer ones first. */ - public GHCommitStatus getStatus() throws IOException { - return owner.getCommitStatus(sha); + public PagedIterable listStatuses() throws IOException { + return owner.listCommitStatuses(sha); + } + + /** + * Gets the last status of this commit, which is what gets shown in the UI. + */ + public GHCommitStatus getLastStatus() throws IOException { + return owner.getLastCommitStatus(sha); } GHCommit wrapUp(GHRepository owner) { diff --git a/src/main/java/org/kohsuke/github/GHCommitStatus.java b/src/main/java/org/kohsuke/github/GHCommitStatus.java index 5f56ffeb6f..319e13cc64 100644 --- a/src/main/java/org/kohsuke/github/GHCommitStatus.java +++ b/src/main/java/org/kohsuke/github/GHCommitStatus.java @@ -42,6 +42,11 @@ public GHCommitState getState() { throw new IllegalStateException("Unexpected state: "+state); } + /** + * The URL that this status is linked to. + * + * This is the URL specified when creating a commit status. + */ public String getTargetUrl() { return target_url; } @@ -64,22 +69,4 @@ public String getUrl() { public GHUser getCreator() { return creator; } - - /** - * Updates the description. - * - * TODO: verify if this actually works, and create setTargetUrl, too. - */ - public void setDescription(String description) throws IOException { - new Poster(root) - .with("description",description) - .withCredential() - .to(url,null,"PATCH"); - this.description = description; - } - - // TODO: verify if it works - public void delete() throws IOException { - new Poster(root).withCredential().to(url,null,"DELETE"); - } } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 5c73b7eca3..a84840a93f 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -442,8 +442,29 @@ protected void wrapUp(GHCommitComment[] page) { }; } - public GHCommitStatus getCommitStatus(String sha1) throws IOException { - return root.retrieve(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1), GHCommitStatus.class).wrapUp(root); + /** + * Lists all the commit statues attached to the given commit, newer ones first. + */ + public PagedIterable listCommitStatuses(final String sha1) throws IOException { + return new PagedIterable() { + public PagedIterator iterator() { + return new PagedIterator(root.retrievePaged(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1),GHCommitStatus[].class,false)) { + @Override + protected void wrapUp(GHCommitStatus[] page) { + for (GHCommitStatus c : page) + c.wrapUp(root); + } + }; + } + }; + } + + /** + * Gets the last status of this commit, which is what gets shown in the UI. + */ + public GHCommitStatus getLastCommitStatus(String sha1) throws IOException { + List v = listCommitStatuses(sha1).asList(); + return v.isEmpty() ? null : v.get(0); } /** diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 5893de9c41..f63cc5788f 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -163,8 +163,11 @@ public static GitHub connectAnonymously() { // append the access token tailApiUrl = tailApiUrl + (tailApiUrl.indexOf('?')>=0 ?'&':'?') + "access_token=" + oauthAccessToken; } - - return new URL("https://api."+githubServer+tailApiUrl); + + if (tailApiUrl.startsWith("/")) + return new URL("https://api."+githubServer+tailApiUrl); + else + return new URL(tailApiUrl); } /*package*/ T retrieve(String tailApiUrl, Class type) throws IOException { diff --git a/src/test/java/org/kohsuke/AppTest.java b/src/test/java/org/kohsuke/AppTest.java index 1b93cee1d1..8163d44be8 100644 --- a/src/test/java/org/kohsuke/AppTest.java +++ b/src/test/java/org/kohsuke/AppTest.java @@ -4,6 +4,8 @@ import org.kohsuke.github.GHCommit; import org.kohsuke.github.GHCommit.File; import org.kohsuke.github.GHCommitComment; +import org.kohsuke.github.GHCommitState; +import org.kohsuke.github.GHCommitStatus; import org.kohsuke.github.GHEvent; import org.kohsuke.github.GHEventInfo; import org.kohsuke.github.GHEventPayload; @@ -45,7 +47,7 @@ public void testRepoCRUD() throws Exception { public void testCredentialValid() throws IOException { assertTrue(GitHub.connect().isCredentialValid()); - assertFalse(GitHub.connect("totally","bogus").isCredentialValid()); + assertFalse(GitHub.connect("totally", "bogus").isCredentialValid()); } public void testRateLimit() throws IOException { @@ -308,4 +310,17 @@ public void testOrganization() throws IOException { // t.add(labs.getRepository("xyz")); } + + public void testCommitStatus() throws Exception { + GitHub gitHub = GitHub.connect(); + GHRepository r = gitHub.getUser("kohsuke").getRepository("test"); + GHCommitStatus state; +// state = r.createCommitStatus("edacdd76b06c5f3f0697a22ca75803169f25f296", GHCommitState.FAILURE, "http://jenkins-ci.org/", "oops!"); + + List lst = r.listCommitStatuses("edacdd76b06c5f3f0697a22ca75803169f25f296").asList(); + state = lst.get(0); + System.out.println(state); + assertEquals("oops!",state.getDescription()); + assertEquals("http://jenkins-ci.org/",state.getTargetUrl()); + } } From f58dbceec7f23498ef00cb1622b134f8fe5386ab Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 18:15:29 -0700 Subject: [PATCH 12/20] Massaging the pull request. In various places of the GitHub API, there's often a full object and then there's a shallow object. I think the client API would be a lot easier to use if a single class represents both and retrieve additional fields on the fly as needed. --- .../kohsuke/github/GHDetailedPullRequest.java | 82 ------------------- .../org/kohsuke/github/GHPullRequest.java | 64 ++++++++++++++- .../java/org/kohsuke/github/GHRepository.java | 4 +- .../java/org/kohsuke/github/GHSmallUser.java | 2 + src/main/java/org/kohsuke/github/GHUser.java | 1 + 5 files changed, 65 insertions(+), 88 deletions(-) delete mode 100644 src/main/java/org/kohsuke/github/GHDetailedPullRequest.java diff --git a/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java b/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java deleted file mode 100644 index 5a0ca156cf..0000000000 --- a/src/main/java/org/kohsuke/github/GHDetailedPullRequest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * The MIT License - * - * Copyright 2012 Honza Brázdil. - * - * 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; - -/** - * - * @author Honza Brázdil - */ -public class GHDetailedPullRequest extends GHPullRequest { - private GHSmallUser merged_by; - private int review_comments, additions; - private boolean merged; - private Boolean mergeable; - private int deletions; - private String mergeable_state; - private int changed_files; - - @Override - GHDetailedPullRequest wrapUp(GHRepository owner){ - super.wrapUp(owner); - if(merged_by != null) merged_by.wrapUp(root); - return this; - } - - public GHSmallUser getMerged_by() { - return merged_by; - } - - public int getReview_comments() { - return review_comments; - } - - public int getAdditions() { - return additions; - } - - public boolean isMerged() { - return merged; - } - - public Boolean getMergeable() { - return mergeable; - } - - public int getDeletions() { - return deletions; - } - - public String getMergeable_state() { - return mergeable_state; - } - - public int getChanged_files() { - return changed_files; - } - - @Override - public GHDetailedPullRequest getDetailedPullRequest() { - return this; - } -} diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 9611175734..e78a88df5d 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -23,7 +23,6 @@ */ package org.kohsuke.github; -import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.Date; @@ -41,6 +40,16 @@ public class GHPullRequest extends GHIssue { private String merged_at; private GHCommitPointer head; + // details that are only available when obtained from ID + private GHSmallUser merged_by; + private int review_comments, additions; + private boolean merged; + private Boolean mergeable; + private int deletions; + private String mergeable_state; + private int changed_files; + + GHPullRequest wrapUp(GHRepository owner) { this.wrap(owner); return wrapUp(owner.root); @@ -50,6 +59,7 @@ GHPullRequest wrapUp(GitHub root) { if (owner!=null) owner.wrap(root); if (base!=null) base.wrapUp(root); if (head!=null) head.wrapUp(root); + if (merged_by != null) merged_by.wrapUp(root); return this; } @@ -116,7 +126,53 @@ public PullRequest getPullRequest() { return null; } - public GHDetailedPullRequest getDetailedPullRequest() throws IOException{ - return owner.getPullRequest(this.getNumber()); - } +// +// details that are only available via get with ID +// +// + public GHSmallUser getMergedBy() { + populate(); + return merged_by; + } + + public int getReviewComments() { + populate(); + return review_comments; + } + + public int getAdditions() { + populate(); + return additions; + } + + public boolean isMerged() { + populate(); + return merged; + } + + public Boolean getMergeable() { + populate(); + return mergeable; + } + + public int getDeletions() { + populate(); + return deletions; + } + + public String getMergeableState() { + populate(); + return mergeable_state; + } + + public int getChangedFiles() { + populate(); + return changed_files; + } + + private void populate() { + if (merged_by!=null) return; // already populated + + root.retrieveWithAuth(getUrl(), this); + } } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 12b3d3c883..15877e2bd7 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -352,8 +352,8 @@ public GHRepository forkTo(GHOrganization org) throws IOException { /** * Retrieves a specified pull request. */ - public GHDetailedPullRequest getPullRequest(int i) throws IOException { - return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHDetailedPullRequest.class).wrapUp(this); + public GHPullRequest getPullRequest(int i) throws IOException { + return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this); } /** diff --git a/src/main/java/org/kohsuke/github/GHSmallUser.java b/src/main/java/org/kohsuke/github/GHSmallUser.java index ca7a1b8523..53e256ecef 100644 --- a/src/main/java/org/kohsuke/github/GHSmallUser.java +++ b/src/main/java/org/kohsuke/github/GHSmallUser.java @@ -27,8 +27,10 @@ import java.net.URL; /** + * This represents a subset of user information that often appear as a part of a bigger data graph in the GitHub API. * * @author Honza Brázdil + * @see GHUser */ public class GHSmallUser { private GitHub root; diff --git a/src/main/java/org/kohsuke/github/GHUser.java b/src/main/java/org/kohsuke/github/GHUser.java index 07f44f71c4..8a0495cfa5 100644 --- a/src/main/java/org/kohsuke/github/GHUser.java +++ b/src/main/java/org/kohsuke/github/GHUser.java @@ -34,6 +34,7 @@ * Represents an user of GitHub. * * @author Kohsuke Kawaguchi + * @see GHSmallUser */ public class GHUser extends GHPerson { From d6d73f516563970b63c432351b17dd2540153ddf Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 18:52:37 -0700 Subject: [PATCH 13/20] A step toward using Poster in place of the retrieve* methods. I was trying to add the flavor of the retrieve method that reads into an existing instance, when I realized that there are just too many orthogonal axes here to rely on overloaded methods. That calls for a builder pattern, which we already have --- it's called Poster, but it can actually already handle GET and other HTTP requests. So I'm retiring the retrieveXYZ methods and moving the code into Poster. This is the first step. --- pom.xml | 2 +- .../org/kohsuke/github/GHCommitComment.java | 7 +- src/main/java/org/kohsuke/github/GHHook.java | 3 +- src/main/java/org/kohsuke/github/GHIssue.java | 6 +- .../org/kohsuke/github/GHOrganization.java | 2 +- .../java/org/kohsuke/github/GHRepository.java | 12 +- src/main/java/org/kohsuke/github/GHUser.java | 4 +- src/main/java/org/kohsuke/github/GitHub.java | 11 +- src/main/java/org/kohsuke/github/Poster.java | 116 +++++++++++++----- src/test/java/org/kohsuke/AppTest.java | 2 +- 10 files changed, 106 insertions(+), 59 deletions(-) diff --git a/pom.xml b/pom.xml index 80feba2160..7cf0c6bc02 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ org.codehaus.jackson jackson-mapper-asl - 1.5.0 + 1.9.9 commons-io diff --git a/src/main/java/org/kohsuke/github/GHCommitComment.java b/src/main/java/org/kohsuke/github/GHCommitComment.java index f4621c2b01..44d7983422 100644 --- a/src/main/java/org/kohsuke/github/GHCommitComment.java +++ b/src/main/java/org/kohsuke/github/GHCommitComment.java @@ -98,9 +98,8 @@ public GHCommit getCommit() throws IOException { */ public void update(String body) throws IOException { GHCommitComment r = new Poster(owner.root) - .with("body",body) - .withCredential() - .to(getApiTail(),GHCommitComment.class,"PATCH"); + .with("body", body) + .withCredential().method("PATCH").to(getApiTail(), GHCommitComment.class); this.body = body; } @@ -108,7 +107,7 @@ public void update(String body) throws IOException { * Deletes this comment. */ public void delete() throws IOException { - new Poster(owner.root).withCredential().to(getApiTail(),null,"DELETE"); + new Poster(owner.root).withCredential().method("DELETE").to(getApiTail()); } private String getApiTail() { diff --git a/src/main/java/org/kohsuke/github/GHHook.java b/src/main/java/org/kohsuke/github/GHHook.java index 94144e6f07..89e7020e77 100644 --- a/src/main/java/org/kohsuke/github/GHHook.java +++ b/src/main/java/org/kohsuke/github/GHHook.java @@ -54,7 +54,6 @@ public int getId() { * Deletes this hook. */ public void delete() throws IOException { - new Poster(repository.root).withCredential() - .to(String.format("/repos/%s/%s/hooks/%d",repository.getOwnerName(),repository.getName(),id),null,"DELETE"); + new Poster(repository.root).withCredential().method("DELETE").to(String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), id)); } } diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index 3224e76f29..e0d7976b71 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.net.URL; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -116,12 +115,11 @@ public Date getUpdatedAt() { * Updates the issue by adding a comment. */ public void comment(String message) throws IOException { - new Poster(root).withCredential().with("body",message).to(getApiRoute()+"/comments",null,"POST"); + new Poster(root).withCredential().with("body",message).to(getApiRoute() + "/comments"); } private void edit(String key, Object value) throws IOException { - new Poster(root).withCredential()._with(key, value) - .to(getApiRoute(),null,"PATCH"); + new Poster(root).withCredential()._with(key, value).method("PATCH").to(getApiRoute()); } /** diff --git a/src/main/java/org/kohsuke/github/GHOrganization.java b/src/main/java/org/kohsuke/github/GHOrganization.java index f15626370c..21d50102c7 100644 --- a/src/main/java/org/kohsuke/github/GHOrganization.java +++ b/src/main/java/org/kohsuke/github/GHOrganization.java @@ -101,7 +101,7 @@ public GHTeam createTeam(String name, Permission p, Collection rep repo_names.add(r.getName()); } post.with("repo_names",repo_names); - return post.to("/orgs/"+login+"/teams",GHTeam.class,"POST").wrapUp(this); + return post.method("POST").to("/orgs/" + login + "/teams", GHTeam.class).wrapUp(this); } public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException { diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index a84840a93f..0106deab4f 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -253,7 +253,7 @@ public void removeCollaborators(Collection users) throws IOException { private void modifyCollaborators(Collection users, String method) throws IOException { verifyMine(); for (GHUser user : users) { - new Poster(root).withCredential().to("/repos/"+owner.login+"/"+name+"/collaborators/"+user.getLogin(),null,method); + new Poster(root).withCredential().method(method).to("/repos/" + owner.login + "/" + name + "/collaborators/" + user.getLogin()); } } @@ -273,8 +273,7 @@ private void edit(String key, String value) throws IOException { Poster poster = new Poster(root).withCredential(); if (!key.equals("name")) poster.with("name", name); // even when we don't change the name, we need to send it in - poster.with(key, value) - .to("/repos/" + owner.login + "/" + name, null, "PATCH"); + poster.with(key, value).method("PATCH").to("/repos/" + owner.login + "/" + name); } /** @@ -314,7 +313,7 @@ public void setHomepage(String value) throws IOException { * Deletes this repository. */ public void delete() throws IOException { - new Poster(root).withCredential().to("/repos/" + owner.login +"/"+name, null, "DELETE"); + new Poster(root).withCredential().method("DELETE").to("/repos/" + owner.login + "/" + name); } /** @@ -324,7 +323,7 @@ public void delete() throws IOException { * Newly forked repository that belong to you. */ public GHRepository fork() throws IOException { - return new Poster(root).withCredential().to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class, "POST").wrap(root); + return new Poster(root).withCredential().method("POST").to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class).wrap(root); } /** @@ -642,8 +641,7 @@ public GHMilestone getMilestone(int number) throws IOException { public GHMilestone createMilestone(String title, String description) throws IOException { return new Poster(root).withCredential() - .with("title", title).with("description", description) - .to("/repos/"+owner.login+"/"+name+"/milestones", GHMilestone.class,"POST").wrap(this); + .with("title", title).with("description", description).method("POST").to("/repos/" + owner.login + "/" + name + "/milestones", GHMilestone.class).wrap(this); } @Override diff --git a/src/main/java/org/kohsuke/github/GHUser.java b/src/main/java/org/kohsuke/github/GHUser.java index 07f44f71c4..373a867c24 100644 --- a/src/main/java/org/kohsuke/github/GHUser.java +++ b/src/main/java/org/kohsuke/github/GHUser.java @@ -41,14 +41,14 @@ public class GHUser extends GHPerson { * Follow this user. */ public void follow() throws IOException { - new Poster(root).withCredential().to("/user/following/"+login,null,"PUT"); + new Poster(root).withCredential().method("PUT").to("/user/following/" + login); } /** * Unfollow this user. */ public void unfollow() throws IOException { - new Poster(root).withCredential().to("/user/following/"+login,null,"DELETE"); + new Poster(root).withCredential().method("DELETE").to("/user/following/" + login); } /** diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index f63cc5788f..b0d2b60f2c 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -170,6 +170,10 @@ public static GitHub connectAnonymously() { return new URL(tailApiUrl); } + /*package*/ Poster retrieve() { + return new Poster(this).method("GET"); + } + /*package*/ T retrieve(String tailApiUrl, Class type) throws IOException { return _retrieve(tailApiUrl, type, "GET", false); } @@ -379,7 +383,7 @@ public GHMyself getMyself() throws IOException { public GHUser getUser(String login) throws IOException { GHUser u = users.get(login); if (u == null) { - u = retrieve("/users/" + login, GHUser.class); + u = retrieve().to("/users/" + login, GHUser.class); u.root = this; users.put(u.getLogin(), u); } @@ -465,9 +469,10 @@ public T parseEventPayload(Reader r, Class type) t * Newly created repository. */ public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException { - return new Poster(this).withCredential() + Poster poster = new Poster(this).withCredential() .with("name", name).with("description", description).with("homepage", homepage) - .with("public", isPublic ? 1 : 0).to("/user/repos", GHRepository.class,"POST").wrap(this); + .with("public", isPublic ? 1 : 0); + return poster.method("POST").to("/user/repos", GHRepository.class).wrap(this); } /** diff --git a/src/main/java/org/kohsuke/github/Poster.java b/src/main/java/org/kohsuke/github/Poster.java index d18c054b62..886947a90b 100644 --- a/src/main/java/org/kohsuke/github/Poster.java +++ b/src/main/java/org/kohsuke/github/Poster.java @@ -26,6 +26,7 @@ import org.apache.commons.io.IOUtils; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Field; @@ -36,11 +37,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.zip.GZIPInputStream; import static org.kohsuke.github.GitHub.*; /** - * Handles HTTP POST. + * A builder pattern for making HTTP call and parsing its output. + * * @author Kohsuke Kawaguchi */ class Poster { @@ -48,6 +51,11 @@ class Poster { private final List args = new ArrayList(); private boolean authenticate; + /** + * Request method. + */ + private String method = "POST"; + private static class Entry { String key; Object value; @@ -62,8 +70,12 @@ private Entry(String key, Object value) { this.root = root; } + /** + * Makes a request with authentication credential. + */ public Poster withCredential() { - root.requireCredential(); + // keeping it inline with retrieveWithAuth not to enforce the check + // root.requireCredential(); authenticate = true; return this; } @@ -97,12 +109,17 @@ public Poster _with(String key, Object value) { return this; } + public Poster method(String method) { + this.method = method; + return this; + } + public void to(String tailApiUrl) throws IOException { to(tailApiUrl,null); } /** - * POSTs the form to the specified URL. + * Sends a request to the specified URL, and parses the response into the given type via databinding. * * @throws IOException * if the server returns 4xx/5xx responses. @@ -110,54 +127,85 @@ public void to(String tailApiUrl) throws IOException { * {@link Reader} that reads the response. */ public T to(String tailApiUrl, Class type) throws IOException { - return to(tailApiUrl,type,"POST"); + return _to(tailApiUrl, type, null); } + /** + * Like {@link #to(String, Class)} but updates an existing object instead of creating a new instance. + */ + public T to(String tailApiUrl, T existingInstance) throws IOException { + return _to(tailApiUrl, null, existingInstance); + } + + /** + * Short for {@code method(method).to(tailApiUrl,type)} + */ + @Deprecated public T to(String tailApiUrl, Class type, String method) throws IOException { + return method(method).to(tailApiUrl,type); + } + + private T _to(String tailApiUrl, Class type, T instance) throws IOException { while (true) {// loop while API rate limit is hit HttpURLConnection uc = (HttpURLConnection) root.getApiURL(tailApiUrl).openConnection(); - - uc.setDoOutput(true); - uc.setRequestProperty("Content-type","application/x-www-form-urlencoded"); - if (authenticate) { - if (root.oauthAccessToken!=null) { - uc.setRequestProperty("Authorization", "token " + root.oauthAccessToken); - } else { - if (root.password==null) - throw new IllegalArgumentException("V3 API doesn't support API token"); - uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization); + uc.setRequestProperty("Accept-Encoding", "gzip"); + + if (!method.equals("GET")) { + uc.setDoOutput(true); + uc.setRequestProperty("Content-type","application/x-www-form-urlencoded"); + if (authenticate) { + if (root.oauthAccessToken!=null) { + uc.setRequestProperty("Authorization", "token " + root.oauthAccessToken); + } else { + if (root.password==null) + throw new IllegalArgumentException("V3 API doesn't support API token"); + uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization); + } } - } - try { - uc.setRequestMethod(method); - } catch (ProtocolException e) { - // JDK only allows one of the fixed set of verbs. Try to override that try { - Field $method = HttpURLConnection.class.getDeclaredField("method"); - $method.setAccessible(true); - $method.set(uc,method); - } catch (Exception x) { - throw (IOException)new IOException("Failed to set the custom verb").initCause(x); + uc.setRequestMethod(method); + } catch (ProtocolException e) { + // JDK only allows one of the fixed set of verbs. Try to override that + try { + Field $method = HttpURLConnection.class.getDeclaredField("method"); + $method.setAccessible(true); + $method.set(uc,method); + } catch (Exception x) { + throw (IOException)new IOException("Failed to set the custom verb").initCause(x); + } } - } - Map json = new HashMap(); - for (Entry e : args) { - json.put(e.key, e.value); + Map json = new HashMap(); + for (Entry e : args) { + json.put(e.key, e.value); + } + MAPPER.writeValue(uc.getOutputStream(),json); } - MAPPER.writeValue(uc.getOutputStream(),json); try { - InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8"); + InputStreamReader r = new InputStreamReader(wrapStream(uc,uc.getInputStream()), "UTF-8"); String data = IOUtils.toString(r); - if (type==null) { - return null; - } - return MAPPER.readValue(data,type); + if (type!=null) + return MAPPER.readValue(data,type); + if (instance!=null) + return MAPPER.readerForUpdating(instance).readValue(data); + return null; } catch (IOException e) { root.handleApiError(e,uc); } } } + + /** + * Handles the "Content-Encoding" header. + */ + private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOException { + String encoding = uc.getContentEncoding(); + if (encoding==null || in==null) return in; + if (encoding.equals("gzip")) return new GZIPInputStream(in); + + throw new UnsupportedOperationException("Unexpected Content-Encoding: "+encoding); + } + } diff --git a/src/test/java/org/kohsuke/AppTest.java b/src/test/java/org/kohsuke/AppTest.java index 8163d44be8..1b3147012c 100644 --- a/src/test/java/org/kohsuke/AppTest.java +++ b/src/test/java/org/kohsuke/AppTest.java @@ -87,7 +87,7 @@ public void testRepoPermissions() throws Exception { assertFalse(r.hasAdminAccess()); } - public void tryGetMyself() throws Exception { + public void testGetMyself() throws Exception { GitHub hub = GitHub.connect(); GHMyself me = hub.getMyself(); System.out.println(me); From b6520cb6f9195ab005969e232a2913bed17387ca Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 18:55:44 -0700 Subject: [PATCH 14/20] got rid of all retrieveXYZ methods in favor of Poster --- .../java/org/kohsuke/github/GHMyself.java | 4 +-- .../org/kohsuke/github/GHOrganization.java | 8 ++--- .../java/org/kohsuke/github/GHPerson.java | 2 +- .../java/org/kohsuke/github/GHRepository.java | 22 ++++++------ src/main/java/org/kohsuke/github/GHTeam.java | 12 +++---- src/main/java/org/kohsuke/github/GHUser.java | 6 ++-- src/main/java/org/kohsuke/github/GitHub.java | 35 ++++--------------- 7 files changed, 33 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHMyself.java b/src/main/java/org/kohsuke/github/GHMyself.java index ddceccd70f..8edc66577e 100644 --- a/src/main/java/org/kohsuke/github/GHMyself.java +++ b/src/main/java/org/kohsuke/github/GHMyself.java @@ -22,7 +22,7 @@ public class GHMyself extends GHUser { * Always non-null. */ public List getEmails() throws IOException { - String[] addresses = root.retrieveWithAuth("/user/emails", String[].class); + String[] addresses = root.retrieve().withCredential().to("/user/emails", String[].class); return Collections.unmodifiableList(Arrays.asList(addresses)); } @@ -33,7 +33,7 @@ public List getEmails() throws IOException { * Always non-null. */ public List getPublicKeys() throws IOException { - return Collections.unmodifiableList(Arrays.asList(root.retrieveWithAuth("/user/keys", GHKey[].class))); + return Collections.unmodifiableList(Arrays.asList(root.retrieve().withCredential().to("/user/keys", GHKey[].class))); } // public void addEmails(Collection emails) throws IOException { diff --git a/src/main/java/org/kohsuke/github/GHOrganization.java b/src/main/java/org/kohsuke/github/GHOrganization.java index 21d50102c7..d68368e9b2 100644 --- a/src/main/java/org/kohsuke/github/GHOrganization.java +++ b/src/main/java/org/kohsuke/github/GHOrganization.java @@ -42,7 +42,7 @@ public GHRepository createRepository(String name, String description, String hom * Teams by their names. */ public Map getTeams() throws IOException { - GHTeam[] teams = root.retrieveWithAuth("/orgs/" + login + "/teams", GHTeam[].class); + GHTeam[] teams = root.retrieve().withCredential().to("/orgs/" + login + "/teams", GHTeam[].class); Map r = new TreeMap(); for (GHTeam t : teams) { r.put(t.getName(),t.wrapUp(this)); @@ -54,7 +54,7 @@ public Map getTeams() throws IOException { * Publicizes the membership. */ public void publicize(GHUser u) throws IOException { - root.retrieveWithAuth("/orgs/" + login + "/public_members/" + u.getLogin(), null, "PUT"); + root.retrieve().withCredential().method("PUT").to("/orgs/" + login + "/public_members/" + u.getLogin(), null); } /** @@ -64,7 +64,7 @@ public List getMembers() throws IOException { return new AbstractList() { // these are shallow objects with only some limited values filled out // TODO: it's better to allow objects to fill themselves in later when missing values are requested - final GHUser[] shallow = root.retrieveWithAuth("/orgs/" + login + "/members", GHUser[].class); + final GHUser[] shallow = root.retrieve().withCredential().to("/orgs/" + login + "/members", GHUser[].class); @Override public GHUser get(int index) { @@ -86,7 +86,7 @@ public int size() { * Conceals the membership. */ public void conceal(GHUser u) throws IOException { - root.retrieveWithAuth("/orgs/" + login + "/public_members/" + u.getLogin(), null, "DELETE"); + root.retrieve().withCredential().method("DELETE").to("/orgs/" + login + "/public_members/" + u.getLogin(), null); } public enum Permission { ADMIN, PUSH, PULL } diff --git a/src/main/java/org/kohsuke/github/GHPerson.java b/src/main/java/org/kohsuke/github/GHPerson.java index 4811839c0a..b6484dcd85 100644 --- a/src/main/java/org/kohsuke/github/GHPerson.java +++ b/src/main/java/org/kohsuke/github/GHPerson.java @@ -85,7 +85,7 @@ public void remove() { */ public GHRepository getRepository(String name) throws IOException { try { - return root.retrieveWithAuth("/repos/" + login + '/' + name, GHRepository.class).wrap(root); + return root.retrieve().withCredential().to("/repos/" + login + '/' + name, GHRepository.class).wrap(root); } catch (FileNotFoundException e) { return null; } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 0106deab4f..e7ace143c1 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -141,7 +141,7 @@ public GHUser getOwner() throws IOException { } public List getIssues(GHIssueState state) throws IOException { - return Arrays.asList(GHIssue.wrap(root.retrieve("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this)); + return Arrays.asList(GHIssue.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this)); } protected String getOwnerName() { @@ -213,7 +213,7 @@ public int getSize() { */ @WithBridgeMethods(Set.class) public GHPersonSet getCollaborators() throws IOException { - return new GHPersonSet(GHUser.wrap(root.retrieve("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root)); + return new GHPersonSet(GHUser.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root)); } /** @@ -222,7 +222,7 @@ public GHPersonSet getCollaborators() throws IOException { */ public Set getCollaboratorNames() throws IOException { Set r = new HashSet(); - for (GHUser u : GHUser.wrap(root.retrieve("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root)) + for (GHUser u : GHUser.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root)) r.add(u.login); return r; } @@ -231,7 +231,7 @@ public Set getCollaboratorNames() throws IOException { * If this repository belongs to an organization, return a set of teams. */ public Set getTeams() throws IOException { - return Collections.unmodifiableSet(new HashSet(Arrays.asList(GHTeam.wrapUp(root.retrieveWithAuth("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login))))); + return Collections.unmodifiableSet(new HashSet(Arrays.asList(GHTeam.wrapUp(root.retrieve().withCredential().to("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login))))); } public void addCollaborators(GHUser... users) throws IOException { @@ -352,7 +352,7 @@ public GHRepository forkTo(GHOrganization org) throws IOException { * Retrieves a specified pull request. */ public GHPullRequest getPullRequest(int i) throws IOException { - return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this); + return root.retrieve().withCredential().to("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this); } /** @@ -386,14 +386,14 @@ protected void wrapUp(GHPullRequest[] page) { */ public List getHooks() throws IOException { List list = new ArrayList(Arrays.asList( - root.retrieveWithAuth(String.format("/repos/%s/%s/hooks", owner.login, name), GHHook[].class))); + root.retrieve().withCredential().to(String.format("/repos/%s/%s/hooks", owner.login, name), GHHook[].class))); for (GHHook h : list) h.wrap(this); return list; } public GHHook getHook(int id) throws IOException { - return root.retrieveWithAuth(String.format("/repos/%s/%s/hooks/%d", owner.login, name, id), GHHook.class).wrap(this); + return root.retrieve().withCredential().to(String.format("/repos/%s/%s/hooks/%d", owner.login, name, id), GHHook.class).wrap(this); } /** @@ -402,7 +402,7 @@ public GHHook getHook(int id) throws IOException { public GHCommit getCommit(String sha1) throws IOException { GHCommit c = commits.get(sha1); if (c==null) { - c = root.retrieve(String.format("/repos/%s/%s/commits/%s", owner.login, name, sha1), GHCommit.class).wrapUp(this); + c = root.retrieve().to(String.format("/repos/%s/%s/commits/%s", owner.login, name, sha1), GHCommit.class).wrapUp(this); commits.put(sha1,c); } return c; @@ -610,7 +610,7 @@ public boolean remove(Object url) { */ public Map getBranches() throws IOException { Map r = new TreeMap(); - for (GHBranch p : root.retrieve("/repos/" + owner.login + "/" + name + "/branches", GHBranch[].class)) { + for (GHBranch p : root.retrieve().to("/repos/" + owner.login + "/" + name + "/branches", GHBranch[].class)) { p.wrap(this); r.put(p.getName(),p); } @@ -619,7 +619,7 @@ public Map getBranches() throws IOException { public Map getMilestones() throws IOException { Map milestones = new TreeMap(); - GHMilestone[] ms = root.retrieve("/repos/" + owner.login + "/" + name + "/milestones", GHMilestone[].class); + GHMilestone[] ms = root.retrieve().to("/repos/" + owner.login + "/" + name + "/milestones", GHMilestone[].class); for (GHMilestone m : ms) { m.owner = this; m.root = root; @@ -631,7 +631,7 @@ public Map getMilestones() throws IOException { public GHMilestone getMilestone(int number) throws IOException { GHMilestone m = milestones.get(number); if (m == null) { - m = root.retrieve("/repos/" + owner.login + "/" + name + "/milestones/" + number, GHMilestone.class); + m = root.retrieve().to("/repos/" + owner.login + "/" + name + "/milestones/" + number, GHMilestone.class); m.owner = this; m.root = root; milestones.put(m.getNumber(), m); diff --git a/src/main/java/org/kohsuke/github/GHTeam.java b/src/main/java/org/kohsuke/github/GHTeam.java index d4bf99d582..dca37e3313 100644 --- a/src/main/java/org/kohsuke/github/GHTeam.java +++ b/src/main/java/org/kohsuke/github/GHTeam.java @@ -46,11 +46,11 @@ public int getId() { * Retrieves the current members. */ public Set getMembers() throws IOException { - return new HashSet(Arrays.asList(GHUser.wrap(org.root.retrieveWithAuth(api("/members"), GHUser[].class), org.root))); + return new HashSet(Arrays.asList(GHUser.wrap(org.root.retrieve().withCredential().to(api("/members"), GHUser[].class), org.root))); } public Map getRepositories() throws IOException { - GHRepository[] repos = org.root.retrieveWithAuth(api("/repos"), GHRepository[].class); + GHRepository[] repos = org.root.retrieve().withCredential().to(api("/repos"), GHRepository[].class); Map m = new TreeMap(); for (GHRepository r : repos) { m.put(r.getName(),r.wrap(org.root)); @@ -62,22 +62,22 @@ public Map getRepositories() throws IOException { * Adds a member to the team. */ public void add(GHUser u) throws IOException { - org.root.retrieveWithAuth(api("/members/" + u.getLogin()), null, "PUT"); + org.root.retrieve().withCredential().method("PUT").to(api("/members/" + u.getLogin()), null); } /** * Removes a member to the team. */ public void remove(GHUser u) throws IOException { - org.root.retrieveWithAuth(api("/members/" + u.getLogin()), null, "DELETE"); + org.root.retrieve().withCredential().method("DELETE").to(api("/members/" + u.getLogin()), null); } public void add(GHRepository r) throws IOException { - org.root.retrieveWithAuth(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null, "PUT"); + org.root.retrieve().withCredential().method("PUT").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null); } public void remove(GHRepository r) throws IOException { - org.root.retrieveWithAuth(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null, "DELETE"); + org.root.retrieve().withCredential().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null); } private String api(String tail) { diff --git a/src/main/java/org/kohsuke/github/GHUser.java b/src/main/java/org/kohsuke/github/GHUser.java index 373a867c24..074b960935 100644 --- a/src/main/java/org/kohsuke/github/GHUser.java +++ b/src/main/java/org/kohsuke/github/GHUser.java @@ -56,7 +56,7 @@ public void unfollow() throws IOException { */ @WithBridgeMethods(Set.class) public GHPersonSet getFollows() throws IOException { - GHUser[] followers = root.retrieve("/users/" + login + "/following", GHUser[].class); + GHUser[] followers = root.retrieve().to("/users/" + login + "/following", GHUser[].class); return new GHPersonSet(Arrays.asList(wrap(followers,root))); } @@ -65,7 +65,7 @@ public GHPersonSet getFollows() throws IOException { */ @WithBridgeMethods(Set.class) public GHPersonSet getFollowers() throws IOException { - GHUser[] followers = root.retrieve("/users/" + login + "/followers", GHUser[].class); + GHUser[] followers = root.retrieve().to("/users/" + login + "/followers", GHUser[].class); return new GHPersonSet(Arrays.asList(wrap(followers,root))); } @@ -82,7 +82,7 @@ public GHPersonSet getFollowers() throws IOException { public GHPersonSet getOrganizations() throws IOException { GHPersonSet orgs = new GHPersonSet(); Set names = new HashSet(); - for (GHOrganization o : root.retrieve("/users/" + login + "/orgs", GHOrganization[].class)) { + for (GHOrganization o : root.retrieve().to("/users/" + login + "/orgs", GHOrganization[].class)) { if (names.add(o.getLogin())) // I've seen some duplicates in the data orgs.add(root.getOrganization(o.getLogin())); } diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index b0d2b60f2c..af343875ba 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -174,29 +174,6 @@ public static GitHub connectAnonymously() { return new Poster(this).method("GET"); } - /*package*/ T retrieve(String tailApiUrl, Class type) throws IOException { - return _retrieve(tailApiUrl, type, "GET", false); - } - - /*package*/ T retrieveWithAuth(String tailApiUrl, Class type) throws IOException { - return _retrieve(tailApiUrl, type, "GET", true); - } - - /*package*/ T retrieveWithAuth(String tailApiUrl, Class type, String method) throws IOException { - return _retrieve(tailApiUrl, type, method, true); - } - - private T _retrieve(String tailApiUrl, Class type, String method, boolean withAuth) throws IOException { - while (true) {// loop while API rate limit is hit - HttpURLConnection uc = setupConnection(method, withAuth, getApiURL(tailApiUrl)); - try { - return parse(uc,type); - } catch (IOException e) { - handleApiError(e,uc); - } - } - } - /** * Loads pagenated resources. * @@ -359,7 +336,7 @@ private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOEx * Gets the current rate limit. */ public GHRateLimit getRateLimit() throws IOException { - return retrieveWithAuth("/rate_limit", JsonRateLimit.class).rate; + return retrieve().withCredential().to("/rate_limit", JsonRateLimit.class).rate; } /** @@ -369,7 +346,7 @@ public GHRateLimit getRateLimit() throws IOException { public GHMyself getMyself() throws IOException { requireCredential(); - GHMyself u = retrieveWithAuth("/user", GHMyself.class); + GHMyself u = retrieve().withCredential().to("/user", GHMyself.class); u.root = this; users.put(u.getLogin(), u); @@ -406,7 +383,7 @@ protected GHUser getUser(GHUser orig) throws IOException { public GHOrganization getOrganization(String name) throws IOException { GHOrganization o = orgs.get(name); if (o==null) { - o = retrieve("/orgs/" + name, GHOrganization.class).wrapUp(this); + o = retrieve().to("/orgs/" + name, GHOrganization.class).wrapUp(this); orgs.put(name,o); } return o; @@ -429,7 +406,7 @@ public GHRepository getRepository(String name) throws IOException { * TODO: make this automatic. */ public Map getMyOrganizations() throws IOException { - GHOrganization[] orgs = retrieveWithAuth("/user/orgs", GHOrganization[].class); + GHOrganization[] orgs = retrieve().withCredential().to("/user/orgs", GHOrganization[].class); Map r = new HashMap(); for (GHOrganization o : orgs) { // don't put 'o' into orgs because they are shallow @@ -443,7 +420,7 @@ public Map getMyOrganizations() throws IOException { */ public List getEvents() throws IOException { // TODO: pagenation - GHEventInfo[] events = retrieve("/events", GHEventInfo[].class); + GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class); for (GHEventInfo e : events) e.wrapUp(this); return Arrays.asList(events); @@ -480,7 +457,7 @@ public GHRepository createRepository(String name, String description, String hom */ public boolean isCredentialValid() throws IOException { try { - retrieveWithAuth("/user", GHUser.class); + retrieve().withCredential().to("/user", GHUser.class); return true; } catch (IOException e) { return false; From 435363a2460abf8ce2c81d372d9be302083e7933 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 19:08:29 -0700 Subject: [PATCH 15/20] moved the pagenation API over to Poster --- .../java/org/kohsuke/github/GHCommit.java | 2 +- src/main/java/org/kohsuke/github/GHIssue.java | 2 +- .../java/org/kohsuke/github/GHPerson.java | 2 +- .../java/org/kohsuke/github/GHRepository.java | 8 +- src/main/java/org/kohsuke/github/GitHub.java | 189 +----------------- src/main/java/org/kohsuke/github/Poster.java | 174 ++++++++++++++-- 6 files changed, 172 insertions(+), 205 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHCommit.java b/src/main/java/org/kohsuke/github/GHCommit.java index f210f00008..cda5566191 100644 --- a/src/main/java/org/kohsuke/github/GHCommit.java +++ b/src/main/java/org/kohsuke/github/GHCommit.java @@ -203,7 +203,7 @@ private GHUser resolveUser(User author) throws IOException { public PagedIterable listComments() { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(owner.root.retrievePaged(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment[].class,false)) { + return new PagedIterator(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment[].class)) { @Override protected void wrapUp(GHCommitComment[] page) { for (GHCommitComment c : page) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index e0d7976b71..e58327d437 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -167,7 +167,7 @@ public List getComments() throws IOException { public PagedIterable listComments() throws IOException { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrievePaged(getApiRoute() + "/comments",GHIssueComment[].class,false)) { + return new PagedIterator(root.retrieve().asIterator(getApiRoute() + "/comments",GHIssueComment[].class)) { protected void wrapUp(GHIssueComment[] page) { for (GHIssueComment c : page) c.wrapUp(GHIssue.this); diff --git a/src/main/java/org/kohsuke/github/GHPerson.java b/src/main/java/org/kohsuke/github/GHPerson.java index b6484dcd85..23cfc1d648 100644 --- a/src/main/java/org/kohsuke/github/GHPerson.java +++ b/src/main/java/org/kohsuke/github/GHPerson.java @@ -56,7 +56,7 @@ public synchronized Map getRepositories() throws IOExceptio public synchronized Iterable> iterateRepositories(final int pageSize) { return new Iterable>() { public Iterator> iterator() { - final Iterator pager = root.retrievePaged("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class,false); + final Iterator pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class); return new Iterator>() { public boolean hasNext() { diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index e7ace143c1..5090aee328 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -370,7 +370,7 @@ public List getPullRequests(GHIssueState state) throws IOExceptio public PagedIterable listPullRequests(final GHIssueState state) { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrievePaged(String.format("/repos/%s/%s/pulls?state=%s", owner.login,name,state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class, false)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/pulls?state=%s", owner.login,name,state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class)) { @Override protected void wrapUp(GHPullRequest[] page) { for (GHPullRequest pr : page) @@ -414,7 +414,7 @@ public GHCommit getCommit(String sha1) throws IOException { public PagedIterable listCommits() { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrievePaged(String.format("/repos/%s/%s/commits",owner.login,name),GHCommit[].class,false)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/commits",owner.login,name),GHCommit[].class)) { protected void wrapUp(GHCommit[] page) { for (GHCommit c : page) c.wrapUp(GHRepository.this); @@ -430,7 +430,7 @@ protected void wrapUp(GHCommit[] page) { public PagedIterable listCommitComments() { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrievePaged(String.format("/repos/%s/%s/comments",owner.login,name),GHCommitComment[].class,false)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/comments",owner.login,name),GHCommitComment[].class)) { @Override protected void wrapUp(GHCommitComment[] page) { for (GHCommitComment c : page) @@ -447,7 +447,7 @@ protected void wrapUp(GHCommitComment[] page) { public PagedIterable listCommitStatuses(final String sha1) throws IOException { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrievePaged(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1),GHCommitStatus[].class,false)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1),GHCommitStatus[].class)) { @Override protected void wrapUp(GHCommitStatus[] page) { for (GHCommitStatus c : page) diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index af343875ba..d9359e0bd5 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -23,18 +23,20 @@ */ package org.kohsuke.github; -import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.ANY; -import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.infradna.tool.bridge_method_injector.WithBridgeMethods; +import org.apache.commons.io.IOUtils; +import org.codehaus.jackson.map.DeserializationConfig.Feature; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.introspect.VisibilityChecker.Std; +import sun.misc.BASE64Encoder; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.InterruptedIOException; import java.io.Reader; -import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; @@ -42,25 +44,12 @@ import java.util.Arrays; import java.util.Date; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Properties; import java.util.TimeZone; -import java.util.zip.GZIPInputStream; - -import com.infradna.tool.bridge_method_injector.WithBridgeMethods; -import org.apache.commons.io.IOUtils; -import org.codehaus.jackson.map.DeserializationConfig.Feature; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.introspect.VisibilityChecker.Std; -import sun.misc.BASE64Encoder; - -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlForm; -import com.gargoylesoftware.htmlunit.html.HtmlPage; +import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*; /** * Root of the GitHub API. @@ -174,164 +163,6 @@ public static GitHub connectAnonymously() { return new Poster(this).method("GET"); } - /** - * Loads pagenated resources. - * - * Every iterator call reports a new batch. - */ - /*package*/ Iterator retrievePaged(final String tailApiUrl, final Class type, final boolean withAuth) { - return new Iterator() { - /** - * The next batch to be returned from {@link #next()}. - */ - T next; - /** - * URL of the next resource to be retrieved, or null if no more data is available. - */ - URL url; - - { - try { - url = getApiURL(tailApiUrl); - } catch (IOException e) { - throw new Error(e); - } - } - - public boolean hasNext() { - fetch(); - return next!=null; - } - - public T next() { - fetch(); - T r = next; - if (r==null) throw new NoSuchElementException(); - next = null; - return r; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - private void fetch() { - if (next!=null) return; // already fetched - if (url==null) return; // no more data to fetch - - try { - while (true) {// loop while API rate limit is hit - HttpURLConnection uc = setupConnection("GET", withAuth, url); - try { - next = parse(uc,type); - assert next!=null; - findNextURL(uc); - return; - } catch (IOException e) { - handleApiError(e,uc); - } - } - } catch (IOException e) { - throw new Error(e); - } - } - - /** - * Locate the next page from the pagination "Link" tag. - */ - private void findNextURL(HttpURLConnection uc) throws MalformedURLException { - url = null; // start defensively - String link = uc.getHeaderField("Link"); - if (link==null) return; - - for (String token : link.split(", ")) { - if (token.endsWith("rel=\"next\"")) { - // found the next page. This should look something like - // ; rel="next" - int idx = token.indexOf('>'); - url = new URL(token.substring(1,idx)); - return; - } - } - - // no more "next" link. we are done. - } - }; - } - - private HttpURLConnection setupConnection(String method, boolean withAuth, URL url) throws IOException { - HttpURLConnection uc = (HttpURLConnection) url.openConnection(); - - // if the authentication is needed but no credential is given, try it anyway (so that some calls - // that do work with anonymous access in the reduced form should still work.) - // if OAuth token is present, it'll be set in the URL, so need to set the Authorization header - if (withAuth && encodedAuthorization!=null && this.oauthAccessToken == null) - uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization); - - uc.setRequestMethod(method); - uc.setRequestProperty("Accept-Encoding", "gzip"); - if (method.equals("PUT")) { - uc.setDoOutput(true); - uc.setRequestProperty("Content-Length","0"); - uc.getOutputStream().close(); - } - return uc; - } - - private T parse(HttpURLConnection uc, Class type) throws IOException { - InputStreamReader r = null; - try { - r = new InputStreamReader(wrapStream(uc, uc.getInputStream()), "UTF-8"); - if (type==null) { - String data = IOUtils.toString(r); - return null; - } - return MAPPER.readValue(r,type); - } finally { - IOUtils.closeQuietly(r); - } - } - - /** - * Handles the "Content-Encoding" header. - */ - private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOException { - String encoding = uc.getContentEncoding(); - if (encoding==null || in==null) return in; - if (encoding.equals("gzip")) return new GZIPInputStream(in); - - throw new UnsupportedOperationException("Unexpected Content-Encoding: "+encoding); - } - - /** - * If the error is because of the API limit, wait 10 sec and return normally. - * Otherwise throw an exception reporting an error. - */ - /*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException { - if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) { - // API limit reached. wait 10 secs and return normally - try { - Thread.sleep(10000); - return; - } catch (InterruptedException _) { - throw (InterruptedIOException)new InterruptedIOException().initCause(e); - } - } - - if (e instanceof FileNotFoundException) - throw e; // pass through 404 Not Found to allow the caller to handle it intelligently - - InputStream es = wrapStream(uc, uc.getErrorStream()); - try { - if (es!=null) - throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e); - else - throw e; - } finally { - IOUtils.closeQuietly(es); - } - } - /** * Gets the current rate limit. */ diff --git a/src/main/java/org/kohsuke/github/Poster.java b/src/main/java/org/kohsuke/github/Poster.java index 886947a90b..daf793f1c9 100644 --- a/src/main/java/org/kohsuke/github/Poster.java +++ b/src/main/java/org/kohsuke/github/Poster.java @@ -25,18 +25,24 @@ import org.apache.commons.io.IOUtils; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.InterruptedIOException; import java.io.Reader; import java.lang.reflect.Field; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.ProtocolException; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.zip.GZIPInputStream; import static org.kohsuke.github.GitHub.*; @@ -147,21 +153,11 @@ public T to(String tailApiUrl, Class type, String method) throws IOExcept private T _to(String tailApiUrl, Class type, T instance) throws IOException { while (true) {// loop while API rate limit is hit - HttpURLConnection uc = (HttpURLConnection) root.getApiURL(tailApiUrl).openConnection(); - uc.setRequestProperty("Accept-Encoding", "gzip"); + HttpURLConnection uc = setupConnection(root.getApiURL(tailApiUrl)); if (!method.equals("GET")) { uc.setDoOutput(true); uc.setRequestProperty("Content-type","application/x-www-form-urlencoded"); - if (authenticate) { - if (root.oauthAccessToken!=null) { - uc.setRequestProperty("Authorization", "token " + root.oauthAccessToken); - } else { - if (root.password==null) - throw new IllegalArgumentException("V3 API doesn't support API token"); - uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization); - } - } try { uc.setRequestMethod(method); } catch (ProtocolException e) { @@ -184,19 +180,131 @@ private T _to(String tailApiUrl, Class type, T instance) throws IOExcepti } try { - InputStreamReader r = new InputStreamReader(wrapStream(uc,uc.getInputStream()), "UTF-8"); - String data = IOUtils.toString(r); - if (type!=null) - return MAPPER.readValue(data,type); - if (instance!=null) - return MAPPER.readerForUpdating(instance).readValue(data); - return null; + return parse(uc,type,instance); } catch (IOException e) { - root.handleApiError(e,uc); + handleApiError(e,uc); } } } + /** + * Loads pagenated resources. + * + * Every iterator call reports a new batch. + */ + /*package*/ Iterator asIterator(final String tailApiUrl, final Class type) { + method("GET"); + if (!args.isEmpty()) throw new IllegalStateException(); + + return new Iterator() { + /** + * The next batch to be returned from {@link #next()}. + */ + T next; + /** + * URL of the next resource to be retrieved, or null if no more data is available. + */ + URL url; + + { + try { + url = root.getApiURL(tailApiUrl); + } catch (IOException e) { + throw new Error(e); + } + } + + public boolean hasNext() { + fetch(); + return next!=null; + } + + public T next() { + fetch(); + T r = next; + if (r==null) throw new NoSuchElementException(); + next = null; + return r; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + private void fetch() { + if (next!=null) return; // already fetched + if (url==null) return; // no more data to fetch + + try { + while (true) {// loop while API rate limit is hit + HttpURLConnection uc = setupConnection(url); + try { + next = parse(uc,type,null); + assert next!=null; + findNextURL(uc); + return; + } catch (IOException e) { + handleApiError(e,uc); + } + } + } catch (IOException e) { + throw new Error(e); + } + } + + /** + * Locate the next page from the pagination "Link" tag. + */ + private void findNextURL(HttpURLConnection uc) throws MalformedURLException { + url = null; // start defensively + String link = uc.getHeaderField("Link"); + if (link==null) return; + + for (String token : link.split(", ")) { + if (token.endsWith("rel=\"next\"")) { + // found the next page. This should look something like + // ; rel="next" + int idx = token.indexOf('>'); + url = new URL(token.substring(1,idx)); + return; + } + } + + // no more "next" link. we are done. + } + }; + } + + + private HttpURLConnection setupConnection(URL url) throws IOException { + HttpURLConnection uc = (HttpURLConnection) url.openConnection(); + + // if the authentication is needed but no credential is given, try it anyway (so that some calls + // that do work with anonymous access in the reduced form should still work.) + // if OAuth token is present, it'll be set in the URL, so need to set the Authorization header + if (authenticate && root.encodedAuthorization!=null && root.oauthAccessToken == null) + uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization); + + uc.setRequestMethod(method); + uc.setRequestProperty("Accept-Encoding", "gzip"); + return uc; + } + + private T parse(HttpURLConnection uc, Class type, T instance) throws IOException { + InputStreamReader r = null; + try { + r = new InputStreamReader(wrapStream(uc, uc.getInputStream()), "UTF-8"); + String data = IOUtils.toString(r); + if (type!=null) + return MAPPER.readValue(data,type); + if (instance!=null) + return MAPPER.readerForUpdating(instance).readValue(data); + return null; + } finally { + IOUtils.closeQuietly(r); + } + } + /** * Handles the "Content-Encoding" header. */ @@ -208,4 +316,32 @@ private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOEx throw new UnsupportedOperationException("Unexpected Content-Encoding: "+encoding); } + /** + * If the error is because of the API limit, wait 10 sec and return normally. + * Otherwise throw an exception reporting an error. + */ + /*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException { + if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) { + // API limit reached. wait 10 secs and return normally + try { + Thread.sleep(10000); + return; + } catch (InterruptedException _) { + throw (InterruptedIOException)new InterruptedIOException().initCause(e); + } + } + + if (e instanceof FileNotFoundException) + throw e; // pass through 404 Not Found to allow the caller to handle it intelligently + + InputStream es = wrapStream(uc, uc.getErrorStream()); + try { + if (es!=null) + throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e); + else + throw e; + } finally { + IOUtils.closeQuietly(es); + } + } } From ea9f3eacbce3c714bd609d1735a972c66d53cd5a Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 19:10:37 -0700 Subject: [PATCH 16/20] bug fix --- src/main/java/org/kohsuke/github/Poster.java | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/kohsuke/github/Poster.java b/src/main/java/org/kohsuke/github/Poster.java index daf793f1c9..90c3e87ddf 100644 --- a/src/main/java/org/kohsuke/github/Poster.java +++ b/src/main/java/org/kohsuke/github/Poster.java @@ -158,19 +158,6 @@ private T _to(String tailApiUrl, Class type, T instance) throws IOExcepti if (!method.equals("GET")) { uc.setDoOutput(true); uc.setRequestProperty("Content-type","application/x-www-form-urlencoded"); - try { - uc.setRequestMethod(method); - } catch (ProtocolException e) { - // JDK only allows one of the fixed set of verbs. Try to override that - try { - Field $method = HttpURLConnection.class.getDeclaredField("method"); - $method.setAccessible(true); - $method.set(uc,method); - } catch (Exception x) { - throw (IOException)new IOException("Failed to set the custom verb").initCause(x); - } - } - Map json = new HashMap(); for (Entry e : args) { @@ -285,7 +272,18 @@ private HttpURLConnection setupConnection(URL url) throws IOException { if (authenticate && root.encodedAuthorization!=null && root.oauthAccessToken == null) uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization); - uc.setRequestMethod(method); + try { + uc.setRequestMethod(method); + } catch (ProtocolException e) { + // JDK only allows one of the fixed set of verbs. Try to override that + try { + Field $method = HttpURLConnection.class.getDeclaredField("method"); + $method.setAccessible(true); + $method.set(uc,method); + } catch (Exception x) { + throw (IOException)new IOException("Failed to set the custom verb").initCause(x); + } + } uc.setRequestProperty("Accept-Encoding", "gzip"); return uc; } From 8a95847b0aaf5583491ddfb531c8882a5221fe52 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 19:13:10 -0700 Subject: [PATCH 17/20] Renaming to better represent what it does. --- .../java/org/kohsuke/github/GHCommit.java | 4 +-- .../org/kohsuke/github/GHCommitComment.java | 4 +-- src/main/java/org/kohsuke/github/GHHook.java | 2 +- src/main/java/org/kohsuke/github/GHIssue.java | 6 ++-- .../java/org/kohsuke/github/GHMyself.java | 2 +- .../org/kohsuke/github/GHOrganization.java | 4 +-- .../java/org/kohsuke/github/GHRepository.java | 28 +++++++++---------- src/main/java/org/kohsuke/github/GHUser.java | 4 +-- src/main/java/org/kohsuke/github/GitHub.java | 8 +++--- .../github/{Poster.java => Requester.java} | 20 ++++++------- 10 files changed, 41 insertions(+), 41 deletions(-) rename src/main/java/org/kohsuke/github/{Poster.java => Requester.java} (95%) diff --git a/src/main/java/org/kohsuke/github/GHCommit.java b/src/main/java/org/kohsuke/github/GHCommit.java index cda5566191..8069066780 100644 --- a/src/main/java/org/kohsuke/github/GHCommit.java +++ b/src/main/java/org/kohsuke/github/GHCommit.java @@ -203,7 +203,7 @@ private GHUser resolveUser(User author) throws IOException { public PagedIterable listComments() { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment[].class)) { + return new PagedIterator(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class)) { @Override protected void wrapUp(GHCommitComment[] page) { for (GHCommitComment c : page) @@ -220,7 +220,7 @@ protected void wrapUp(GHCommitComment[] page) { * I'm not sure how path/line/position parameters interact with each other. */ public GHCommitComment createComment(String body, String path, Integer line, Integer position) throws IOException { - GHCommitComment r = new Poster(owner.root) + GHCommitComment r = new Requester(owner.root) .with("body",body) .with("path",path) .with("line",line) diff --git a/src/main/java/org/kohsuke/github/GHCommitComment.java b/src/main/java/org/kohsuke/github/GHCommitComment.java index 44d7983422..9ff07304eb 100644 --- a/src/main/java/org/kohsuke/github/GHCommitComment.java +++ b/src/main/java/org/kohsuke/github/GHCommitComment.java @@ -97,7 +97,7 @@ public GHCommit getCommit() throws IOException { * Updates the body of the commit message. */ public void update(String body) throws IOException { - GHCommitComment r = new Poster(owner.root) + GHCommitComment r = new Requester(owner.root) .with("body", body) .withCredential().method("PATCH").to(getApiTail(), GHCommitComment.class); this.body = body; @@ -107,7 +107,7 @@ public void update(String body) throws IOException { * Deletes this comment. */ public void delete() throws IOException { - new Poster(owner.root).withCredential().method("DELETE").to(getApiTail()); + new Requester(owner.root).withCredential().method("DELETE").to(getApiTail()); } private String getApiTail() { diff --git a/src/main/java/org/kohsuke/github/GHHook.java b/src/main/java/org/kohsuke/github/GHHook.java index 89e7020e77..8d1e5da027 100644 --- a/src/main/java/org/kohsuke/github/GHHook.java +++ b/src/main/java/org/kohsuke/github/GHHook.java @@ -54,6 +54,6 @@ public int getId() { * Deletes this hook. */ public void delete() throws IOException { - new Poster(repository.root).withCredential().method("DELETE").to(String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), id)); + new Requester(repository.root).withCredential().method("DELETE").to(String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), id)); } } diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index e58327d437..11145d23aa 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -115,11 +115,11 @@ public Date getUpdatedAt() { * Updates the issue by adding a comment. */ public void comment(String message) throws IOException { - new Poster(root).withCredential().with("body",message).to(getApiRoute() + "/comments"); + new Requester(root).withCredential().with("body",message).to(getApiRoute() + "/comments"); } private void edit(String key, Object value) throws IOException { - new Poster(root).withCredential()._with(key, value).method("PATCH").to(getApiRoute()); + new Requester(root).withCredential()._with(key, value).method("PATCH").to(getApiRoute()); } /** @@ -167,7 +167,7 @@ public List getComments() throws IOException { public PagedIterable listComments() throws IOException { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrieve().asIterator(getApiRoute() + "/comments",GHIssueComment[].class)) { + return new PagedIterator(root.retrieve().asIterator(getApiRoute() + "/comments", GHIssueComment[].class)) { protected void wrapUp(GHIssueComment[] page) { for (GHIssueComment c : page) c.wrapUp(GHIssue.this); diff --git a/src/main/java/org/kohsuke/github/GHMyself.java b/src/main/java/org/kohsuke/github/GHMyself.java index 8edc66577e..2a16abcdad 100644 --- a/src/main/java/org/kohsuke/github/GHMyself.java +++ b/src/main/java/org/kohsuke/github/GHMyself.java @@ -37,7 +37,7 @@ public List getPublicKeys() throws IOException { } // public void addEmails(Collection emails) throws IOException { -//// new Poster(root,ApiVersion.V3).withCredential().to("/user/emails"); +//// new Requester(root,ApiVersion.V3).withCredential().to("/user/emails"); // root.retrieveWithAuth3() // } } diff --git a/src/main/java/org/kohsuke/github/GHOrganization.java b/src/main/java/org/kohsuke/github/GHOrganization.java index d68368e9b2..8328c72235 100644 --- a/src/main/java/org/kohsuke/github/GHOrganization.java +++ b/src/main/java/org/kohsuke/github/GHOrganization.java @@ -33,7 +33,7 @@ public GHRepository createRepository(String name, String description, String hom public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException { // such API doesn't exist, so fall back to HTML scraping - return new Poster(root).withCredential() + return new Requester(root).withCredential() .with("name", name).with("description", description).with("homepage", homepage) .with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root); } @@ -95,7 +95,7 @@ public enum Permission { ADMIN, PUSH, PULL } * Creates a new team and assigns the repositories. */ public GHTeam createTeam(String name, Permission p, Collection repositories) throws IOException { - Poster post = new Poster(root).withCredential().with("name", name).with("permission", p.name().toLowerCase()); + Requester post = new Requester(root).withCredential().with("name", name).with("permission", p.name().toLowerCase()); List repo_names = new ArrayList(); for (GHRepository r : repositories) { repo_names.add(r.getName()); diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 5090aee328..291b616c3a 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -253,7 +253,7 @@ public void removeCollaborators(Collection users) throws IOException { private void modifyCollaborators(Collection users, String method) throws IOException { verifyMine(); for (GHUser user : users) { - new Poster(root).withCredential().method(method).to("/repos/" + owner.login + "/" + name + "/collaborators/" + user.getLogin()); + new Requester(root).withCredential().method(method).to("/repos/" + owner.login + "/" + name + "/collaborators/" + user.getLogin()); } } @@ -270,10 +270,10 @@ public void setEmailServiceHook(String address) throws IOException { } private void edit(String key, String value) throws IOException { - Poster poster = new Poster(root).withCredential(); + Requester requester = new Requester(root).withCredential(); if (!key.equals("name")) - poster.with("name", name); // even when we don't change the name, we need to send it in - poster.with(key, value).method("PATCH").to("/repos/" + owner.login + "/" + name); + requester.with("name", name); // even when we don't change the name, we need to send it in + requester.with(key, value).method("PATCH").to("/repos/" + owner.login + "/" + name); } /** @@ -313,7 +313,7 @@ public void setHomepage(String value) throws IOException { * Deletes this repository. */ public void delete() throws IOException { - new Poster(root).withCredential().method("DELETE").to("/repos/" + owner.login + "/" + name); + new Requester(root).withCredential().method("DELETE").to("/repos/" + owner.login + "/" + name); } /** @@ -323,7 +323,7 @@ public void delete() throws IOException { * Newly forked repository that belong to you. */ public GHRepository fork() throws IOException { - return new Poster(root).withCredential().method("POST").to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class).wrap(root); + return new Requester(root).withCredential().method("POST").to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class).wrap(root); } /** @@ -333,7 +333,7 @@ public GHRepository fork() throws IOException { * Newly forked repository that belong to you. */ public GHRepository forkTo(GHOrganization org) throws IOException { - new Poster(root).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin())); + new Requester(root).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin())); // this API is asynchronous. we need to wait for a bit for (int i=0; i<10; i++) { @@ -370,7 +370,7 @@ public List getPullRequests(GHIssueState state) throws IOExceptio public PagedIterable listPullRequests(final GHIssueState state) { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/pulls?state=%s", owner.login,name,state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/pulls?state=%s", owner.login, name, state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class)) { @Override protected void wrapUp(GHPullRequest[] page) { for (GHPullRequest pr : page) @@ -414,7 +414,7 @@ public GHCommit getCommit(String sha1) throws IOException { public PagedIterable listCommits() { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/commits",owner.login,name),GHCommit[].class)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/commits", owner.login, name), GHCommit[].class)) { protected void wrapUp(GHCommit[] page) { for (GHCommit c : page) c.wrapUp(GHRepository.this); @@ -430,7 +430,7 @@ protected void wrapUp(GHCommit[] page) { public PagedIterable listCommitComments() { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/comments",owner.login,name),GHCommitComment[].class)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/comments", owner.login, name), GHCommitComment[].class)) { @Override protected void wrapUp(GHCommitComment[] page) { for (GHCommitComment c : page) @@ -447,7 +447,7 @@ protected void wrapUp(GHCommitComment[] page) { public PagedIterable listCommitStatuses(final String sha1) throws IOException { return new PagedIterable() { public PagedIterator iterator() { - return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1),GHCommitStatus[].class)) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1), GHCommitStatus[].class)) { @Override protected void wrapUp(GHCommitStatus[] page) { for (GHCommitStatus c : page) @@ -475,7 +475,7 @@ public GHCommitStatus getLastCommitStatus(String sha1) throws IOException { * Optional short description. */ public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException { - return new Poster(root) + return new Requester(root) .withCredential() .with("state",state.name().toLowerCase(Locale.ENGLISH)) .with("target_url", targetUrl) @@ -504,7 +504,7 @@ public GHHook createHook(String name, Map config, Collection T parseEventPayload(Reader r, Class type) t * Newly created repository. */ public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException { - Poster poster = new Poster(this).withCredential() + Requester requester = new Requester(this).withCredential() .with("name", name).with("description", description).with("homepage", homepage) .with("public", isPublic ? 1 : 0); - return poster.method("POST").to("/user/repos", GHRepository.class).wrap(this); + return requester.method("POST").to("/user/repos", GHRepository.class).wrap(this); } /** diff --git a/src/main/java/org/kohsuke/github/Poster.java b/src/main/java/org/kohsuke/github/Requester.java similarity index 95% rename from src/main/java/org/kohsuke/github/Poster.java rename to src/main/java/org/kohsuke/github/Requester.java index 90c3e87ddf..be8bb3ffd6 100644 --- a/src/main/java/org/kohsuke/github/Poster.java +++ b/src/main/java/org/kohsuke/github/Requester.java @@ -52,7 +52,7 @@ * * @author Kohsuke Kawaguchi */ -class Poster { +class Requester { private final GitHub root; private final List args = new ArrayList(); private boolean authenticate; @@ -72,50 +72,50 @@ private Entry(String key, Object value) { } } - Poster(GitHub root) { + Requester(GitHub root) { this.root = root; } /** * Makes a request with authentication credential. */ - public Poster withCredential() { + public Requester withCredential() { // keeping it inline with retrieveWithAuth not to enforce the check // root.requireCredential(); authenticate = true; return this; } - public Poster with(String key, int value) { + public Requester with(String key, int value) { return _with(key, value); } - public Poster with(String key, Integer value) { + public Requester with(String key, Integer value) { if (value!=null) _with(key, value.intValue()); return this; } - public Poster with(String key, boolean value) { + public Requester with(String key, boolean value) { return _with(key, value); } - public Poster with(String key, String value) { + public Requester with(String key, String value) { return _with(key, value); } - public Poster with(String key, Collection value) { + public Requester with(String key, Collection value) { return _with(key, value); } - public Poster _with(String key, Object value) { + public Requester _with(String key, Object value) { if (value!=null) { args.add(new Entry(key,value)); } return this; } - public Poster method(String method) { + public Requester method(String method) { this.method = method; return this; } From 1dd875adacdd67835bda5acbfdd878f5f1c8d2d1 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 19:25:56 -0700 Subject: [PATCH 18/20] adding lazy population to GHUser and got rid of GHSmallUser --- src/main/java/org/kohsuke/github/GHIssue.java | 48 +++++--------- .../java/org/kohsuke/github/GHPerson.java | 51 ++++++++++----- .../org/kohsuke/github/GHPullRequest.java | 30 +++++---- .../java/org/kohsuke/github/GHSmallUser.java | 65 ------------------- src/main/java/org/kohsuke/github/GHUser.java | 1 - src/test/java/org/kohsuke/AppTest.java | 9 +++ 6 files changed, 80 insertions(+), 124 deletions(-) delete mode 100644 src/main/java/org/kohsuke/github/GHSmallUser.java diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index 5952c424ca..18f2594e3a 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -43,20 +43,20 @@ public class GHIssue { GHRepository owner; // API v3 - private GHSmallUser assignee; - private String state; - private int number; - private String closed_at; - private int comments; - private String body; - private List labels; - private GHSmallUser user; - private String title, created_at, html_url; - private GHIssue.PullRequest pull_request; - private GHMilestone milestone; - private String url, updated_at; - private int id; - private GHSmallUser closed_by; + protected GHUser assignee; + protected String state; + protected int number; + protected String closed_at; + protected int comments; + protected String body; + protected List labels; + protected GHUser user; + protected String title, created_at, html_url; + protected GHIssue.PullRequest pull_request; + protected GHMilestone milestone; + protected String url, updated_at; + protected int id; + protected GHUser closed_by; /*package*/ GHIssue wrap(GHRepository owner) { this.owner = owner; @@ -204,33 +204,19 @@ private String getApiRoute() { return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number; } - public GHSmallUser getAssignee() { + public GHUser getAssignee() { return assignee; } /** * User who submitted the issue. - * - * @return May return null when IOException occures. Prefered way is getSmallUser().getUser(). - * @see #getSmallUser() */ @Deprecated public GHUser getUser() { - try { - return user.getUser(); - } catch (IOException ex) { - return null; - } + return user; } - /** - * Shallow user who submitted the issue. - */ - public GHSmallUser getSmallUser(){ - return user; - } - - public GHSmallUser getClosedBy() { + public GHUser getClosedBy() { if(!"closed".equals(state)) return null; if(closed_by != null) return closed_by; diff --git a/src/main/java/org/kohsuke/github/GHPerson.java b/src/main/java/org/kohsuke/github/GHPerson.java index 23cfc1d648..71c7c54c6f 100644 --- a/src/main/java/org/kohsuke/github/GHPerson.java +++ b/src/main/java/org/kohsuke/github/GHPerson.java @@ -17,13 +17,13 @@ public abstract class GHPerson { /*package almost final*/ GitHub root; - // common - protected String login,location,blog,email,name,created_at,company; + // core data fields that exist even for "small" user data (such as the user info in pull request) + protected String login, avatar_url, url, gravatar_id; protected int id; - protected String gravatar_id; // appears in V3 as well but presumably subsumed by avatar_url? - // V3 - protected String avatar_url,html_url; + // other fields (that only show up in full data) + protected String location,blog,email,name,created_at,company; + protected String html_url; protected int followers,following,public_repos,public_gists; /*package*/ GHPerson wrapUp(GitHub root) { @@ -31,6 +31,17 @@ public abstract class GHPerson { return this; } + /** + * Fully populate the data by retrieving missing data. + * + * Depending on the original API call where this object is created, it may not contain everything. + */ + protected void populate() throws IOException { + if (created_at!=null) return; // already populated + + root.retrieve().to(url, this); + } + /** * Gets the repositories this user owns. */ @@ -123,51 +134,60 @@ public String getLogin() { /** * Gets the human-readable name of the user, like "Kohsuke Kawaguchi" */ - public String getName() { + public String getName() throws IOException { + populate(); return name; } /** * Gets the company name of this user, like "Sun Microsystems, Inc." */ - public String getCompany() { + public String getCompany() throws IOException { + populate(); return company; } /** * Gets the location of this user, like "Santa Clara, California" */ - public String getLocation() { + public String getLocation() throws IOException { + populate(); return location; } - public String getCreatedAt() { + public String getCreatedAt() throws IOException { + populate(); return created_at; } /** * Gets the blog URL of this user. */ - public String getBlog() { + public String getBlog() throws IOException { + populate(); return blog; } /** * Gets the e-mail address of the user. */ - public String getEmail() { + public String getEmail() throws IOException { + populate(); return email; } - public int getPublicGistCount() { + public int getPublicGistCount() throws IOException { + populate(); return public_gists; } - public int getPublicRepoCount() { + public int getPublicRepoCount() throws IOException { + populate(); return public_repos; } - public int getFollowingCount() { + public int getFollowingCount() throws IOException { + populate(); return following; } @@ -178,7 +198,8 @@ public int getId() { return id; } - public int getFollowersCount() { + public int getFollowersCount() throws IOException { + populate(); return followers; } diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index e78a88df5d..32523005e2 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -23,6 +23,7 @@ */ package org.kohsuke.github; +import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.Date; @@ -41,7 +42,7 @@ public class GHPullRequest extends GHIssue { private GHCommitPointer head; // details that are only available when obtained from ID - private GHSmallUser merged_by; + private GHUser merged_by; private int review_comments, additions; private boolean merged; private Boolean mergeable; @@ -117,7 +118,7 @@ public Collection getLabels() { } @Override - public GHSmallUser getClosedBy() { + public GHUser getClosedBy() { return null; } @@ -130,49 +131,54 @@ public PullRequest getPullRequest() { // details that are only available via get with ID // // - public GHSmallUser getMergedBy() { + public GHUser getMergedBy() throws IOException { populate(); return merged_by; } - public int getReviewComments() { + public int getReviewComments() throws IOException { populate(); return review_comments; } - public int getAdditions() { + public int getAdditions() throws IOException { populate(); return additions; } - public boolean isMerged() { + public boolean isMerged() throws IOException { populate(); return merged; } - public Boolean getMergeable() { + public Boolean getMergeable() throws IOException { populate(); return mergeable; } - public int getDeletions() { + public int getDeletions() throws IOException { populate(); return deletions; } - public String getMergeableState() { + public String getMergeableState() throws IOException { populate(); return mergeable_state; } - public int getChangedFiles() { + public int getChangedFiles() throws IOException { populate(); return changed_files; } - private void populate() { + /** + * Fully populate the data by retrieving missing data. + * + * Depending on the original API call where this object is created, it may not contain everything. + */ + private void populate() throws IOException { if (merged_by!=null) return; // already populated - root.retrieveWithAuth(getUrl(), this); + root.retrieve().to(url, this); } } diff --git a/src/main/java/org/kohsuke/github/GHSmallUser.java b/src/main/java/org/kohsuke/github/GHSmallUser.java deleted file mode 100644 index 53e256ecef..0000000000 --- a/src/main/java/org/kohsuke/github/GHSmallUser.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License - * - * Copyright 2012 Honza Brázdil. - * - * 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; - -/** - * This represents a subset of user information that often appear as a part of a bigger data graph in the GitHub API. - * - * @author Honza Brázdil - * @see GHUser - */ -public class GHSmallUser { - private GitHub root; - private String avatar_url, login, url, gravatar_id; - private int id; - - /*package*/ GHSmallUser wrapUp(GitHub root) { - this.root = root; - return this; - } - - - public URL getAvatarUrl() { - return GitHub.parseURL(avatar_url); - } - - public String getLogin() { - return login; - } - - public URL getApiUrl() { - return GitHub.parseURL(url); - } - - public String getGravatarId() { - return gravatar_id; - } - - public GHUser getUser() throws IOException{ - return root.getUser(login); - } -} diff --git a/src/main/java/org/kohsuke/github/GHUser.java b/src/main/java/org/kohsuke/github/GHUser.java index a87d9cf51f..329434f38c 100644 --- a/src/main/java/org/kohsuke/github/GHUser.java +++ b/src/main/java/org/kohsuke/github/GHUser.java @@ -34,7 +34,6 @@ * Represents an user of GitHub. * * @author Kohsuke Kawaguchi - * @see GHSmallUser */ public class GHUser extends GHPerson { diff --git a/src/test/java/org/kohsuke/AppTest.java b/src/test/java/org/kohsuke/AppTest.java index 1b3147012c..722914d47e 100644 --- a/src/test/java/org/kohsuke/AppTest.java +++ b/src/test/java/org/kohsuke/AppTest.java @@ -11,6 +11,7 @@ import org.kohsuke.github.GHEventPayload; import org.kohsuke.github.GHHook; import org.kohsuke.github.GHBranch; +import org.kohsuke.github.GHIssue; import org.kohsuke.github.GHIssueState; import org.kohsuke.github.GHKey; import org.kohsuke.github.GHMyself; @@ -323,4 +324,12 @@ public void testCommitStatus() throws Exception { assertEquals("oops!",state.getDescription()); assertEquals("http://jenkins-ci.org/",state.getTargetUrl()); } + + public void testPullRequestPopulate() throws Exception { + GitHub gitHub = GitHub.connect(); + GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api"); + GHPullRequest p = r.getPullRequest(17); + GHUser u = p.getUser(); + System.out.println(u.getName()); + } } From 3e75e967189fe5b882f1a04437ba21a0a596c646 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 19:26:25 -0700 Subject: [PATCH 19/20] adding an assertion --- src/test/java/org/kohsuke/AppTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/kohsuke/AppTest.java b/src/test/java/org/kohsuke/AppTest.java index 722914d47e..ba4a4a4804 100644 --- a/src/test/java/org/kohsuke/AppTest.java +++ b/src/test/java/org/kohsuke/AppTest.java @@ -330,6 +330,6 @@ public void testPullRequestPopulate() throws Exception { GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api"); GHPullRequest p = r.getPullRequest(17); GHUser u = p.getUser(); - System.out.println(u.getName()); + assertNotNull(u.getName()); } } From a0fdcca1293da38953362531771297531c8f24ae Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 5 Sep 2012 19:30:04 -0700 Subject: [PATCH 20/20] [maven-release-plugin] prepare release github-api-1.32 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7cf0c6bc02..f8ac35a431 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ github-api - 1.32-SNAPSHOT + 1.32 GitHub API for Java http://github-api.kohsuke.org/ GitHub API for Java