> responseHeaderFields;
+
protected String url;
protected int id;
protected String created_at;
@@ -26,6 +32,21 @@ public abstract class GHObject {
/*package*/ GHObject() {
}
+ /**
+ * Returns the HTTP response headers given along with the state of this object.
+ *
+ *
+ * Some of the HTTP headers have nothing to do with the object, for example "Cache-Control"
+ * and others are different depending on how this object was retrieved.
+ *
+ * This method was added as a kind of hack to allow the caller to retrieve OAuth scopes and such.
+ * Use with caution. The method might be removed in the future.
+ */
+ @CheckForNull @Deprecated
+ public Map> getResponseHeaderFields() {
+ return responseHeaderFields;
+ }
+
/**
* When was this resource created?
*/
diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java
index 078603d6fa..6e1a5d90e5 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 javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
@@ -30,9 +31,8 @@
import java.util.Collection;
import java.util.Date;
import java.util.List;
-import javax.annotation.CheckForNull;
-import static org.kohsuke.github.Previews.BLACK_CAT;
+import static org.kohsuke.github.Previews.*;
/**
* A pull request.
@@ -206,7 +206,7 @@ public String getMergeCommitSha() throws IOException {
* 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
+ if (mergeable_state!=null) return; // already populated
if (root.isOffline()) {
return; // cannot populate, will have to live with what we have
}
@@ -346,9 +346,29 @@ public void merge(String msg) throws IOException {
* SHA that pull request head must match to allow merge.
*/
public void merge(String msg, String sha) throws IOException {
- new Requester(root).method("PUT").with("commit_message",msg).with("sha",sha).to(getApiRoute()+"/merge");
+ merge(msg, sha, null);
}
+ /**
+ * Merge this pull request, using the specified merge method.
+ *
+ * The equivalent of the big green "Merge pull request" button.
+ *
+ * @param msg
+ * Commit message. If null, the default one will be used.
+ * @param method
+ * SHA that pull request head must match to allow merge.
+ */
+ public void merge(String msg, String sha, MergeMethod method) throws IOException {
+ new Requester(root).method("PUT")
+ .with("commit_message",msg)
+ .with("sha",sha)
+ .with("merge_method",method)
+ .to(getApiRoute()+"/merge");
+ }
+
+ public enum MergeMethod{ MERGE, SQUASH, REBASE }
+
private void fetchIssue() throws IOException {
if (!fetchedIssueDetails) {
new Requester(root).to(getIssuesApiRoute(), this);
diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReview.java b/src/main/java/org/kohsuke/github/GHPullRequestReview.java
index b25f28b59a..d7afe59aad 100644
--- a/src/main/java/org/kohsuke/github/GHPullRequestReview.java
+++ b/src/main/java/org/kohsuke/github/GHPullRequestReview.java
@@ -26,7 +26,7 @@
import java.io.IOException;
import java.net.URL;
-import static org.kohsuke.github.Previews.BLACK_CAT;
+import static org.kohsuke.github.Previews.*;
/**
* Review to the pull request
diff --git a/src/main/java/org/kohsuke/github/GHRateLimit.java b/src/main/java/org/kohsuke/github/GHRateLimit.java
index b9b900d785..9500b0a736 100644
--- a/src/main/java/org/kohsuke/github/GHRateLimit.java
+++ b/src/main/java/org/kohsuke/github/GHRateLimit.java
@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
import java.util.Date;
/**
diff --git a/src/main/java/org/kohsuke/github/GHReaction.java b/src/main/java/org/kohsuke/github/GHReaction.java
index 55b26365e0..6a00eb305c 100644
--- a/src/main/java/org/kohsuke/github/GHReaction.java
+++ b/src/main/java/org/kohsuke/github/GHReaction.java
@@ -3,7 +3,7 @@
import java.io.IOException;
import java.net.URL;
-import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
+import static org.kohsuke.github.Previews.*;
/**
* Reaction to issue, comment, PR, and so on.
diff --git a/src/main/java/org/kohsuke/github/GHRef.java b/src/main/java/org/kohsuke/github/GHRef.java
index 8212dc2c3b..c8462d3fe6 100644
--- a/src/main/java/org/kohsuke/github/GHRef.java
+++ b/src/main/java/org/kohsuke/github/GHRef.java
@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
import java.io.IOException;
import java.net.URL;
diff --git a/src/main/java/org/kohsuke/github/GHRelease.java b/src/main/java/org/kohsuke/github/GHRelease.java
index 96421da415..a299add2c5 100644
--- a/src/main/java/org/kohsuke/github/GHRelease.java
+++ b/src/main/java/org/kohsuke/github/GHRelease.java
@@ -8,7 +8,7 @@
import java.util.Date;
import java.util.List;
-import static java.lang.String.format;
+import static java.lang.String.*;
/**
* Release in a github repository.
@@ -45,10 +45,12 @@ public boolean isDraft() {
return draft;
}
+ /**
+ * @deprecated
+ * Use {@link #update()}
+ */
public GHRelease setDraft(boolean draft) throws IOException {
- edit("draft", draft);
- this.draft = draft;
- return this;
+ return update().draft(draft).update();
}
public URL getHtmlUrl() {
@@ -149,10 +151,10 @@ public void delete() throws IOException {
}
/**
- * Edit this release.
+ * Updates this release via a builder.
*/
- private void edit(String key, Object value) throws IOException {
- new Requester(root)._with(key, value).method("PATCH").to(owner.getApiTailUrl("releases/"+id));
+ public GHReleaseUpdater update() {
+ return new GHReleaseUpdater(this);
}
private String getApiTailUrl(String end) {
diff --git a/src/main/java/org/kohsuke/github/GHReleaseBuilder.java b/src/main/java/org/kohsuke/github/GHReleaseBuilder.java
index b1daac956e..e427bdf8a5 100644
--- a/src/main/java/org/kohsuke/github/GHReleaseBuilder.java
+++ b/src/main/java/org/kohsuke/github/GHReleaseBuilder.java
@@ -21,9 +21,7 @@ public GHReleaseBuilder(GHRepository ghRepository, String tag) {
* @param body The release notes body.
*/
public GHReleaseBuilder body(String body) {
- if (body != null) {
- builder.with("body", body);
- }
+ builder.with("body", body);
return this;
}
@@ -35,9 +33,7 @@ public GHReleaseBuilder body(String body) {
* already exists.
*/
public GHReleaseBuilder commitish(String commitish) {
- if (commitish != null) {
- builder.with("target_commitish", commitish);
- }
+ builder.with("target_commitish", commitish);
return this;
}
@@ -56,9 +52,7 @@ public GHReleaseBuilder draft(boolean draft) {
* @param name the name of the release
*/
public GHReleaseBuilder name(String name) {
- if (name != null) {
- builder.with("name", name);
- }
+ builder.with("name", name);
return this;
}
diff --git a/src/main/java/org/kohsuke/github/GHReleaseUpdater.java b/src/main/java/org/kohsuke/github/GHReleaseUpdater.java
new file mode 100644
index 0000000000..a34a5b0bc2
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHReleaseUpdater.java
@@ -0,0 +1,81 @@
+package org.kohsuke.github;
+
+import java.io.IOException;
+
+/**
+ * Modifies {@link GHRelease}.
+ *
+ * @author Kohsuke Kawaguchi
+ * @see GHRelease#update()
+ */
+public class GHReleaseUpdater {
+ private final GHRelease base;
+ private final Requester builder;
+
+ GHReleaseUpdater(GHRelease base) {
+ this.base = base;
+ this.builder = new Requester(base.root);
+ }
+
+ public GHReleaseUpdater tag(String tag) {
+ builder.with("tag_name",tag);
+ return this;
+ }
+
+ /**
+ * @param body The release notes body.
+ */
+ public GHReleaseUpdater body(String body) {
+ builder.with("body", body);
+ return this;
+ }
+
+ /**
+ * Specifies the commitish value that determines where the Git tag is created from. Can be any branch or
+ * commit SHA.
+ *
+ * @param commitish Defaults to the repository’s default branch (usually "master"). Unused if the Git tag
+ * already exists.
+ */
+ public GHReleaseUpdater commitish(String commitish) {
+ builder.with("target_commitish", commitish);
+ return this;
+ }
+
+ /**
+ * Optional.
+ *
+ * @param draft {@code true} to create a draft (unpublished) release, {@code false} to create a published one.
+ * Default is {@code false}.
+ */
+ public GHReleaseUpdater draft(boolean draft) {
+ builder.with("draft", draft);
+ return this;
+ }
+
+ /**
+ * @param name the name of the release
+ */
+ public GHReleaseUpdater name(String name) {
+ builder.with("name", name);
+ return this;
+ }
+
+ /**
+ * Optional
+ *
+ * @param prerelease {@code true} to identify the release as a prerelease. {@code false} to identify the release
+ * as a full release. Default is {@code false}.
+ */
+ public GHReleaseUpdater prerelease(boolean prerelease) {
+ builder.with("prerelease", prerelease);
+ return this;
+ }
+
+ public GHRelease update() throws IOException {
+ return builder
+ .method("PATCH")
+ .to(base.owner.getApiTailUrl("releases/"+base.id), GHRelease.class).wrap(base.owner);
+ }
+
+}
diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java
index d40549fd78..29cac80d44 100644
--- a/src/main/java/org/kohsuke/github/GHRepository.java
+++ b/src/main/java/org/kohsuke/github/GHRepository.java
@@ -883,6 +883,10 @@ public GHTree getTree(String sha) throws IOException {
return root.retrieve().to(url, GHTree.class).wrap(this);
}
+ public GHTreeBuilder createTree() {
+ return new GHTreeBuilder(this);
+ }
+
/**
* Retrieves the tree for the current GitHub repository, recursively as described in here:
* https://developer.github.com/v3/git/trees/#get-a-tree-recursively
@@ -912,6 +916,10 @@ public GHBlob getBlob(String blobSha) throws IOException {
return root.retrieve().to(target, GHBlob.class);
}
+ public GHBlobBuilder createBlob() {
+ return new GHBlobBuilder(this);
+ }
+
/**
* Reads the content of a blob as a stream for better efficiency.
*
@@ -935,6 +943,10 @@ public GHCommit getCommit(String sha1) throws IOException {
return c;
}
+ public GHCommitBuilder createCommit() {
+ return new GHCommitBuilder(this);
+ }
+
/**
* Lists all the commits.
*/
@@ -1540,6 +1552,19 @@ public GHNotificationStream listNotifications() {
return new GHNotificationStream(root,getApiTailUrl("/notifications"));
}
+ /**
+ * https://developer.github.com/v3/repos/traffic/#views
+ */
+ public GHRepositoryViewTraffic getViewTraffic() throws IOException{
+ return root.retrieve().to(getApiTailUrl("/traffic/views"), GHRepositoryViewTraffic.class);
+ }
+
+ /**
+ * https://developer.github.com/v3/repos/traffic/#clones
+ */
+ public GHRepositoryCloneTraffic getCloneTraffic() throws IOException{
+ return root.retrieve().to(getApiTailUrl("/traffic/clones"), GHRepositoryCloneTraffic.class);
+ }
@Override
public int hashCode() {
diff --git a/src/main/java/org/kohsuke/github/GHRepositoryCloneTraffic.java b/src/main/java/org/kohsuke/github/GHRepositoryCloneTraffic.java
new file mode 100644
index 0000000000..c75198e4a8
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHRepositoryCloneTraffic.java
@@ -0,0 +1,37 @@
+package org.kohsuke.github;
+
+import java.util.List;
+
+/**
+ * Repository clone statistics.
+ *
+ * @see GHRepository#getCloneTraffic()
+ */
+public class GHRepositoryCloneTraffic extends GHRepositoryTraffic {
+ private List clones;
+
+ /*package*/ GHRepositoryCloneTraffic() {
+ }
+
+ /*package*/ GHRepositoryCloneTraffic(Integer count, Integer uniques, List clones) {
+ super(count, uniques);
+ this.clones = clones;
+ }
+
+ public List getClones() {
+ return clones;
+ }
+
+ public List getDailyInfo() {
+ return getClones();
+ }
+
+ public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
+ /*package*/ DailyInfo() {
+ }
+
+ /*package*/ DailyInfo(String timestamp, int count, int uniques) {
+ super(timestamp, count, uniques);
+ }
+ }
+}
diff --git a/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java b/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java
index 03b12f5133..642f7f3d95 100644
--- a/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java
+++ b/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java
@@ -1,7 +1,5 @@
package org.kohsuke.github;
-import java.util.Locale;
-
/**
* Search repositories.
*
diff --git a/src/main/java/org/kohsuke/github/GHRepositoryTraffic.java b/src/main/java/org/kohsuke/github/GHRepositoryTraffic.java
new file mode 100644
index 0000000000..42d07e848d
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHRepositoryTraffic.java
@@ -0,0 +1,54 @@
+package org.kohsuke.github;
+
+import java.util.Date;
+import java.util.List;
+
+public abstract class GHRepositoryTraffic implements TrafficInfo {
+ private int count;
+ private int uniques;
+
+ /*package*/ GHRepositoryTraffic() {
+ }
+
+ /*package*/ GHRepositoryTraffic(int count, int uniques) {
+ this.count = count;
+ this.uniques = uniques;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public int getUniques() {
+ return uniques;
+ }
+
+ public abstract List extends DailyInfo> getDailyInfo();
+
+ public static abstract class DailyInfo implements TrafficInfo {
+ private String timestamp;
+ private int count;
+ private int uniques;
+
+ public Date getTimestamp() {
+ return GitHub.parseDate(timestamp);
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public int getUniques() {
+ return uniques;
+ }
+
+ /*package*/ DailyInfo() {
+ }
+
+ /*package*/ DailyInfo(String timestamp, Integer count, Integer uniques) {
+ this.timestamp = timestamp;
+ this.count = count;
+ this.uniques = uniques;
+ }
+ }
+}
diff --git a/src/main/java/org/kohsuke/github/GHRepositoryViewTraffic.java b/src/main/java/org/kohsuke/github/GHRepositoryViewTraffic.java
new file mode 100644
index 0000000000..f2f1e5b440
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHRepositoryViewTraffic.java
@@ -0,0 +1,37 @@
+package org.kohsuke.github;
+
+import java.util.List;
+
+/**
+ * Repository view statistics.
+ *
+ * @see GHRepository#getViewTraffic()
+ */
+public class GHRepositoryViewTraffic extends GHRepositoryTraffic {
+ private List views;
+
+ /*package*/ GHRepositoryViewTraffic() {
+ }
+
+ /*package*/ GHRepositoryViewTraffic(int count, int uniques, List views) {
+ super(count, uniques);
+ this.views = views;
+ }
+
+ public List getViews() {
+ return views;
+ }
+
+ public List getDailyInfo() {
+ return getViews();
+ }
+
+ public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
+ /*package*/ DailyInfo() {
+ }
+
+ /*package*/ DailyInfo(String timestamp, int count, int uniques) {
+ super(timestamp, count, uniques);
+ }
+ }
+}
diff --git a/src/main/java/org/kohsuke/github/GHThread.java b/src/main/java/org/kohsuke/github/GHThread.java
index a1964b10de..faf4c87b7b 100644
--- a/src/main/java/org/kohsuke/github/GHThread.java
+++ b/src/main/java/org/kohsuke/github/GHThread.java
@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
diff --git a/src/main/java/org/kohsuke/github/GHTreeBuilder.java b/src/main/java/org/kohsuke/github/GHTreeBuilder.java
new file mode 100644
index 0000000000..122c5775b5
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHTreeBuilder.java
@@ -0,0 +1,90 @@
+package org.kohsuke.github;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Builder pattern for creating a new tree.
+ * Based on https://developer.github.com/v3/git/trees/#create-a-tree
+ */
+public class GHTreeBuilder {
+ private final GHRepository repo;
+ private final Requester req;
+
+ private final List treeEntries = new ArrayList();
+
+ @SuppressFBWarnings("URF_UNREAD_FIELD")
+ private static final class TreeEntry {
+ private final String path;
+ private final String mode;
+ private final String type;
+ private String sha;
+ private String content;
+
+ private TreeEntry(String path, String mode, String type) {
+ this.path = path;
+ this.mode = mode;
+ this.type = type;
+ }
+ }
+
+ GHTreeBuilder(GHRepository repo) {
+ this.repo = repo;
+ req = new Requester(repo.root);
+ }
+
+ /**
+ * @param baseTree the SHA of tree you want to update with new data
+ */
+ public GHTreeBuilder baseTree(String baseTree) {
+ req.with("base_tree", baseTree);
+ return this;
+ }
+
+ /**
+ * Adds a new entry to the tree.
+ * Exactly one of the parameters {@code sha} and {@code content} must be non-null.
+ */
+ public GHTreeBuilder entry(String path, String mode, String type, String sha, String content) {
+ TreeEntry entry = new TreeEntry(path, mode, type);
+ entry.sha = sha;
+ entry.content = content;
+ treeEntries.add(entry);
+ return this;
+ }
+
+ /**
+ * Specialized version of {@link #entry(String, String, String, String, String)} for adding an existing blob referred by its SHA.
+ */
+ public GHTreeBuilder shaEntry(String path, String sha, boolean executable) {
+ TreeEntry entry = new TreeEntry(path, executable ? "100755" : "100644", "blob");
+ entry.sha = sha;
+ treeEntries.add(entry);
+ return this;
+ }
+
+ /**
+ * Specialized version of {@link #entry(String, String, String, String, String)} for adding a text file with the specified {@code content}.
+ */
+ public GHTreeBuilder textEntry(String path, String content, boolean executable) {
+ TreeEntry entry = new TreeEntry(path, executable ? "100755" : "100644", "blob");
+ entry.content = content;
+ treeEntries.add(entry);
+ return this;
+ }
+
+ private String getApiTail() {
+ return String.format("/repos/%s/%s/git/trees", repo.getOwnerName(), repo.getName());
+ }
+
+ /**
+ * Creates a tree based on the parameters specified thus far.
+ */
+ public GHTree create() throws IOException {
+ req._with("tree", treeEntries);
+ return req.method("POST").to(getApiTail(), GHTree.class).wrap(repo);
+ }
+}
diff --git a/src/main/java/org/kohsuke/github/GHUserSearchBuilder.java b/src/main/java/org/kohsuke/github/GHUserSearchBuilder.java
index ca40f4d050..3edcd15065 100644
--- a/src/main/java/org/kohsuke/github/GHUserSearchBuilder.java
+++ b/src/main/java/org/kohsuke/github/GHUserSearchBuilder.java
@@ -1,7 +1,5 @@
package org.kohsuke.github;
-import java.util.Locale;
-
/**
* Search users.
*
diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java
index 49c860e4dc..62c1b4e80c 100644
--- a/src/main/java/org/kohsuke/github/GitHub.java
+++ b/src/main/java/org/kohsuke/github/GitHub.java
@@ -27,6 +27,12 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
+import org.apache.commons.codec.Charsets;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -47,18 +53,14 @@
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-import org.apache.commons.codec.Charsets;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.io.IOUtils;
-import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
-import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
-import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
-import static java.util.logging.Level.FINE;
-import static org.kohsuke.github.Previews.DRAX;
+import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*;
+import static java.net.HttpURLConnection.*;
+import static java.util.logging.Level.*;
+import static org.kohsuke.github.Previews.*;
/**
* Root of the GitHub API.
@@ -79,9 +81,10 @@ public class GitHub {
*/
/*package*/ final String encodedAuthorization;
- private final Map users = new Hashtable();
- private final Map orgs = new Hashtable();
-
+ private final ConcurrentMap users;
+ private final ConcurrentMap orgs;
+ // Cache of myself object.
+ private GHMyself myself;
private final String apiUrl;
/*package*/ final RateLimitHandler rateLimitHandler;
@@ -146,6 +149,8 @@ public class GitHub {
}
}
+ users = new ConcurrentHashMap();
+ orgs = new ConcurrentHashMap();
this.rateLimitHandler = rateLimitHandler;
this.abuseLimitHandler = abuseLimitHandler;
@@ -357,13 +362,15 @@ public GHRateLimit rateLimit() throws IOException {
@WithBridgeMethods(GHUser.class)
public GHMyself getMyself() throws IOException {
requireCredential();
+ synchronized (this) {
+ if (this.myself != null) return myself;
+
+ GHMyself u = retrieve().to("/user", GHMyself.class);
- GHMyself u = retrieve().to("/user", GHMyself.class);
-
- u.root = this;
- users.put(u.getLogin(), u);
-
- return u;
+ u.root = this;
+ this.myself = u;
+ return u;
+ }
}
/**
@@ -379,7 +386,7 @@ public GHUser getUser(String login) throws IOException {
return u;
}
-
+
/**
* clears all cached data in order for external changes (modifications and del
*/
@@ -641,6 +648,18 @@ public boolean isCredentialValid() throws IOException {
}
}
+ /*package*/ GHUser intern(GHUser user) throws IOException {
+ if (user==null) return user;
+
+ // if we already have this user in our map, use it
+ GHUser u = users.get(user.getLogin());
+ if (u!=null) return u;
+
+ // if not, remember this new user
+ users.putIfAbsent(user.getLogin(),user);
+ return user;
+ }
+
private static class GHApiInfo {
private String rate_limit_url;
diff --git a/src/main/java/org/kohsuke/github/GitUser.java b/src/main/java/org/kohsuke/github/GitUser.java
index 751aabd378..9cd50bb22a 100644
--- a/src/main/java/org/kohsuke/github/GitUser.java
+++ b/src/main/java/org/kohsuke/github/GitUser.java
@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
import java.util.Date;
/**
diff --git a/src/main/java/org/kohsuke/github/HttpConnector.java b/src/main/java/org/kohsuke/github/HttpConnector.java
index 72f7ce5b05..2d87c148c9 100644
--- a/src/main/java/org/kohsuke/github/HttpConnector.java
+++ b/src/main/java/org/kohsuke/github/HttpConnector.java
@@ -5,7 +5,6 @@
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
-import java.util.concurrent.TimeUnit;
/**
* Pluggability for customizing HTTP request behaviors or using altogether different library.
diff --git a/src/main/java/org/kohsuke/github/HttpException.java b/src/main/java/org/kohsuke/github/HttpException.java
index 16c8e68be2..79def83c2a 100644
--- a/src/main/java/org/kohsuke/github/HttpException.java
+++ b/src/main/java/org/kohsuke/github/HttpException.java
@@ -1,11 +1,10 @@
package org.kohsuke.github;
+import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
-import javax.annotation.CheckForNull;
-
/**
* {@link IOException} for http exceptions because {@link HttpURLConnection} throws un-discerned
* {@link IOException} and it can help to know the http response code to decide how to handle an
diff --git a/src/main/java/org/kohsuke/github/PagedIterable.java b/src/main/java/org/kohsuke/github/PagedIterable.java
index 41c5bfdb0c..1c6eccea36 100644
--- a/src/main/java/org/kohsuke/github/PagedIterable.java
+++ b/src/main/java/org/kohsuke/github/PagedIterable.java
@@ -1,6 +1,5 @@
package org.kohsuke.github;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
diff --git a/src/main/java/org/kohsuke/github/PagedSearchIterable.java b/src/main/java/org/kohsuke/github/PagedSearchIterable.java
index 1efe49a1ef..f23bd6b58c 100644
--- a/src/main/java/org/kohsuke/github/PagedSearchIterable.java
+++ b/src/main/java/org/kohsuke/github/PagedSearchIterable.java
@@ -2,7 +2,6 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.io.IOException;
import java.util.Iterator;
/**
diff --git a/src/main/java/org/kohsuke/github/Requester.java b/src/main/java/org/kohsuke/github/Requester.java
index 576d313f5d..00f11f2426 100644
--- a/src/main/java/org/kohsuke/github/Requester.java
+++ b/src/main/java/org/kohsuke/github/Requester.java
@@ -25,6 +25,11 @@
import com.fasterxml.jackson.databind.JsonMappingException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.WillClose;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -49,18 +54,16 @@
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
-import javax.annotation.WillClose;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import static java.util.Arrays.asList;
-import java.util.logging.Level;
+import static java.util.Arrays.*;
import static java.util.logging.Level.*;
-import static org.kohsuke.github.GitHub.MAPPER;
+import static org.apache.commons.lang.StringUtils.*;
+import static org.kohsuke.github.GitHub.*;
/**
* A builder pattern for making HTTP call and parsing its output.
@@ -76,7 +79,7 @@ class Requester {
* Request method.
*/
private String method = "POST";
- private String contentType = "application/x-www-form-urlencoded";
+ private String contentType = null;
private InputStream body;
/**
@@ -275,7 +278,7 @@ private T _to(String tailApiUrl, Class type, T instance) throws IOExcepti
if (nextLinkMatcher.find()) {
final String link = nextLinkMatcher.group(1);
T nextResult = _to(link, type, instance);
-
+ setResponseHeaders(nextResult);
final int resultLength = Array.getLength(result);
final int nextResultLength = Array.getLength(nextResult);
T concatResult = (T) Array.newInstance(type.getComponentType(), resultLength + nextResultLength);
@@ -285,7 +288,7 @@ private T _to(String tailApiUrl, Class type, T instance) throws IOExcepti
}
}
}
- return result;
+ return setResponseHeaders(result);
} catch (IOException e) {
handleApiError(e);
} finally {
@@ -392,18 +395,19 @@ public String getResponseHeader(String header) {
private void buildRequest() throws IOException {
if (isMethodWithBody()) {
uc.setDoOutput(true);
- uc.setRequestProperty("Content-type", contentType);
if (body == null) {
+ uc.setRequestProperty("Content-type", defaultString(contentType,"application/json"));
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(), json);
} else {
+ uc.setRequestProperty("Content-type", defaultString(contentType,"application/x-www-form-urlencoded"));
try {
byte[] bytes = new byte[32768];
- int read = 0;
+ int read;
while ((read = body.read(bytes)) != -1) {
uc.getOutputStream().write(bytes, 0, read);
}
@@ -585,6 +589,7 @@ private void setRequestMethod(HttpURLConnection uc) throws IOException {
throw new IllegalStateException("Failed to set the request method to "+method);
}
+ @CheckForNull
private T parse(Class type, T instance) throws IOException {
return parse(type, instance, 2);
}
@@ -608,12 +613,13 @@ private T parse(Class type, T instance, int timeouts) throws IOException
String data = IOUtils.toString(r);
if (type!=null)
try {
- return MAPPER.readValue(data,type);
+ return setResponseHeaders(MAPPER.readValue(data, type));
} catch (JsonMappingException e) {
throw (IOException)new IOException("Failed to deserialize " +data).initCause(e);
}
- if (instance!=null)
- return MAPPER.readerForUpdating(instance).readValue(data);
+ if (instance!=null) {
+ return setResponseHeaders(MAPPER.readerForUpdating(instance).readValue(data));
+ }
return null;
} catch (FileNotFoundException e) {
// java.net.URLConnection handles 404 exception has FileNotFoundException, don't wrap exception in HttpException
@@ -621,7 +627,7 @@ private T parse(Class type, T instance, int timeouts) throws IOException
throw e;
} catch (IOException e) {
if (e instanceof SocketTimeoutException && timeouts > 0) {
- LOGGER.log(Level.INFO, "timed out accessing " + uc.getURL() + "; will try " + timeouts + " more time(s)", e);
+ LOGGER.log(INFO, "timed out accessing " + uc.getURL() + "; will try " + timeouts + " more time(s)", e);
return parse(type, instance, timeouts - 1);
}
throw new HttpException(responseCode, responseMessage, uc.getURL(), e);
@@ -630,6 +636,21 @@ private T parse(Class type, T instance, int timeouts) throws IOException
}
}
+ private T setResponseHeaders(T readValue) {
+ if (readValue instanceof GHObject[]) {
+ for (GHObject ghObject : (GHObject[]) readValue) {
+ setResponseHeaders(ghObject);
+ }
+ } else if (readValue instanceof GHObject) {
+ setResponseHeaders((GHObject) readValue);
+ }
+ return readValue;
+ }
+
+ private void setResponseHeaders(GHObject readValue) {
+ readValue.responseHeaderFields = uc.getHeaderFields();
+ }
+
/**
* Handles the "Content-Encoding" header.
*/
@@ -662,13 +683,13 @@ private InputStream wrapStream(InputStream in) throws IOException {
String error = IOUtils.toString(es, "UTF-8");
if (e instanceof FileNotFoundException) {
// pass through 404 Not Found to allow the caller to handle it intelligently
- e = (IOException) new FileNotFoundException(error).initCause(e);
+ e = (IOException) new GHFileNotFoundException(error).withResponseHeaderFields(uc).initCause(e);
} else if (e instanceof HttpException) {
HttpException http = (HttpException) e;
e = new HttpException(error, http.getResponseCode(), http.getResponseMessage(),
http.getUrl(), e);
} else {
- e = (IOException) new IOException(error).initCause(e);
+ e = (IOException) new GHIOException(error).withResponseHeaderFields(uc).initCause(e);
}
} finally {
IOUtils.closeQuietly(es);
diff --git a/src/main/java/org/kohsuke/github/TrafficInfo.java b/src/main/java/org/kohsuke/github/TrafficInfo.java
new file mode 100644
index 0000000000..9b232e9f1a
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/TrafficInfo.java
@@ -0,0 +1,16 @@
+package org.kohsuke.github;
+
+/**
+ * @author Kohsuke Kawaguchi
+ */
+public interface TrafficInfo {
+ /**
+ * Total count of hits.
+ */
+ int getCount();
+
+ /**
+ * Unique visitors.
+ */
+ int getUniques();
+}
diff --git a/src/main/java/org/kohsuke/github/extras/OkHttp3Connector.java b/src/main/java/org/kohsuke/github/extras/OkHttp3Connector.java
new file mode 100644
index 0000000000..d2fd8c6978
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/extras/OkHttp3Connector.java
@@ -0,0 +1,32 @@
+package org.kohsuke.github.extras;
+
+import okhttp3.OkHttpClient;
+import okhttp3.OkUrlFactory;
+import org.kohsuke.github.HttpConnector;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * {@link HttpConnector} for {@link OkHttpClient}.
+ *
+ * Unlike {@link #DEFAULT}, OkHttp does response caching.
+ * Making a conditional request against GitHubAPI and receiving a 304
+ * response does not count against the rate limit.
+ * See http://developer.github.com/v3/#conditional-requests
+ *
+ * @author Roberto Tyley
+ * @author Kohsuke Kawaguchi
+ */
+public class OkHttp3Connector implements HttpConnector {
+ private final OkUrlFactory urlFactory;
+
+ public OkHttp3Connector(OkUrlFactory urlFactory) {
+ this.urlFactory = urlFactory;
+ }
+
+ public HttpURLConnection connect(URL url) throws IOException {
+ return urlFactory.open(url);
+ }
+}
diff --git a/src/test/java/Foo.java b/src/test/java/Foo.java
index 35e5eb5955..540f9fd3ca 100644
--- a/src/test/java/Foo.java
+++ b/src/test/java/Foo.java
@@ -1,10 +1,7 @@
-import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHRepository.Contributor;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
-import java.util.Collection;
-
/**
* @author Kohsuke Kawaguchi
*/
diff --git a/src/test/java/org/kohsuke/github/AppTest.java b/src/test/java/org/kohsuke/github/AppTest.java
index 248bd0ef11..7087920dd7 100755
--- a/src/test/java/org/kohsuke/github/AppTest.java
+++ b/src/test/java/org/kohsuke/github/AppTest.java
@@ -5,8 +5,6 @@
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
-import org.hamcrest.CoreMatchers;
-import org.junit.Assume;
import org.junit.Test;
import org.kohsuke.github.GHCommit.File;
import org.kohsuke.github.GHOrganization.Permission;
@@ -16,7 +14,6 @@
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
-import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import static org.hamcrest.CoreMatchers.*;
diff --git a/src/test/java/org/kohsuke/github/CommitTest.java b/src/test/java/org/kohsuke/github/CommitTest.java
index c42fceef41..8e4edb1fed 100644
--- a/src/test/java/org/kohsuke/github/CommitTest.java
+++ b/src/test/java/org/kohsuke/github/CommitTest.java
@@ -1,7 +1,6 @@
package org.kohsuke.github;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import org.junit.Test;
import java.io.IOException;
diff --git a/src/test/java/org/kohsuke/github/GHHookTest.java b/src/test/java/org/kohsuke/github/GHHookTest.java
new file mode 100644
index 0000000000..b27484b5e5
--- /dev/null
+++ b/src/test/java/org/kohsuke/github/GHHookTest.java
@@ -0,0 +1,78 @@
+package org.kohsuke.github;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasKey;
+import static org.hamcrest.Matchers.hasValue;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertThat;
+
+
+/**
+ * @author Kanstantsin Shautsou
+ */
+public class GHHookTest {
+
+ @Ignore
+ @Test
+ public void exposeResponceHeaders() throws Exception {
+ String user1Login = "KostyaSha-auto";
+ String user1Pass = "secret";
+
+ String clientId = "90140219451";
+ String clientSecret = "1451245425";
+
+ String orgRepo = "KostyaSha-org/test";
+
+ // some login based user that has access to application
+ final GitHub gitHub = GitHub.connectUsingPassword(user1Login, user1Pass);
+ gitHub.getMyself();
+
+ // we request read
+ final List scopes = Arrays.asList("repo", "read:org", "user:email", "read:repo_hook");
+
+ // application creates token with scopes
+ final GHAuthorization auth = gitHub.createOrGetAuth(clientId, clientSecret, scopes, "", "");
+ String token = auth.getToken();
+ if (StringUtils.isEmpty(token)) {
+ gitHub.deleteAuth(auth.getId());
+ token = gitHub.createOrGetAuth(clientId, clientSecret, scopes, "", "").getToken();
+ }
+
+ /// now create connection using token
+ final GitHub gitHub2 = GitHub.connectUsingOAuth(token);
+ // some repo in organisation
+ final GHRepository repository = gitHub2.getRepository(orgRepo);
+
+ // doesn't fail because we have read access
+ final List hooks = repository.getHooks();
+
+ try {
+ // fails because application isn't approved in organisation and you can find it only after doing real call
+ final GHHook hook = repository.createHook(
+ "my-hook",
+ singletonMap("url", "http://localhost"),
+ singletonList(GHEvent.PUSH),
+ true
+ );
+ } catch (IOException ex) {
+ assertThat(ex, instanceOf(GHFileNotFoundException.class));
+ final GHFileNotFoundException ghFileNotFoundException = (GHFileNotFoundException) ex;
+ final Map> responseHeaderFields = ghFileNotFoundException.getResponseHeaderFields();
+ assertThat(responseHeaderFields, hasKey("X-Accepted-OAuth-Scopes"));
+ assertThat(responseHeaderFields.get("X-Accepted-OAuth-Scopes"),
+ hasItem("admin:repo_hook, public_repo, repo, write:repo_hook")
+ );
+ }
+ }
+}
diff --git a/src/test/java/org/kohsuke/github/GitHubTest.java b/src/test/java/org/kohsuke/github/GitHubTest.java
index 010adc8b24..a418db5fde 100644
--- a/src/test/java/org/kohsuke/github/GitHubTest.java
+++ b/src/test/java/org/kohsuke/github/GitHubTest.java
@@ -8,7 +8,6 @@
import java.util.Map;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.notNullValue;
diff --git a/src/test/java/org/kohsuke/github/PullRequestTest.java b/src/test/java/org/kohsuke/github/PullRequestTest.java
index ce09b2ab13..5ea2628303 100644
--- a/src/test/java/org/kohsuke/github/PullRequestTest.java
+++ b/src/test/java/org/kohsuke/github/PullRequestTest.java
@@ -99,6 +99,19 @@ public void testMergeCommitSHA() throws Exception {
fail();
}
+ @Test
+ public void testSquashMerge() throws Exception {
+ String name = rnd.next();
+ GHRef masterRef = getRepository().getRef("heads/master");
+ GHRef branchRef = getRepository().createRef("refs/heads/" + name, masterRef.getObject().getSha());
+ getRepository().createContent(name, name, name, name);
+ Thread.sleep(1000);
+ GHPullRequest p = getRepository().createPullRequest(name, name, "master", "## test squash");
+ Thread.sleep(1000);
+ p.merge("squash merge", null, GHPullRequest.MergeMethod.SQUASH);
+ branchRef.delete();
+ }
+
@Test
// Requires push access to the test repo to pass
public void setLabels() throws Exception {
diff --git a/src/test/java/org/kohsuke/github/RepositoryTrafficTest.java b/src/test/java/org/kohsuke/github/RepositoryTrafficTest.java
new file mode 100644
index 0000000000..b1f13e6c97
--- /dev/null
+++ b/src/test/java/org/kohsuke/github/RepositoryTrafficTest.java
@@ -0,0 +1,167 @@
+package org.kohsuke.github;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.kohsuke.github.GHRepositoryTraffic.DailyInfo;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
+
+public class RepositoryTrafficTest {
+ final private String login = "kohsuke", repositoryName = "github-api";
+
+ @SuppressWarnings("unchecked")
+ private void checkResponse(T expected, T actual){
+ Assert.assertEquals(expected.getCount(), actual.getCount());
+ Assert.assertEquals(expected.getUniques(), actual.getUniques());
+
+ List extends DailyInfo> expectedList = expected.getDailyInfo();
+ List extends DailyInfo> actualList = actual.getDailyInfo();
+ Iterator extends DailyInfo> expectedIt;
+ Iterator extends DailyInfo> actualIt;
+
+ Assert.assertEquals(expectedList.size(), actualList.size());
+ expectedIt = expectedList.iterator();
+ actualIt = actualList.iterator();
+
+ while(expectedIt.hasNext() && actualIt.hasNext()) {
+ DailyInfo expectedDailyInfo = expectedIt.next();
+ DailyInfo actualDailyInfo = actualIt.next();
+ Assert.assertEquals(expectedDailyInfo.getCount(), actualDailyInfo.getCount());
+ Assert.assertEquals(expectedDailyInfo.getUniques(), actualDailyInfo.getUniques());
+ Assert.assertEquals(expectedDailyInfo.getTimestamp(), actualDailyInfo.getTimestamp());
+ }
+ }
+
+ private void testTraffic(T expectedResult) throws IOException{
+ SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ ObjectMapper mapper = new ObjectMapper().setDateFormat(dateFormat);
+ String mockedResponse = mapper.writeValueAsString(expectedResult);
+
+
+ GitHub gitHub = GitHub.connect(login, null);
+ GitHub gitHubSpy = Mockito.spy(gitHub);
+ GHRepository repo = gitHubSpy.getUser(login).getRepository(repositoryName);
+
+
+ // accessing traffic info requires push access to the repo
+ // since we don't have that, let the mocking begin...
+
+ HttpConnector connectorSpy = Mockito.spy(gitHubSpy.getConnector());
+ Mockito.doReturn(connectorSpy).when(gitHubSpy).getConnector();
+
+
+ // also known as the "uc" in the Requester class
+ HttpURLConnection mockHttpURLConnection = Mockito.mock(HttpURLConnection.class);
+
+
+ // needed for Requester.setRequestMethod
+ Mockito.doReturn("GET").when(mockHttpURLConnection).getRequestMethod();
+
+
+ // this covers calls on "uc" in Requester.setupConnection and Requester.buildRequest
+ URL trafficURL = new URL(
+ "https://api.github.com/repos/"+login+"/"+repositoryName+"/traffic/" +
+ ((expectedResult instanceof GHRepositoryViewTraffic) ? "views" : "clones")
+ );
+ Mockito.doReturn(mockHttpURLConnection).when(connectorSpy).connect(Mockito.eq(trafficURL));
+
+
+ // make Requester.parse work
+ Mockito.doReturn(200).when(mockHttpURLConnection).getResponseCode();
+ Mockito.doReturn("OK").when(mockHttpURLConnection).getResponseMessage();
+ InputStream stubInputStream = IOUtils.toInputStream(mockedResponse, "UTF-8");
+ Mockito.doReturn(stubInputStream).when(mockHttpURLConnection).getInputStream();
+
+ if(expectedResult instanceof GHRepositoryViewTraffic){
+ GHRepositoryViewTraffic views = repo.getViewTraffic();
+ checkResponse(expectedResult, views);
+ }
+ else if(expectedResult instanceof GHRepositoryCloneTraffic) {
+ GHRepositoryCloneTraffic clones = repo.getCloneTraffic();
+ checkResponse(expectedResult, clones);
+ }
+ }
+
+ @Test
+ public void testGetViews() throws IOException{
+ GHRepositoryViewTraffic expectedResult = new GHRepositoryViewTraffic(
+ 21523359,
+ 65534,
+ Arrays.asList(
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-10T00:00:00Z", 3, 2),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-11T00:00:00Z", 9, 4),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-12T00:00:00Z", 27, 8),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-13T00:00:00Z", 81, 16),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-14T00:00:00Z", 243, 32),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-15T00:00:00Z", 729, 64),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-16T00:00:00Z", 2187, 128),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-17T00:00:00Z", 6561, 256),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-18T00:00:00Z", 19683, 512),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-19T00:00:00Z", 59049, 1024),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-20T00:00:00Z", 177147, 2048),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-21T00:00:00Z", 531441, 4096),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-22T00:00:00Z", 1594323, 8192),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-23T00:00:00Z", 4782969, 16384),
+ new GHRepositoryViewTraffic.DailyInfo("2016-10-24T00:00:00Z", 14348907, 32768)
+ )
+ );
+ testTraffic(expectedResult);
+ }
+
+ @Test
+ public void testGetClones() throws IOException{
+ GHRepositoryCloneTraffic expectedResult = new GHRepositoryCloneTraffic(
+ 1500,
+ 455,
+ Arrays.asList(
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-10T00:00:00Z", 10,3),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-11T00:00:00Z", 20,6),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-12T00:00:00Z", 30,5),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-13T00:00:00Z", 40,7),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-14T00:00:00Z", 50,11),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-15T00:00:00Z", 60,12),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-16T00:00:00Z", 70,19),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-17T00:00:00Z", 170,111),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-18T00:00:00Z", 180,70),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-19T00:00:00Z", 190,10),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-20T00:00:00Z", 200,18),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-21T00:00:00Z", 210,8),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-22T00:00:00Z", 220,168),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-23T00:00:00Z", 5,2),
+ new GHRepositoryCloneTraffic.DailyInfo("2016-10-24T00:00:00Z", 45,5)
+ )
+ );
+ testTraffic(expectedResult);
+ }
+
+ @Test
+ public void testGetTrafficStatsAccessFailureDueToInsufficientPermissions() throws IOException {
+ String errorMsg = "Exception should be thrown, since we don't have permission to access repo traffic info.";
+ GitHub gitHub = GitHub.connect(login, null);
+ GHRepository repo = gitHub.getUser(login).getRepository(repositoryName);
+ try {
+ repo.getViewTraffic();
+ Assert.fail(errorMsg);
+ }
+ catch (HttpException ex){
+ }
+ try {
+ repo.getCloneTraffic();
+ Assert.fail(errorMsg);
+ }
+ catch (HttpException ex){
+ }
+ }
+}