Compare commits

..

45 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
47409a9a99 [maven-release-plugin] prepare release github-api-1.89 2017-09-09 13:28:31 -07:00
Kohsuke Kawaguchi
60bfea2d3b Bug fix 2017-09-09 13:22:16 -07:00
Kohsuke Kawaguchi
d3ed8eaed5 Merge pull request #339 2017-09-09 13:07:18 -07:00
Kohsuke Kawaguchi
692dccf110 Massaging the change a bit 2017-09-09 13:03:18 -07:00
Kohsuke Kawaguchi
92caf98683 Reverting java1.6 change which I assume is accidental.
Not that I really care about Java5 but I think that change should
be done separatel & intentionally
2017-09-09 12:57:52 -07:00
Kohsuke Kawaguchi
6178d38895 connector usage is unsynchronized 2017-09-09 12:47:18 -07:00
Kohsuke Kawaguchi
40fb38a9ba Window focus problem 2017-09-09 12:45:58 -07:00
Kohsuke Kawaguchi
20e68d53fd Merge pull request #283 2017-09-09 12:45:02 -07:00
Kohsuke Kawaguchi
2d3557e049 Improved the intern logic
if the user record does not exist yet, there's no need to fetch that
eagerly, as they are fetched on demand via the populate() method.
2017-09-09 12:44:12 -07:00
Kohsuke Kawaguchi
d8f4bc7395 Added updater
This solves #331 differently
2017-09-09 12:31:10 -07:00
Kohsuke Kawaguchi
353f9bb809 pointless null check since the with method already does it 2017-09-09 12:23:40 -07:00
Kohsuke Kawaguchi
ccfe3ad4f7 Merge pull request #333 2017-09-09 12:17:39 -07:00
Kohsuke Kawaguchi
9012820c03 Massage the signature a bit.
AFAICT sha and merge_method are not mutually exclusive.
2017-09-09 12:17:21 -07:00
Kohsuke Kawaguchi
fe2af19e42 Unused constant 2017-09-09 12:15:21 -07:00
Kohsuke Kawaguchi
f721e053f1 Added convenience connector for OkHttp3
Note that the existing one needs to be kept for compatibility with OkHttp2
2017-09-09 12:11:36 -07:00
Kohsuke Kawaguchi
e6ad9feb84 Keeping Findbugs happy 2017-09-09 12:05:39 -07:00
Kohsuke Kawaguchi
635350c40e Additional naming consistency change 2017-09-09 12:02:44 -07:00
Kohsuke Kawaguchi
17edd33703 Reorganized imports following #337 2017-09-09 12:00:23 -07:00
Kohsuke Kawaguchi
b0f2a871c6 Merge pull request #337 2017-09-09 11:58:13 -07:00
Kohsuke Kawaguchi
8928a8a1dc Content type should be JSON by default when sending JSON.
This solves #350 a little differently.
2017-09-09 11:51:55 -07:00
Kohsuke Kawaguchi
2b6f37a6cc Merge pull request #361 2017-09-09 11:48:33 -07:00
Kohsuke Kawaguchi
f3a3b87861 Defined entry points 2017-09-09 11:48:25 -07:00
Kohsuke Kawaguchi
9cf6ee78d4 Pointless string conversion 2017-09-09 11:44:09 -07:00
Kohsuke Kawaguchi
bbc2f3962f Proper access control modifier 2017-09-09 11:44:02 -07:00
Kohsuke Kawaguchi
be49eb22d2 Merge pull request #368 2017-09-09 11:40:41 -07:00
Kohsuke Kawaguchi
fb47067215 Naming changes to emphasize that these are just traffic info 2017-09-09 11:40:28 -07:00
Kohsuke Kawaguchi
2c80ef178d Capture commonality between total and daily 2017-09-09 11:39:12 -07:00
Kohsuke Kawaguchi
9af8112148 Tightening up access control and use primitive type 2017-09-09 11:37:18 -07:00
Kohsuke Kawaguchi
57c36f437a [maven-release-plugin] prepare for next development iteration 2017-09-09 08:19:29 -07:00
Kohsuke Kawaguchi
5ed8a34566 [maven-release-plugin] prepare release github-api-1.88 2017-09-09 08:19:20 -07:00
Kohsuke Kawaguchi
ea8df9bd61 javadoc fix 2017-09-09 08:13:51 -07:00
Kohsuke Kawaguchi
b0c51e03b7 [maven-release-plugin] prepare for next development iteration 2017-09-09 08:06:27 -07:00
Matt Mitchell
f2a2ad90b7 Switch to a concurrent hash map 2017-08-29 11:30:25 -07:00
Matt Mitchell
cfe4c0c510 Merge remote-tracking branch 'upstream/master' into synchro-api 2017-08-29 10:38:54 -07:00
adw1n
a1819bf232 Added getClones method to GHRepository (https://developer.github.com/v3/repos/traffic/#clones). 2017-08-01 06:38:28 +02:00
adw1n
8accf07d46 Changed timestamp (GHRepositoryViews.DayViews.timestamp) field type from String to Date. 2017-08-01 04:02:10 +02:00
adw1n
6dcbace572 Added getViews method to GHRepository.
getViews implements https://developer.github.com/v3/repos/traffic/#views
2017-08-01 03:38:46 +02:00
Greg Gianforcaro
e90c86ec2f Remove Preview status, merge_method is now out of preview 2017-07-18 11:36:38 -04:00
Greg Gianforcaro
971ae1fa4d Merge branch 'master' into pr-merge-method 2017-07-18 11:33:20 -04:00
Serban Iordache
b537f9925b issue #360: Add support for committing multiple files 2017-07-07 16:21:11 +02:00
Kanstantsin Shautsou
be081eec3f Inject responce headers in GHObject and Exceptions.
GH has specific to GET/POST headers required for analysing in case of error.

Signed-off-by: Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
2017-02-10 04:11:20 +03:00
Kanstantsin Shautsou
55b00a87f6 Set 1.6 level. I'm not so old.
Signed-off-by: Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
2017-02-10 03:28:06 +03:00
Sebastian Kürten
fafe6b0ff7 Remove unused imports
Especially also remove the unsued import of
javax.xml.bind.DatatypeConverter from GHContent which is non-public API
as of Java 8
2017-02-09 18:15:20 +01:00
Greg Gianforcaro
5b156006fb Add 'Preview' support for MergeMethod on GHPullRequest
- Add 'polaris' preview
- Add MergeMethod Enum
- Add merge method to GHPullRequest which takes a MergeMethod
2017-01-27 23:36:54 -05:00
Matt Mitchell
9f3f644b83 Add some level of synchronization to the root of the API
This adds some synchronization to the maps at the root of the API to avoid duplicated calls to the actual GH REST API.  Specifically this is targeted around the two maps, orgs and users.  This fix makes the GHPRB jenkins plugin behave much better when there are lots of projects that could build for a specific repo (even if only a few are actually triggered)

There are also a few fixes around GHUser and GHPullRequest
* GHPullRequest was checking a field that may be null (merged_by) when determining whether to fetch details.  An unmerged PR would make a bunch of Github API calls for each property accessed.
* Where GHUser was returned in various objects, we weren't going through the caching mechanism at the root, so calls to APIs on GHUSer often resulted in new REST calls.  Instead, return from the cache wherever possible.
2016-06-08 10:43:40 -07:00
63 changed files with 1045 additions and 129 deletions

16
pom.xml
View File

@@ -7,7 +7,7 @@
</parent>
<artifactId>github-api</artifactId>
<version>1.87</version>
<version>1.89</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -16,7 +16,7 @@
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
<url>http://${project.artifactId}.kohsuke.org/</url>
<tag>github-api-1.87</tag>
<tag>github-api-1.89</tag>
</scm>
<distributionManagement>
@@ -111,6 +111,12 @@
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
@@ -144,6 +150,12 @@
<version>2.7.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>3.4.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>wordnet-random-name</artifactId>

View File

@@ -1,9 +1,9 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**

View File

@@ -0,0 +1,49 @@
package org.kohsuke.github;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
/**
* Builder pattern for creating a new blob.
* Based on https://developer.github.com/v3/git/blobs/#create-a-blob
*/
public class GHBlobBuilder {
private final GHRepository repo;
private final Requester req;
GHBlobBuilder(GHRepository repo) {
this.repo = repo;
req = new Requester(repo.root);
}
/**
* Configures a blob with the specified text {@code content}.
*/
public GHBlobBuilder textContent(String content) {
req.with("content", content);
req.with("encoding", "utf-8");
return this;
}
/**
* Configures a blob with the specified binary {@code content}.
*/
public GHBlobBuilder binaryContent(byte[] content) {
String base64Content = Base64.encodeBase64String(content);
req.with("content", base64Content);
req.with("encoding", "base64");
return this;
}
private String getApiTail() {
return String.format("/repos/%s/%s/git/blobs", repo.getOwnerName(), repo.getName());
}
/**
* Creates a blob based on the parameters specified thus far.
*/
public GHBlob create() throws IOException {
return req.method("POST").to(getApiTail(), GHBlob.class);
}
}

View File

@@ -1,14 +1,13 @@
package org.kohsuke.github;
import static org.kohsuke.github.Previews.LOKI;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import static org.kohsuke.github.Previews.*;
/**
* A branch in a repository.

View File

@@ -1,11 +1,10 @@
package org.kohsuke.github;
import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"URF_UNREAD_FIELD" }, justification = "JSON API")
public class GHBranchProtection {

View File

@@ -1,6 +1,6 @@
package org.kohsuke.github;
import static org.kohsuke.github.Previews.LOKI;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.ArrayList;
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import static org.kohsuke.github.Previews.*;
/**
* Builder to configure the branch protection settings.

View File

@@ -2,6 +2,7 @@ package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.AbstractList;

View File

@@ -0,0 +1,92 @@
package org.kohsuke.github;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
/**
* Builder pattern for creating a new commit.
* Based on https://developer.github.com/v3/git/commits/#create-a-commit
*/
public class GHCommitBuilder {
private final GHRepository repo;
private final Requester req;
private final List<String> parents = new ArrayList<String>();
private static final class UserInfo {
private final String name;
private final String email;
private final String date;
private UserInfo(String name, String email, Date date) {
this.name = name;
this.email = email;
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(tz);
this.date = df.format((date != null) ? date : new Date());
}
}
GHCommitBuilder(GHRepository repo) {
this.repo = repo;
req = new Requester(repo.root);
}
/**
* @param message the commit message
*/
public GHCommitBuilder message(String message) {
req.with("message", message);
return this;
}
/**
* @param tree the SHA of the tree object this commit points to
*/
public GHCommitBuilder tree(String tree) {
req.with("tree", tree);
return this;
}
/**
* @param parent the SHA of a parent commit.
*/
public GHCommitBuilder parent(String parent) {
parents.add(parent);
return this;
}
/**
* Configures the author of this commit.
*/
public GHCommitBuilder author(String name, String email, Date date) {
req._with("author", new UserInfo(name, email, date));
return this;
}
/**
* Configures the committer of this commit.
*/
public GHCommitBuilder committer(String name, String email, Date date) {
req._with("committer", new UserInfo(name, email, date));
return this;
}
private String getApiTail() {
return String.format("/repos/%s/%s/git/commits", repo.getOwnerName(), repo.getName());
}
/**
* Creates a blob based on the parameters specified thus far.
*/
public GHCommit create() throws IOException {
req._with("parents", parents);
return req.method("POST").to(getApiTail(), GHCommit.class).wrapUp(repo);
}
}

View File

@@ -1,11 +1,11 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.Previews.*;
/**
* A comment attached to a commit (or a specific line in a specific file of a commit.)

View File

@@ -39,7 +39,8 @@ public class GHCommitPointer {
* This points to the user who owns
* the {@link #getRepository()}.
*/
public GHUser getUser() {
public GHUser getUser() throws IOException {
if (user != null) return user.root.intern(user);
return user;
}

View File

@@ -1,9 +1,9 @@
package org.kohsuke.github;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
/**
* Search commits.
*

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
/**
@@ -45,8 +46,8 @@ public class GHCommitStatus extends GHObject {
return description;
}
public GHUser getCreator() {
return creator;
public GHUser getCreator() throws IOException {
return root.intern(creator);
}
public String getContext() {

View File

@@ -7,8 +7,6 @@ import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.bind.DatatypeConverter;
/**
* A Content of a repository.
*

View File

@@ -1,9 +1,9 @@
package org.kohsuke.github;
import java.io.IOException;
import org.apache.commons.lang.builder.ToStringBuilder;
import java.io.IOException;
public class GHDeployKey {
protected String url, key, title;

View File

@@ -1,6 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
public class GHDeployment extends GHObject {
@@ -41,8 +41,8 @@ public class GHDeployment extends GHObject {
public String getEnvironment() {
return environment;
}
public GHUser getCreator() {
return creator;
public GHUser getCreator() throws IOException {
return root.intern(creator);
}
public String getRef() {
return ref;

View File

@@ -1,7 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Locale;
public class GHDeploymentStatusBuilder {
private final Requester builder;

View File

@@ -1,11 +1,11 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.databind.node.ObjectNode;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.Date;
/**
* Represents an event.
*

View File

@@ -3,6 +3,7 @@ package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.Reader;
import java.util.List;
@@ -77,7 +78,7 @@ public abstract class GHEventPayload {
throw new IllegalStateException("Expected pull_request payload, but got something else. Maybe we've got another type of event?");
if (repository!=null) {
repository.wrap(root);
pull_request.wrap(repository);
pull_request.wrapUp(repository);
} else {
pull_request.wrapUp(root);
}

View File

@@ -0,0 +1,34 @@
package org.kohsuke.github;
import javax.annotation.CheckForNull;
import java.io.FileNotFoundException;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
/**
* Request/responce contains useful metadata.
* Custom exception allows store info for next diagnostics.
*
* @author Kanstantsin Shautsou
*/
public class GHFileNotFoundException extends FileNotFoundException {
protected Map<String, List<String>> responseHeaderFields;
public GHFileNotFoundException() {
}
public GHFileNotFoundException(String s) {
super(s);
}
@CheckForNull
public Map<String, List<String>> getResponseHeaderFields() {
return responseHeaderFields;
}
GHFileNotFoundException withResponseHeaderFields(HttpURLConnection urlConnection) {
this.responseHeaderFields = urlConnection.getHeaderFields();
return this;
}
}

View File

@@ -38,8 +38,8 @@ public class GHGist extends GHObject {
/**
* User that owns this Gist.
*/
public GHUser getOwner() {
return owner;
public GHUser getOwner() throws IOException {
return root.intern(owner);
}
public String getForksUrl() {

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;

View File

@@ -5,7 +5,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**

View File

@@ -0,0 +1,34 @@
package org.kohsuke.github;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
/**
* Request/responce contains useful metadata.
* Custom exception allows store info for next diagnostics.
*
* @author Kanstantsin Shautsou
*/
public class GHIOException extends IOException {
protected Map<String, List<String>> responseHeaderFields;
public GHIOException() {
}
public GHIOException(String message) {
super(message);
}
@CheckForNull
public Map<String, List<String>> getResponseHeaderFields() {
return responseHeaderFields;
}
GHIOException withResponseHeaderFields(HttpURLConnection urlConnection) {
this.responseHeaderFields = urlConnection.getHeaderFields();
return this;
}
}

View File

@@ -288,8 +288,8 @@ public class GHIssue extends GHObject implements Reactable{
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
}
public GHUser getAssignee() {
return assignee;
public GHUser getAssignee() throws IOException {
return root.intern(assignee);
}
public List<GHUser> getAssignees() {
@@ -299,8 +299,8 @@ public class GHIssue extends GHObject implements Reactable{
/**
* User who submitted the issue.
*/
public GHUser getUser() {
return user;
public GHUser getUser() throws IOException {
return root.intern(user);
}
/**
@@ -311,12 +311,16 @@ public class GHIssue extends GHObject implements Reactable{
* even for an issue that's already closed. See
* https://github.com/kohsuke/github-api/issues/60.
*/
public GHUser getClosedBy() {
public GHUser getClosedBy() throws IOException {
if(!"closed".equals(state)) return null;
if(closed_by != null) return closed_by;
//TODO closed_by = owner.getIssue(number).getClosed_by();
return closed_by;
//TODO
/*
if (closed_by==null) {
closed_by = owner.getIssue(number).getClosed_by();
}
*/
return root.intern(closed_by);
}
public int getCommentsCount(){

View File

@@ -26,7 +26,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.Previews.*;
/**
* Comment to the issue

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* Search issues.
*

View File

@@ -32,7 +32,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static org.kohsuke.github.Previews.DRAX;
import static org.kohsuke.github.Previews.*;
/**
* The GitHub Preview API's license information

View File

@@ -27,8 +27,8 @@ public class GHMilestone extends GHObject {
return owner;
}
public GHUser getCreator() {
return creator;
public GHUser getCreator() throws IOException {
return root.intern(creator);
}
public Date getDueOn() {

View File

@@ -6,7 +6,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

View File

@@ -3,14 +3,15 @@ package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.lang.reflect.FieldUtils;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Most (all?) domain objects in GitHub seems to have these 4 properties.
@@ -18,6 +19,11 @@ import java.util.Date;
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
public abstract class GHObject {
/**
* Capture response HTTP headers on the state object.
*/
protected Map<String, List<String>> 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.
*
* <p>
* 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<String, List<String>> getResponseHeaderFields() {
return responseHeaderFields;
}
/**
* When was this resource created?
*/

View File

@@ -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.Arrays;
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 class GHPullRequest extends GHIssue {
* 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 class GHPullRequest extends GHIssue {
* 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);

View File

@@ -26,7 +26,7 @@ package org.kohsuke.github;
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

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
/**

View File

@@ -3,7 +3,7 @@ package org.kohsuke.github;
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.

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;

View File

@@ -8,7 +8,7 @@ import java.util.Arrays;
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 class GHRelease extends GHObject {
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 class GHRelease extends GHObject {
}
/**
* 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) {

View File

@@ -21,9 +21,7 @@ public class GHReleaseBuilder {
* @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 class GHReleaseBuilder {
* 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 class GHReleaseBuilder {
* @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;
}

View File

@@ -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 repositorys 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);
}
}

View File

@@ -883,6 +883,10 @@ public class GHRepository extends GHObject {
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 class GHRepository extends GHObject {
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 class GHRepository extends GHObject {
return c;
}
public GHCommitBuilder createCommit() {
return new GHCommitBuilder(this);
}
/**
* Lists all the commits.
*/
@@ -1540,6 +1552,19 @@ public class GHRepository extends GHObject {
return new GHNotificationStream(root,getApiTailUrl("/notifications"));
}
/**
* <a href="https://developer.github.com/v3/repos/traffic/#views">https://developer.github.com/v3/repos/traffic/#views</a>
*/
public GHRepositoryViewTraffic getViewTraffic() throws IOException{
return root.retrieve().to(getApiTailUrl("/traffic/views"), GHRepositoryViewTraffic.class);
}
/**
* <a href="https://developer.github.com/v3/repos/traffic/#clones">https://developer.github.com/v3/repos/traffic/#clones</a>
*/
public GHRepositoryCloneTraffic getCloneTraffic() throws IOException{
return root.retrieve().to(getApiTailUrl("/traffic/clones"), GHRepositoryCloneTraffic.class);
}
@Override
public int hashCode() {

View File

@@ -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<DailyInfo> clones;
/*package*/ GHRepositoryCloneTraffic() {
}
/*package*/ GHRepositoryCloneTraffic(Integer count, Integer uniques, List<DailyInfo> clones) {
super(count, uniques);
this.clones = clones;
}
public List<DailyInfo> getClones() {
return clones;
}
public List<DailyInfo> getDailyInfo() {
return getClones();
}
public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
/*package*/ DailyInfo() {
}
/*package*/ DailyInfo(String timestamp, int count, int uniques) {
super(timestamp, count, uniques);
}
}
}

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* Search repositories.
*

View File

@@ -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;
}
}
}

View File

@@ -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<DailyInfo> views;
/*package*/ GHRepositoryViewTraffic() {
}
/*package*/ GHRepositoryViewTraffic(int count, int uniques, List<DailyInfo> views) {
super(count, uniques);
this.views = views;
}
public List<DailyInfo> getViews() {
return views;
}
public List<DailyInfo> getDailyInfo() {
return getViews();
}
public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
/*package*/ DailyInfo() {
}
/*package*/ DailyInfo(String timestamp, int count, int uniques) {
super(timestamp, count, uniques);
}
}
}

View File

@@ -5,7 +5,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Represents an annotated tag in a {@link GHRepository}
*
* @see GHRepository#getAnnotatedTag()
* @see GHRepository#getTagObject(String)
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")

View File

@@ -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;

View File

@@ -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<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
@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);
}
}

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* Search users.
*

View File

@@ -27,6 +27,12 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
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.List;
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<String,GHUser> users = new Hashtable<String, GHUser>();
private final Map<String,GHOrganization> orgs = new Hashtable<String, GHOrganization>();
private final ConcurrentMap<String,GHUser> users;
private final ConcurrentMap<String,GHOrganization> 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<String, GHUser>();
orgs = new ConcurrentHashMap<String, GHOrganization>();
this.rateLimitHandler = rateLimitHandler;
this.abuseLimitHandler = abuseLimitHandler;
@@ -357,13 +362,15 @@ public class GitHub {
@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 class GitHub {
return u;
}
/**
* clears all cached data in order for external changes (modifications and del
*/
@@ -641,6 +648,18 @@ public class GitHub {
}
}
/*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;

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
/**

View File

@@ -5,7 +5,6 @@ import org.kohsuke.github.extras.ImpatientHttpConnector;
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.

View File

@@ -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

View File

@@ -1,6 +1,5 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

View File

@@ -2,7 +2,6 @@ package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.Iterator;
/**

View File

@@ -25,6 +25,11 @@ package org.kohsuke.github;
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.List;
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 @@ class Requester {
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 @@ class Requester {
}
}
}
return result;
return setResponseHeaders(result);
} catch (IOException e) {
handleApiError(e);
} finally {
@@ -392,18 +395,19 @@ class Requester {
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 @@ class Requester {
throw new IllegalStateException("Failed to set the request method to "+method);
}
@CheckForNull
private <T> T parse(Class<T> type, T instance) throws IOException {
return parse(type, instance, 2);
}
@@ -608,12 +613,13 @@ class Requester {
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).<T>readValue(data);
if (instance!=null) {
return setResponseHeaders(MAPPER.readerForUpdating(instance).<T>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 @@ class Requester {
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 @@ class Requester {
}
}
private <T> 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 @@ class Requester {
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);

View File

@@ -0,0 +1,16 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
public interface TrafficInfo {
/**
* Total count of hits.
*/
int getCount();
/**
* Unique visitors.
*/
int getUniques();
}

View File

@@ -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);
}
}

View File

@@ -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
*/

View File

@@ -5,8 +5,6 @@ import com.google.common.collect.Iterables;
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.io.InputStream;
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.*;

View File

@@ -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;

View File

@@ -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<String> 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<GHHook> 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<String, List<String>> 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")
);
}
}
}

View File

@@ -8,7 +8,6 @@ import java.util.HashMap;
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;

View File

@@ -99,6 +99,19 @@ public class PullRequestTest extends AbstractGitHubApiTestBase {
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 {

View File

@@ -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 <T extends GHRepositoryTraffic> 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 <T extends GHRepositoryTraffic> 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){
}
}
}