Compare commits

..

107 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
2bee34da59 [maven-release-plugin] prepare release github-api-1.15 2011-12-31 21:20:00 -08:00
Kohsuke Kawaguchi
74415b14be bug fix in the error handling 2011-12-19 17:47:08 -08:00
Kohsuke Kawaguchi
1e9a68a16b fixed the behaviour in case the repository doesn't exist. 2011-12-19 17:07:56 -08:00
Kohsuke Kawaguchi
7a3127ed65 added a method to list all members 2011-12-19 17:07:56 -08:00
Kohsuke Kawaguchi
11b0ac19fd PUT apparently requires some payload. 2011-12-19 17:07:56 -08:00
Michael O'Cleirigh
c8eab1f53e OAuth related changes to getMyself() and getUser (username)
Changed how getMyself() works so that in the OAuth case it will use the username less url.

Changed how getUser (username) works in the OAuth case so that you can lookup the specific username using the OAuth token.

Previously getUser (username) would always return the 'myself' user.
2011-11-19 01:08:01 -05:00
Kohsuke Kawaguchi
1c613c5f07 added a method to publicize/conceal 2011-11-15 15:26:47 -08:00
Kohsuke Kawaguchi
b8a63541e8 v3 API changed the meaning of the 'url' field. 2011-11-10 14:30:27 -08:00
Kohsuke Kawaguchi
f25083fde1 more test 2011-11-10 14:25:59 -08:00
Kohsuke Kawaguchi
c86c974400 GHOrganization.createRepository broke, so I fixed that by using V3 API.
That didn't quite work because v3 API has owner as an object, whereas v2 API has owner as string.

So then I fixed all the other retrieval points for GHRepository to use v3.
2011-11-10 14:23:36 -08:00
Kohsuke Kawaguchi
0ad419b026 [maven-release-plugin] prepare for next development iteration 2011-10-26 17:39:15 -07:00
Kohsuke Kawaguchi
a395697980 [maven-release-plugin] prepare release github-api-1.14 2011-10-26 17:39:10 -07:00
Kohsuke Kawaguchi
834a6eb865 GitHub has changed the element ID 2011-10-26 17:38:51 -07:00
Kohsuke Kawaguchi
e1c6927fd8 [maven-release-plugin] prepare for next development iteration 2011-09-15 11:44:31 -07:00
Kohsuke Kawaguchi
f02f2d7585 [maven-release-plugin] prepare release github-api-1.13 2011-09-15 11:44:20 -07:00
Kohsuke Kawaguchi
d4c099d447 Merge pull request #2 from lacostej/lacostej-master
expose issue_updated_at. It looks like a better representation of update
2011-09-06 13:28:30 -07:00
Jerome Lacoste (f19)
5756133840 expose issue_updated_at. It looks like a better representation of update time for an pull request than updated_at 2011-09-06 13:07:50 +02:00
Kohsuke Kawaguchi
6cc966adee [maven-release-plugin] prepare for next development iteration 2011-08-26 18:43:22 -07:00
Kohsuke Kawaguchi
107a1b83c0 [maven-release-plugin] prepare release github-api-1.12 2011-08-26 18:43:18 -07:00
Kohsuke Kawaguchi
1f8e732d3a added command to disable Wiki 2011-08-26 18:36:29 -07:00
Kohsuke Kawaguchi
d5e52a2468 [maven-release-plugin] prepare for next development iteration 2011-08-26 18:12:18 -07:00
Kohsuke Kawaguchi
231a9b11ae [maven-release-plugin] prepare release github-api-1.11 2011-08-26 18:12:14 -07:00
Kohsuke Kawaguchi
a272ab93ec test 2011-08-26 18:11:55 -07:00
Kohsuke Kawaguchi
a693328ec9 this needs credential 2011-08-26 18:06:00 -07:00
Kohsuke Kawaguchi
7d1a043495 fixed a bug in v3 authentication that doesn't support API token 2011-08-26 17:48:56 -07:00
Kohsuke Kawaguchi
0180f8c352 adding a demo code of org fork 2011-08-26 17:35:26 -07:00
Kohsuke Kawaguchi
7c630842b0 bug fix 2011-08-26 17:33:59 -07:00
Kohsuke Kawaguchi
c7f3c5ef8b Started adding mechanism to use v3 API. 2011-08-26 17:26:34 -07:00
Kohsuke Kawaguchi
2b23bfd296 [maven-release-plugin] prepare for next development iteration 2011-07-11 11:20:50 -07:00
Kohsuke Kawaguchi
29bf00b202 [maven-release-plugin] prepare release github-api-1.10 2011-07-11 11:20:45 -07:00
Michael O'Cleirigh
2f97393b62 add getMyOrganizations() to Github and associated changes.
Add JsonOrganizations to support getting a list of my organizations.

Because Github.login is needed we fetch it during the Github.connectUsingOAuth() method.
2011-07-10 15:23:24 -04:00
Michael O'Cleirigh
558d5d7934 Add support for OAuth access_token based authentication.
This requires some changes as there is no login and password
but instead a token at a certain priviledge level.
2011-07-09 16:04:34 -04:00
Kohsuke Kawaguchi
2fdec0d484 no longer doing caching 2011-06-28 17:34:27 -07:00
Kohsuke Kawaguchi
06dd7c83f8 this method runs a lot faster 2011-06-28 17:34:27 -07:00
Kohsuke Kawaguchi
9a88e52260 added methods to retrieve teams 2011-06-28 17:34:27 -07:00
Kohsuke Kawaguchi
ca071d2732 [maven-release-plugin] prepare for next development iteration 2011-06-27 18:44:03 -07:00
Kohsuke Kawaguchi
11048f5a97 [maven-release-plugin] prepare release github-api-1.9 2011-06-27 18:43:58 -07:00
Kohsuke Kawaguchi
fff8975d29 deploy via GitHub pages 2011-06-27 18:38:07 -07:00
Kohsuke Kawaguchi
00d2788206 inherit the POM 2011-06-27 18:37:03 -07:00
Kohsuke Kawaguchi
4d77d9ec9c added a method to enable/disable the issue tracker. 2011-06-27 18:32:52 -07:00
Kohsuke Kawaguchi
bb3bfe4be8 added support for the issue comments 2011-06-24 17:56:51 -07:00
Kohsuke Kawaguchi
561f8397ee adding more operations 2011-06-24 17:45:10 -07:00
Kohsuke Kawaguchi
5e2a27ab75 fall out from issue/pull-request unification 2011-06-24 17:43:43 -07:00
Kohsuke Kawaguchi
f8d4ec267b The standard Java naming convention calls for all capital enum constant. 2011-06-24 17:42:36 -07:00
Kohsuke Kawaguchi
898a190312 unifying issue and pull request. 2011-06-24 17:31:05 -07:00
Kohsuke Kawaguchi
409eda1cbc formatting changes 2011-06-24 17:02:14 -07:00
Kohsuke Kawaguchi
aa2cba06e3 hiding JSON classes from public visibility 2011-06-24 17:01:18 -07:00
Kohsuke Kawaguchi
281c15cb68 Merge branch 'ermau/master' 2011-06-24 17:00:23 -07:00
Kohsuke Kawaguchi
86f77b6b5b [maven-release-plugin] prepare for next development iteration 2011-06-16 23:51:02 -07:00
Kohsuke Kawaguchi
fa761bf280 [maven-release-plugin] prepare release github-api-1.8 2011-06-16 23:50:53 -07:00
Kohsuke Kawaguchi
3cba773184 removed a local test 2011-06-16 23:50:36 -07:00
Kohsuke Kawaguchi
073b0e6d7f added more convenience methods 2011-06-16 22:39:47 -07:00
Kohsuke Kawaguchi
dc150b7680 GitHub uses different formats in different places 2011-06-16 22:20:45 -07:00
Kohsuke Kawaguchi
6c7b891397 added a page-scraping based method of listing up all the pull requests 2011-06-16 22:03:47 -07:00
Kohsuke Kawaguchi
795c642f49 this doesn't work as intended. 2011-06-16 21:36:55 -07:00
Kohsuke Kawaguchi
559cc37055 added pull request support 2011-06-16 21:32:28 -07:00
Kohsuke Kawaguchi
4b5816242a moved the convenience method here so that I can use it from elsewhere 2011-06-16 21:18:40 -07:00
Kohsuke Kawaguchi
2864d650cc added a convenience method 2011-06-16 21:14:58 -07:00
Kohsuke Kawaguchi
bc2da22671 added value equality 2011-06-16 21:04:40 -07:00
Kohsuke Kawaguchi
2df450c199 [maven-release-plugin] prepare for next development iteration 2011-05-28 15:25:48 +02:00
Kohsuke Kawaguchi
b953d0c831 [maven-release-plugin] prepare release github-api-1.7 2011-05-28 15:25:37 +02:00
ermau
70d18631b0 Added basic GitHub issue support 2011-05-27 19:09:16 -04:00
Kohsuke Kawaguchi
ecb71006d8 ignore static fields 2011-03-16 22:03:55 -07:00
Kohsuke Kawaguchi
cffa552ba1 renameTo wasn't updating some fields 2011-03-16 17:12:22 -07:00
Kohsuke Kawaguchi
f6e7ee3253 Merge commit 'github-api-1.6' 2011-03-15 19:11:01 -07:00
Kohsuke Kawaguchi
275b55f674 set a plugin repository, too 2011-03-15 19:08:22 -07:00
Kohsuke Kawaguchi
d3a18d234f set a repository 2011-03-15 19:05:48 -07:00
Kohsuke Kawaguchi
ab2d0cebaf [maven-release-plugin] prepare for next development iteration 2011-03-15 19:03:49 -07:00
Kohsuke Kawaguchi
878468820b [maven-release-plugin] prepare release github-api-1.6 2011-03-15 19:03:45 -07:00
Kohsuke Kawaguchi
6c9ebd1b5f added an ability to set the post commit hook e-mail address 2011-03-15 17:21:20 -07:00
Kohsuke Kawaguchi
985a11d896 Merge commit 'github-api-1.5' 2011-02-23 09:35:35 +09:00
Kohsuke Kawaguchi
c0dff74536 switched the Maven repository 2011-02-23 09:31:02 +09:00
Kohsuke Kawaguchi
c6712ed6d5 [maven-release-plugin] prepare for next development iteration 2011-02-23 09:26:26 +09:00
Kohsuke Kawaguchi
7f82306908 [maven-release-plugin] prepare release github-api-1.5 2011-02-23 09:26:18 +09:00
Kohsuke Kawaguchi
7c599393bf added methods to parse timestamp 2011-02-23 06:58:14 +09:00
Kohsuke Kawaguchi
d1cd06aec4 incorporated the pagenation 2011-02-23 06:47:22 +09:00
Kohsuke Kawaguchi
13c9da9e91 missing the return statement 2010-12-16 15:31:29 -08:00
Kohsuke Kawaguchi
a827e51fa7 added the rename support. 2010-12-16 15:23:17 -08:00
Kohsuke Kawaguchi
637950c8be added a method to fork a repository into an organization. 2010-12-16 13:36:05 -08:00
Kohsuke Kawaguchi
fa40b625cc explicitly declare Wagon 2010-12-14 10:05:28 -08:00
Kohsuke Kawaguchi
15612e6bfd [maven-release-plugin] prepare for next development iteration 2010-12-14 09:54:23 -08:00
Kohsuke Kawaguchi
83a45f9adc [maven-release-plugin] prepare release github-api-1.4 2010-12-14 09:54:18 -08:00
Kohsuke Kawaguchi
d6b1aad91f added an operation to retrieve repositories from a team. 2010-12-12 21:21:31 -08:00
Kohsuke Kawaguchi
bbf045dbf1 bug fix 2010-12-12 21:17:08 -08:00
Kohsuke Kawaguchi
a282cd3f74 handle API rate limit 2010-12-12 10:30:24 -08:00
Kohsuke Kawaguchi
94c307238f unused method 2010-12-12 09:52:34 -08:00
Kohsuke Kawaguchi
ac1c003d54 simplified. 2010-12-12 09:52:00 -08:00
Kohsuke Kawaguchi
01257b4b86 simplified a bit 2010-12-12 09:50:14 -08:00
Kohsuke Kawaguchi
9484a5f2ac fixed a bug in createRepository 2010-12-12 09:33:09 -08:00
Kohsuke Kawaguchi
75144f1563 shouldn't be a part of test 2010-12-11 23:19:57 -08:00
Kohsuke Kawaguchi
f2271e8ee3 added an operation to create a team. 2010-12-11 22:35:03 -08:00
Kohsuke Kawaguchi
2e544ed8ed keeping up with GitHub API changes 2010-12-11 22:34:47 -08:00
Kohsuke Kawaguchi
0212dce1e8 support multiple values for the same key. 2010-12-11 22:26:04 -08:00
Kohsuke Kawaguchi
f96fda2d86 support deleting a repository from organization 2010-12-11 22:16:17 -08:00
Kohsuke Kawaguchi
e413912edd unified the handling of the password and API token. 2010-12-09 16:04:17 -08:00
Kohsuke Kawaguchi
c6558c6527 added a method to test credential. 2010-12-09 15:58:33 -08:00
Kohsuke Kawaguchi
a7c63e5d6c added another factory method 2010-12-09 15:31:51 -08:00
Kohsuke Kawaguchi
1f3b8866e7 added support for repository commit hook access 2010-12-09 10:00:54 -08:00
Kohsuke Kawaguchi
280b8d3b5a [maven-release-plugin] prepare for next development iteration 2010-11-23 21:52:30 -08:00
Kohsuke Kawaguchi
31028a31e6 [maven-release-plugin] prepare release github-api-1.3 2010-11-23 21:52:24 -08:00
Kohsuke Kawaguchi
efdabb819b shouldn't be public 2010-11-23 21:51:37 -08:00
Kohsuke Kawaguchi
e665365f11 introduced the common base type between GHOrganization and GHUser. 2010-11-23 21:51:14 -08:00
Kohsuke Kawaguchi
5159a21de1 doc improvement 2010-11-23 21:36:35 -08:00
Kohsuke Kawaguchi
ea97f8108f member addition and removal 2010-11-23 21:35:50 -08:00
Kohsuke Kawaguchi
9b1dd273fd added team support 2010-11-23 21:29:41 -08:00
Kohsuke Kawaguchi
7cd0d921cb adding support for creating repositories into an organization 2010-11-22 21:25:51 -08:00
Kohsuke Kawaguchi
959945a93d [maven-release-plugin] prepare for next development iteration 2010-04-19 09:38:25 -07:00
28 changed files with 1767 additions and 217 deletions

95
pom.xml
View File

@@ -1,80 +1,43 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.kohsuke</groupId>
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>1</version>
</parent>
<artifactId>github-api</artifactId>
<packaging>jar</packaging>
<version>1.2</version>
<version>1.15</version>
<name>GitHub API for Java</name>
<url>http://kohsuke.org/github-api/</url>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
<distributionManagement>
<repository>
<id>java.net-m2-repository</id>
<url>java-net:/maven2-repository/trunk/repository/</url>
</repository>
<scm>
<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>
</scm>
<distributionManagement>
<site>
<id>kohsuke.org</id>
<url>scp://kohsuke.org/home/kohsuke/kohsuke.org/github-api/</url>
<id>github-pages</id>
<url>gitsite:git@github.com/kohsuke/${project.artifactId}.git</url>
</site>
</distributionManagement>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.0</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-provider-gitexe</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.3</version>
</plugin>
</plugins>
<extensions>
<extension>
<groupId>org.jvnet.wagon-svn</groupId>
<artifactId>wagon-svn</artifactId>
<version>1.9</version>
</extension>
</extensions>
</build>
<scm>
<connection>scm:git:git@github.com:kohsuke/github-api.git</connection>
</scm>
<developers>
<developer>
<id>kohsuke</id>
<name>Kohsuke Kawaguchi</name>
</developer>
</developers>
<licenses>
<license>
<name>MIT License</name>
<distribution>repository</distribution>
<url>http://www.opensource.org/licenses/mit-license.php</url>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>htmlunit</artifactId>
<version>2.6-hudson-2</version>
<exclusions>
<exclusion>
<!-- hides JDK DOM classes in Eclipse -->
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View File

@@ -0,0 +1,17 @@
package org.kohsuke.github;
/**
* Different API versions.
*
* @author Kohsuke Kawaguchi
*/
enum ApiVersion {
V2("https://github.com/api/v2/json"),
V3("https://api.github.com");
final String url;
ApiVersion(String url) {
this.url = url;
}
}

View File

@@ -0,0 +1,71 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
/**
* Identifies a commit in {@link GHPullRequest}.
*
* @author Kohsuke Kawaguchi
*/
public class GHCommitPointer {
private String ref, sha, label;
private GHUser user;
private GHRepository repository;
/**
* This points to the user who owns
* the {@link #repository}.
*/
public GHUser getUser() {
return user;
}
/**
* The repository that contains the commit.
*/
public GHRepository getRepository() {
return repository;
}
/**
* Named ref to the commit.
*/
public String getRef() {
return ref;
}
/**
* SHA1 of the commit.
*/
public String getSha() {
return sha;
}
/**
* String that looks like "USERNAME:REF".
*/
public String getLabel() {
return label;
}
}

View File

@@ -0,0 +1,14 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
public class GHException extends RuntimeException {
public GHException(String message) {
super(message);
}
public GHException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,129 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Represents an issue on GitHub.
*
* @author Eric Maupin
* @author Kohsuke Kawaguchi
*/
public class GHIssue {
GitHub root;
GHRepository owner;
private String gravatar_id,body,title,state,created_at,updated_at,html_url;
private List<String> labels;
private int number,votes,comments;
private int position;
/**
* Repository to which the issue belongs.
*/
public GHRepository getRepository() {
return owner;
}
/**
* The description of this pull request.
*/
public String getBody() {
return body;
}
/**
* ID.
*/
public int getNumber() {
return number;
}
/**
* The HTML page of this issue,
* like https://github.com/jenkinsci/jenkins/issues/100
*/
public URL getUrl() {
return GitHub.parseURL(html_url);
}
public String getTitle() {
return title;
}
public GHIssueState getState() {
return Enum.valueOf(GHIssueState.class, state);
}
public Collection<String> getLabels() {
return Collections.unmodifiableList(labels);
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
/**
* Updates the issue by adding a comment.
*/
public void comment(String message) throws IOException {
new Poster(root).withCredential().with("comment",message).to(getApiRoute("comment"));
}
/**
* Closes this issue.
*/
public void close() throws IOException {
new Poster(root).withCredential().to(getApiRoute("close"));
}
/**
* Reopens this issue.
*/
public void reopen() throws IOException {
new Poster(root).withCredential().to(getApiRoute("reopen"));
}
/**
* Obtains all the comments associated with this issue.
*/
public List<GHIssueComment> getComments() throws IOException {
return root.retrieve(getApiRoute("comments"), JsonIssueComments.class).wrap(this);
}
private String getApiRoute(String verb) {
return "/issues/"+verb+"/"+owner.getOwnerName()+"/"+owner.getName()+"/"+number;
}
}

View File

@@ -0,0 +1,79 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.io.IOException;
import java.util.Date;
/**
* Comment to the issue
*
* @author Kohsuke Kawaguchi
*/
public class GHIssueComment {
GHIssue owner;
private String body, gravatar_id, user, created_at, updated_at;
private int id;
/**
* Gets the issue to which this comment is associated.
*/
public GHIssue getParent() {
return owner;
}
/**
* The comment itself.
*/
public String getBody() {
return body;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
public int getId() {
return id;
}
/**
* Gets the ID of the user who posted this comment.
*/
public String getUserName() {
return user;
}
/**
* Gets the user who posted this comment.
*/
public GHUser getUser() throws IOException {
return owner.root.getUser(user);
}
}

View File

@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
* Copyright (c) 2011, Eric Maupin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonRepository {
public GHRepository repository;
}
public enum GHIssueState {
OPEN,
CLOSED
}

View File

@@ -0,0 +1,127 @@
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.ApiVersion.V3;
/**
* @author Kohsuke Kawaguchi
*/
public class GHOrganization extends GHPerson {
/**
* Creates a new repository.
*
* @return
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
return createRepository(name,description,homepage,getTeams().get(team),isPublic);
}
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
// such API doesn't exist, so fall back to HTML scraping
return new Poster(root,V3).withCredential()
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root);
}
/**
* Teams by their names.
*/
public Map<String,GHTeam> getTeams() throws IOException {
return root.retrieveWithAuth("/organizations/"+login+"/teams",JsonTeams.class).toMap(this);
}
/**
* Publicizes the membership.
*/
public void publicize(GHUser u) throws IOException {
root.retrieveWithAuth3("/orgs/" + login + "/public_members/" + u.getLogin(), null, "PUT");
}
/**
* All the members of this organization.
*/
public List<GHUser> getMembers() throws IOException {
return new AbstractList<GHUser>() {
// these are shallow objects with only some limited values filled out
// TODO: it's better to allow objects to fill themselves in later when missing values are requested
final GHUser[] shallow = root.retrieveWithAuth3("/orgs/" + login + "/members", GHUser[].class);
@Override
public GHUser get(int index) {
try {
return root.getUser(shallow[index].getLogin());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int size() {
return shallow.length;
}
};
}
/**
* Conceals the membership.
*/
public void conceal(GHUser u) throws IOException {
root.retrieveWithAuth3("/orgs/" + login + "/public_members/" + u.getLogin(), null, "DELETE");
}
public enum Permission { ADMIN, PUSH, PULL }
/**
* Creates a new team and assigns the repositories.
*/
public GHTeam createTeam(String name, Permission p, Collection<GHRepository> repositories) throws IOException {
Poster post = new Poster(root).withCredential().with("team[name]", name).with("team[permission]", p.name().toLowerCase());
for (GHRepository r : repositories) {
post.with("team[repo_names][]",r.getOwnerName()+'/'+r.getName());
}
return post.to("/organizations/"+login+"/teams",JsonTeam.class).wrap(this);
}
public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException {
return createTeam(name,p, Arrays.asList(repositories));
}
/**
* List up repositories that has some open pull requests.
*/
public List<GHRepository> getRepositoriesWithOpenPullRequests() throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/dashboard/pulls");
List<GHRepository> r = new ArrayList<GHRepository>();
for (HtmlAnchor e : pg.getElementById("js-issue-list").<HtmlAnchor>selectNodes(".//UL[@class='smallnav']/LI[not(@class='zeroed')]/A")) {
String a = e.getHrefAttribute();
String name = a.substring(a.lastIndexOf('/')+1);
r.add(getRepository(name));
}
return r;
}
/**
* Gets all the open pull requests in this organizataion.
*/
public List<GHPullRequest> getPullRequests() throws IOException {
List<GHPullRequest> all = new ArrayList<GHPullRequest>();
for (GHRepository r : getRepositoriesWithOpenPullRequests()) {
all.addAll(r.getPullRequests(GHIssueState.OPEN));
}
return all;
}
}

View File

@@ -0,0 +1,58 @@
package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
/**
* Common part of {@link GHUser} and {@link GHOrganization}.
*
* @author Kohsuke Kawaguchi
*/
public abstract class GHPerson {
/*package almost final*/ GitHub root;
protected String gravatar_id,login;
protected int public_gist_count,public_repo_count,following_count,id;
/**
* Gets the repositories this user owns.
*/
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
for (int i=1; ; i++) {
Map<String, GHRepository> map = root.retrieve3("/user/" + login + "/repos?per_page=100&page=" + i, JsonRepositories.class).wrap(root);
repositories.putAll(map);
if (map.isEmpty()) break;
}
return Collections.unmodifiableMap(repositories);
}
/**
*
* @return
* null if the repository was not found
*/
public GHRepository getRepository(String name) throws IOException {
try {
return root.retrieve3("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
}
/**
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
*/
public String getGravatarId() {
return gravatar_id;
}
public String getLogin() {
return login;
}
}

View File

@@ -0,0 +1,96 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.net.URL;
import java.util.Date;
import java.util.Locale;
/**
* A pull request.
*
* @author Kohsuke Kawaguchi
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHPullRequest extends GHIssue {
private String closed_at, patch_url, issue_updated_at;
private GHUser issue_user, user;
// labels??
private GHCommitPointer base, head;
private String mergeable, diff_url;
/**
* The URL of the patch file.
* like https://github.com/jenkinsci/jenkins/pull/100.patch
*/
public URL getPatchUrl() {
return GitHub.parseURL(patch_url);
}
/**
* User who submitted a pull request.
*/
public GHUser getUser() {
return user;
}
/**
* This points to where the change should be pulled into,
* but I'm not really sure what exactly it means.
*/
public GHCommitPointer getBase() {
return base;
}
/**
* The change that should be pulled.
*/
public GHCommitPointer getHead() {
return head;
}
public Date getIssueUpdatedAt() {
return GitHub.parseDate(issue_updated_at);
}
/**
* The HTML page of this pull request,
* like https://github.com/jenkinsci/jenkins/pull/100
*/
public URL getUrl() {
return super.getUrl();
}
/**
* The diff file,
* like https://github.com/jenkinsci/jenkins/pull/100.diff
*/
public URL getDiffUrl() {
return GitHub.parseURL(diff_url);
}
public Date getClosedAt() {
return GitHub.parseDate(closed_at);
}
}

View File

@@ -23,26 +23,49 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import static java.util.Arrays.asList;
import static java.util.Arrays.*;
import static org.kohsuke.github.ApiVersion.V3;
/**
* A repository on GitHub.
*
* @author Kohsuke Kawaguchi
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHRepository {
/*package almost final*/ GitHub root;
private String description, homepage, url, name, owner;
private String description, homepage, name;
private String url; // this is the API url
private String html_url; // this is the UI
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, _private, has_downloads;
private int watchers,forks;
private int watchers,forks,open_issues;
private String created_at, pushed_at;
public String getDescription() {
return description;
@@ -52,8 +75,11 @@ public class GHRepository {
return homepage;
}
/**
* URL of this repository, like 'http://github.com/kohsuke/hudson'
*/
public String getUrl() {
return url;
return html_url;
}
public String getName() {
@@ -61,7 +87,15 @@ public class GHRepository {
}
public GHUser getOwner() throws IOException {
return root.getUser(owner);
return root.getUser(owner.login); // because 'owner' isn't fully populated
}
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
return root.retrieve("/issues/list/" + owner.login + "/" + name + "/" + state.toString().toLowerCase(), JsonIssues.class).wrap(this);
}
protected String getOwnerName() {
return owner.login;
}
public boolean hasIssues() {
@@ -92,17 +126,47 @@ public class GHRepository {
return watchers;
}
public int getOpenIssueCount() {
return open_issues;
}
public Date getPushedAt() {
return GitHub.parseDate(pushed_at);
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
/**
* Gets the collaborators on this repository.
* This set always appear to include the owner.
*/
public Set<GHUser> getCollaborators() throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
for (String u : root.retrieve("/repos/show/"+owner+"/"+name+"/collaborators",JsonCollaborators.class).collaborators)
for (String u : root.retrieve("/repos/show/"+owner.login+"/"+name+"/collaborators",JsonCollaborators.class).collaborators)
r.add(root.getUser(u));
return Collections.unmodifiableSet(r);
}
/**
* Gets the names of the collaborators on this repository.
* This method deviates from the principle of this library but it works a lot faster than {@link #getCollaborators()}.
*/
public Set<String> getCollaboratorNames() throws IOException {
Set<String> r = new HashSet<String>(root.retrieve("/repos/show/"+owner.login+"/"+name+"/collaborators",JsonCollaborators.class).collaborators);
return Collections.unmodifiableSet(r);
}
/**
* If this repository belongs to an organization, return a set of teams.
*/
public Set<GHTeam> getTeams() throws IOException {
return Collections.unmodifiableSet(root.retrieveWithAuth("/repos/show/"+owner.login+"/"+name+"/teams",JsonTeams.class).toSet(
root.getOrganization(owner.login)));
}
public void addCollaborators(GHUser... users) throws IOException {
addCollaborators(asList(users));
}
@@ -122,38 +186,251 @@ public class GHRepository {
private void modifyCollaborators(Collection<GHUser> users, String op) throws IOException {
verifyMine();
for (GHUser user : users) {
new Poster(root).withCredential().to(root.getApiURL("/repos/collaborators/"+name+ op +user.getLogin()));
new Poster(root).withCredential().to("/repos/collaborators/"+name+ op +user.getLogin());
}
}
public void setEmailServiceHook(String address) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlInput email = (HtmlInput)pg.getElementById("email_address");
email.setValueAttribute(address);
HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("email[active]");
active.setChecked(true);
final HtmlForm f = email.getEnclosingFormOrDie();
f.submit((HtmlButton) f.getElementsByTagName("button").get(0));
}
/**
* Enables or disables the issue tracker for this repository.
*/
public void enableIssueTracker(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_issues]",String.valueOf(v))
.to("/repos/show/" + owner.login + "/" + name);
}
/**
* Enables or disables Wiki for this repository.
*/
public void enableWiki(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_wiki]",String.valueOf(v))
.to("/repos/show/" + owner.login + "/" + name);
}
/**
* Deletes this repository.
*/
public void delete() throws IOException {
verifyMine();
Poster poster = new Poster(root).withCredential();
URL url = root.getApiURL("/repos/delete/" + name);
String url = "/repos/delete/" + owner.login +"/"+name;
DeleteToken token = poster.to(url, DeleteToken.class);
poster.with("delete_token",token.delete_token).to(url);
}
/**
* Forks this repository.
* Forks this repository as your repository.
*
* @return
* Newly forked repository that belong to you.
*/
public GHRepository fork() throws IOException {
GHRepository r = new Poster(root).withCredential().to(root.getApiURL("/repos/fork/" + owner + "/" + name), JsonRepository.class).repository;
r.root = root;
return r;
return new Poster(root,V3).withCredential().to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class, "POST").wrap(root);
}
/**
* Forks this repository into an organization.
*
* @return
* Newly forked repository that belong to you.
*/
public GHRepository forkTo(GHOrganization org) throws IOException {
new Poster(root, V3).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin()));
return org.getRepository(name);
}
/**
* Rename this repository.
*/
public void renameTo(String newName) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
for (HtmlForm f : pg.getForms()) {
if (!f.getActionAttribute().endsWith("/rename")) continue;
try {
f.getInputByName("name").setValueAttribute(newName);
f.submit((HtmlButton)f.getElementsByTagName("button").get(0));
// overwrite fields
final GHRepository r = getOwner().getRepository(newName);
for (Field fi : getClass().getDeclaredFields()) {
if (Modifier.isStatic(fi.getModifiers())) continue;
fi.setAccessible(true);
try {
fi.set(this,fi.get(r));
} catch (IllegalAccessException e) {
throw (IllegalAccessError)new IllegalAccessError().initCause(e);
}
}
return;
} catch (ElementNotFoundException e) {
// continue
}
}
throw new IllegalArgumentException("Either you don't have the privilege to rename "+owner.login+'/'+name+" or there's a bug in HTML scraping");
}
/**
* Retrieves a specified pull request.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieveWithAuth("/pulls/" + owner.login + '/' + name + "/" + i, JsonPullRequest.class).wrap(this);
}
/**
* Retrieves all the pull requests of a particular state.
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return root.retrieveWithAuth("/pulls/"+owner.login+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(this);
}
// this is no different from getPullRequests(OPEN)
// /**
// * Retrieves all the pull requests.
// */
// public List<GHPullRequest> getPullRequests() throws IOException {
// return root.retrieveWithAuth("/pulls/"+owner+'/'+name,JsonPullRequests.class).wrap(root);
// }
private void verifyMine() throws IOException {
if (!root.login.equals(owner))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner);
if (!root.login.equals(owner.login))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner.login);
}
/**
* Returns a set that represents the post-commit hook URLs.
* The returned set is live, and changes made to them are reflected to GitHub.
*/
public Set<URL> getPostCommitHooks() {
return postCommitHooks;
}
/**
* Live set view of the post-commit hook.
*/
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
private List<URL> getPostCommitHooks() {
try {
verifyMine();
HtmlForm f = getForm();
List<URL> r = new ArrayList<URL>();
for (HtmlInput i : f.getInputsByName("urls[]")) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
r.add(new URL(v));
}
return r;
} catch (IOException e) {
throw new GHException("Failed to retrieve post-commit hooks",e);
}
}
@Override
public Iterator<URL> iterator() {
return getPostCommitHooks().iterator();
}
@Override
public int size() {
return getPostCommitHooks().size();
}
@Override
public boolean add(URL url) {
try {
String u = url.toExternalForm();
verifyMine();
HtmlForm f = getForm();
List<HtmlInput> controls = f.getInputsByName("urls[]");
for (HtmlInput i : controls) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
if (v.equals(u))
return false; // already there
}
controls.get(controls.size()-1).setValueAttribute(u);
f.submit(null);
return true;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks",e);
}
}
@Override
public boolean remove(Object o) {
try {
String u = ((URL)o).toExternalForm();
verifyMine();
HtmlForm f = getForm();
List<HtmlInput> controls = f.getInputsByName("urls[]");
for (HtmlInput i : controls) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
if (v.equals(u)) {
i.setValueAttribute("");
f.submit(null);
return true;
}
}
return false;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks",e);
}
}
private HtmlForm getForm() throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlForm f = (HtmlForm) pg.getElementById("new_service");
return f;
}
};
/*package*/ GHRepository wrap(GitHub root) {
this.root = root;
return this;
}
@Override
public String toString() {
return "Repository:"+owner+":"+name;
return "Repository:"+owner.login+":"+name;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GHRepository) {
GHRepository that = (GHRepository) obj;
return this.owner.login.equals(that.owner.login)
&& this.name.equals(that.name);
}
return false;
}
}

View File

@@ -0,0 +1,66 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
/**
* A team in GitHub organization.
*
* @author Kohsuke Kawaguchi
*/
public class GHTeam {
private String name,permission;
private int id;
protected /*final*/ GHOrganization org;
public String getName() {
return name;
}
public String getPermission() {
return permission;
}
public int getId() {
return id;
}
/**
* Retrieves the current members.
*/
public Set<GHUser> getMembers() throws IOException {
return org.root.retrieveWithAuth(api("/members"),JsonUsersWithDetails.class).toSet(org.root);
}
public Map<String,GHRepository> getRepositories() throws IOException {
return org.root.retrieveWithAuth3(api("/repos"),JsonRepositories.class).wrap(org.root);
}
/**
* Adds a member to the team.
*/
public void add(GHUser u) throws IOException {
org.root.retrieveWithAuth(api("/members?name="+u.getLogin()),null, "POST");
}
/**
* Removes a member to the team.
*/
public void remove(GHUser u) throws IOException {
org.root.retrieveWithAuth(api("/members?name="+u.getLogin()),null, "DELETE");
}
public void add(GHRepository r) throws IOException {
org.root.retrieveWithAuth(api("/repositories?name="+r.getOwnerName()+'/'+r.getName()),null, "POST");
}
public void remove(GHRepository r) throws IOException {
org.root.retrieveWithAuth(api("/repositories?name="+r.getOwnerName()+'/'+r.getName()),null, "DELETE");
}
private String api(String tail) {
return "/teams/"+id+tail;
}
}

View File

@@ -24,36 +24,16 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static org.kohsuke.github.GitHub.MAPPER;
/**
* Represents an user of GitHub.
*
* @author Kohsuke Kawaguchi
*/
public class GHUser {
/*package almost final*/ GitHub root;
private String gravatar_id,name,company,location,created_at,blog,login,email;
private int public_gist_count,public_repo_count,following_count,id,followers_count;
/**
* Repositories that this user owns.
*/
private transient Map<String,GHRepository> repositories;
/**
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
*/
public String getGravatarId() {
return gravatar_id;
}
public class GHUser extends GHPerson {
private String name,company,location,created_at,blog,email;
private int followers_count;
/**
* Gets the human-readable name of the user, like "Kohsuke Kawaguchi"
@@ -124,38 +104,18 @@ public class GHUser {
return followers_count;
}
/**
* Gets the repositories this user owns.
*/
public Map<String,GHRepository> getRepositories() throws IOException {
if (repositories==null) {
repositories = new TreeMap<String, GHRepository>();
URL url = new URL("http://github.com/api/v2/json/repos/show/" + login);
for (GHRepository r : MAPPER.readValue(url, JsonRepositories.class).repositories) {
r.root = root;
repositories.put(r.getName(),r);
}
}
return Collections.unmodifiableMap(repositories);
}
public GHRepository getRepository(String name) throws IOException {
return getRepositories().get(name);
}
/**
* Follow this user.
*/
public void follow() throws IOException {
new Poster(root).withCredential().to(root.getApiURL("/user/follow/"+login));
new Poster(root).withCredential().to("/user/follow/"+login);
}
/**
* Unfollow this user.
*/
public void unfollow() throws IOException {
new Poster(root).withCredential().to(root.getApiURL("/user/unfollow/"+login));
new Poster(root).withCredential().to("/user/unfollow/"+login);
}
/**

View File

@@ -23,22 +23,34 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.DeserializationConfig.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.introspect.VisibilityChecker.Std;
import sun.misc.BASE64Encoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.ANY;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
import static org.kohsuke.github.ApiVersion.*;
/**
* Root of the GitHub API.
@@ -47,15 +59,38 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
*/
public class GitHub {
/*package*/ final String login;
/*package*/ final String token;
/*package*/ final String encodedAuthorization;
/*package*/ final String password;
/*package*/ final String apiToken;
private final Map<String,GHUser> users = new HashMap<String, GHUser>();
private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>();
private String oauthAccessToken;
private GitHub(String login, String apiToken) {
private GitHub(String login, String apiToken, String password) {
this.login = login;
this.token = apiToken;
this.apiToken = apiToken;
this.password = password;
BASE64Encoder enc = new sun.misc.BASE64Encoder();
if (apiToken!=null || password!=null) {
String userpassword = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
encodedAuthorization = enc.encode(userpassword.getBytes());
} else
encodedAuthorization = null;
}
private GitHub (String oauthAccessToken) throws IOException {
this.password = null;
this.encodedAuthorization = null;
this.oauthAccessToken = oauthAccessToken;
this.apiToken = oauthAccessToken;
this.login = getMyself().getLogin();
}
/**
* Obtains the credential from "~/.github"
*/
@@ -68,56 +103,195 @@ public class GitHub {
} finally {
IOUtils.closeQuietly(in);
}
return new GitHub(props.getProperty("login"),props.getProperty("token"));
return new GitHub(props.getProperty("login"),props.getProperty("token"),props.getProperty("password"));
}
public static GitHub connect(String login, String apiToken) throws IOException {
return new GitHub(login,apiToken);
return new GitHub(login,apiToken,null);
}
public static GitHub connect(String login, String apiToken, String password) throws IOException {
return new GitHub(login,apiToken,password);
}
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
return new GitHub(accessToken);
}
/**
* Connects to GitHub anonymously.
*
* All operations that requires authentication will fail.
*/
public static GitHub connectAnonymously() {
return new GitHub(null,null);
return new GitHub(null,null,null);
}
/*package*/ void requireCredential() {
if (login ==null || token ==null)
if ((login==null || encodedAuthorization==null) && oauthAccessToken == null)
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
}
/*package*/ URL getApiURL(String tail) throws IOException {
return new URL("http://github.com/api/v2/json"+tail);
/*package*/ URL getApiURL(ApiVersion v, String tailApiUrl) throws IOException {
if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + "?access_token=" + oauthAccessToken;
}
return new URL(v.url+tailApiUrl);
}
/*package*/ <T> T retrieve(String tail, Class<T> type) throws IOException {
return MAPPER.readValue(getApiURL(tail),type);
/*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false, V2);
}
/*package*/ <T> T retrieve3(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false, V3);
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type) throws IOException {
return retrieveWithAuth(tailApiUrl,type,"GET");
}
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", true, V3);
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true, V2);
}
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true, V3);
}
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth, ApiVersion v) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) getApiURL(v,tailApiUrl).openConnection();
if (withAuth && this.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method);
if (method.equals("PUT")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-Length","0");
uc.getOutputStream().close();
}
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
if (type==null) {
String data = IOUtils.toString(r);
return null;
}
return MAPPER.readValue(r,type);
} catch (IOException e) {
handleApiError(e,uc);
}
}
}
/**
* Obtains the object that represents the named user.
* If the error is because of the API limit, wait 10 sec and return normally.
* Otherwise throw an exception reporting an error.
*/
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
/*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException {
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
// API limit reached. wait 10 secs and return normally
try {
Thread.sleep(10000);
return;
} catch (InterruptedException _) {
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
}
}
if (e instanceof FileNotFoundException)
throw e; // pass through 404 Not Found to allow the caller to handle it intelligently
InputStream es = uc.getErrorStream();
if (es!=null)
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
else
throw e;
}
/**
* Gets the {@link GHUser} that represents yourself.
*/
public GHUser getMyself() throws IOException {
requireCredential();
GHUser u = null;
if (oauthAccessToken != null) {
u = retrieve("/user/show", JsonUser.class).user;
u.root = this;
users.put(u.getLogin(), u);
return u;
}
else {
return getUser(login);
}
}
/**
* Obtains the object that represents the named user.
*/
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u == null) {
if (oauthAccessToken != null) {
u = retrieve("/user/show/" + login, JsonUser.class).user;
u.root = this;
users.put(u.getLogin(), u);
} else {
u = retrieve("/user/show/" + login, JsonUser.class).user;
u.root = this;
users.put(login, u);
}
}
return u;
}
/**
* Interns the given {@link GHUser}.
*/
protected GHUser getUser(GHUser orig) throws IOException {
GHUser u = users.get(orig.getLogin());
if (u==null) {
u = MAPPER.readValue(getApiURL("/user/show/"+login), JsonUser.class).user;
u.root = this;
users.put(login,u);
orig.root = this;
users.put(login,orig);
return orig;
}
return u;
}
/**
* Gets the {@link GHUser} that represents yourself.
*/
public GHUser getMyself() throws IOException {
requireCredential();
return getUser(login);
public GHOrganization getOrganization(String name) throws IOException {
GHOrganization o = orgs.get(name);
if (o==null) {
o = retrieve("/organizations/"+name,JsonOrganization.class).organization;
o.root = this;
orgs.put(name,o);
}
return o;
}
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
}
/**
* Creates a new repository.
*
@@ -125,16 +299,58 @@ public class GitHub {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
GHRepository r = new Poster(this).withCredential()
return new Poster(this,V3).withCredential()
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic ? 1 : 0).to(getApiURL("/repos/create"), JsonRepository.class).repository;
r.root = this;
return r;
.with("public", isPublic ? 1 : 0).to("/user/repos", GHRepository.class,"POST").wrap(this);
}
/**
* Ensures that the credential is valid.
*/
public boolean isCredentialValid() throws IOException {
try {
retrieveWithAuth("/user/show",JsonUser.class);
return true;
} catch (IOException e) {
return false;
}
}
WebClient createWebClient() throws IOException {
WebClient wc = new WebClient();
wc.setJavaScriptEnabled(false);
wc.setCssEnabled(false);
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/login");
HtmlForm f = pg.getForms().get(0);
f.getInputByName("login").setValueAttribute(login);
f.getInputByName("password").setValueAttribute(password);
f.submit();
return wc;
}
/*package*/ static URL parseURL(String s) {
try {
return s==null ? null : new URL(s);
} catch (MalformedURLException e) {
throw new IllegalStateException("Invalid URL: "+s);
}
}
/*package*/ static Date parseDate(String timestamp) {
for (String f : TIME_FORMATS) {
try {
return new SimpleDateFormat(f).parse(timestamp);
} catch (ParseException e) {
// try next
}
}
throw new IllegalStateException("Unable to parse the timestamp: "+timestamp);
}
/*package*/ static final ObjectMapper MAPPER = new ObjectMapper();
private static final String[] TIME_FORMATS = {"yyyy/MM/dd HH:mm:ss ZZZZ","yyyy-MM-dd'T'HH:mm:ss'Z'"};
static {
MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY));
MAPPER.getDeserializationConfig().set(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

View File

@@ -0,0 +1,38 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
/**
* @author Eric Maupin
*/
class JsonIssue {
GHIssue issue;
GHIssue wrap(GHRepository r) {
issue.owner = r;
issue.root = r.root;
return issue;
}
}

View File

@@ -0,0 +1,16 @@
package org.kohsuke.github;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
*/
class JsonIssueComments {
List<GHIssueComment> comments;
List<GHIssueComment> wrap(GHIssue owner) {
for (GHIssueComment c : comments)
c.owner = owner;
return comments;
}
}

View File

@@ -0,0 +1,38 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.util.List;
class JsonIssues {
List<GHIssue> issues;
public List<GHIssue> wrap(GHRepository owner) {
for (GHIssue issue : issues) {
issue.owner = owner;
issue.root = owner.root;
}
return issues;
}
}

View File

@@ -0,0 +1,8 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonOrganization {
public GHOrganization organization;
}

View File

@@ -0,0 +1,44 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
*
*/
class JsonOrganizations {
public List<GHOrganization> organizations;
public Map<String,GHOrganization> wrap(GitHub root) {
Map<String,GHOrganization> map = new TreeMap<String, GHOrganization>();
for (GHOrganization o : organizations) {
o.root = root;
map.put(o.getLogin(),o);
}
return map;
}
}

View File

@@ -0,0 +1,37 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonPullRequest {
public GHPullRequest pull;
public GHPullRequest wrap(GHRepository owner) {
pull.owner = owner;
pull.root = owner.root;
return pull;
}
}

View File

@@ -0,0 +1,41 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
*/
class JsonPullRequests {
public List<GHPullRequest> pulls;
public List<GHPullRequest> wrap(GHRepository owner) {
for (GHPullRequest pull : pulls) {
pull.owner = owner;
pull.root = owner.root;
}
return pulls;
}
}

View File

@@ -24,10 +24,21 @@
package org.kohsuke.github;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
class JsonRepositories {
public List<GHRepository> repositories;
public Map<String,GHRepository> wrap(GitHub root) {
Map<String,GHRepository> map = new TreeMap<String, GHRepository>();
for (GHRepository r : repositories) {
r.root = root;
map.put(r.getName(),r);
}
return map;
}
}

View File

@@ -0,0 +1,13 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonTeam {
public GHTeam team;
GHTeam wrap(GHOrganization org) {
team.org = org;
return team;
}
}

View File

@@ -0,0 +1,32 @@
package org.kohsuke.github;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
class JsonTeams {
public List<GHTeam> teams;
Map<String, GHTeam> toMap(GHOrganization org) {
Map<String, GHTeam> r = new TreeMap<String, GHTeam>();
for (GHTeam t : teams) {
t.org = org;
r.put(t.getName(),t);
}
return r;
}
Set<GHTeam> toSet(GHOrganization org) {
Set<GHTeam> r = new HashSet<GHTeam>();
for (GHTeam t : teams) {
t.org = org;
r.add(t);
}
return r;
}
}

View File

@@ -0,0 +1,20 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Kohsuke Kawaguchi
*/
class JsonUsersWithDetails {
public List<GHUser> users;
public Set<GHUser> toSet(GitHub root) throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
for (GHUser u : users)
r.add(root.getUser(u));
return r;
}
}

View File

@@ -24,19 +24,23 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.impl.WriterBasedGenerator;
import org.codehaus.jackson.node.ObjectNode;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static org.kohsuke.github.GitHub.MAPPER;
import static org.kohsuke.github.GitHub.*;
/**
* Handles HTTP POST.
@@ -44,29 +48,61 @@ import static org.kohsuke.github.GitHub.MAPPER;
*/
class Poster {
private final GitHub root;
private final Map<String,String> args = new HashMap<String, String>();
private final List<Entry> args = new ArrayList<Entry>();
private boolean authenticate;
private final ApiVersion v;
private static class Entry {
String key;
Object value;
private Entry(String key, Object value) {
this.key = key;
this.value = value;
}
public String toFormArg() throws UnsupportedEncodingException {
return URLEncoder.encode(key,"UTF-8")+'='+URLEncoder.encode(value.toString(),"UTF-8");
}
}
Poster(GitHub root, ApiVersion v) {
this.root = root;
this.v = v;
}
Poster(GitHub root) {
this.root = root;
this(root,ApiVersion.V2);
}
public Poster withCredential() {
root.requireCredential();
return with("login",root.login).with("token",root.token);
}
public Poster with(String key, int value) {
return with(key,String.valueOf(value));
}
public Poster with(String key, String value) {
if (value!=null)
this.args.put(key,value);
authenticate = true;
return this;
}
public void to(URL url) throws IOException {
to(url,null);
public Poster with(String key, int value) {
return _with(key, value);
}
public Poster with(String key, boolean value) {
return _with(key, value);
}
public Poster with(String key, String value) {
return _with(key, value);
}
private Poster _with(String key, Object value) {
if (value!=null) {
args.add(new Entry(key,value));
}
return this;
}
public void to(String tailApiUrl) throws IOException {
to(tailApiUrl,null);
}
/**
@@ -77,33 +113,56 @@ class Poster {
* @return
* {@link Reader} that reads the response.
*/
public <T> T to(URL url, Class<T> type) throws IOException {
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
public <T> T to(String tailApiUrl, Class<T> type) throws IOException {
return to(tailApiUrl,type,"POST");
}
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
uc.setRequestMethod("POST");
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) root.getApiURL(v,tailApiUrl).openConnection();
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
if (authenticate) {
if (v==ApiVersion.V3) {
if (root.password==null)
throw new IllegalArgumentException("V3 API doesn't support API token");
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
} else {
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
}
}
uc.setRequestMethod(method);
StringBuilder body = new StringBuilder();
for (Entry<String, String> e : args.entrySet()) {
if (body.length()>0) body.append('&');
body.append(URLEncoder.encode(e.getKey(),"UTF-8"));
body.append('=');
body.append(URLEncoder.encode(e.getValue(),"UTF-8"));
}
if (v==ApiVersion.V2) {
StringBuilder body = new StringBuilder();
for (Entry e : args) {
if (body.length()>0) body.append('&');
body.append(e.toFormArg());
}
OutputStreamWriter o = new OutputStreamWriter(uc.getOutputStream(), "UTF-8");
o.write(body.toString());
o.close();
OutputStreamWriter o = new OutputStreamWriter(uc.getOutputStream(), "UTF-8");
o.write(body.toString());
o.close();
} else {
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(),json);
}
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
if (type==null) return null;
return MAPPER.readValue(r,type);
} catch (IOException e) {
throw (IOException)new IOException(IOUtils.toString(uc.getErrorStream(),"UTF-8")).initCause(e);
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
String data = IOUtils.toString(r);
if (type==null) {
return null;
}
return MAPPER.readValue(data,type);
} catch (IOException e) {
root.handleApiError(e,uc);
}
}
}
}

View File

@@ -2,21 +2,19 @@
<project name="GitHub API for Java">
<bannerLeft>
<name>GitHub API for Java</name>
<href>http://kohsuke.org/github-api</href>
<href>http://github-api.kohsuke.org/</href>
</bannerLeft>
<skin>
<groupId>org.kohsuke</groupId>
<artifactId>maven-skin</artifactId>
<version>1.1</version>
<version>1.2</version>
</skin>
<!--<bannerRight>-->
<!--<src>http://maven.apache.org/images/maven-small.gif</src>-->
<!--</bannerRight>-->
<body>
<menu name="Git Hub API for Java">
<item name="Introduction" href="index.html"/>
<!--item name="User Guide" href="user-guide.html"/-->
<item name="Download" href="http://maven.dyndns.org/2/org/kohsuke/github-api/"/>
<item name="Introduction" href="/index.html"/>
<item name="Download" href="http://mvnrepository.com/artifact/${project.groupId}/${project.artifactId}"/>
<item name="Source code" href="https://github.com/kohsuke/${project.artifactId}"/>
</menu>
<menu name="References">

View File

@@ -1,21 +1,144 @@
package org.kohsuke;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHOrganization.Permission;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import java.io.IOException;
import java.net.URL;
import java.util.Set;
/**
* Unit test for simple App.
*/
public class AppTest extends TestCase {
public void testApp() throws IOException {
GitHub hub = GitHub.connectAnonymously();
// hub.createRepository("test","test repository",null,true);
// hub.getUser("kohsuke").getRepository("test").delete();
public void testCredentialValid() throws IOException {
assertTrue(GitHub.connect().isCredentialValid());
assertFalse(GitHub.connect("totally","bogus").isCredentialValid());
}
System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators());
public void tryOrgFork() throws Exception {
GitHub gh = GitHub.connect();
gh.getUser("kohsuke").getRepository("rubywm").forkTo(gh.getOrganization("jenkinsci"));
}
public void tryGetTeamsForRepo() throws Exception {
GitHub gh = GitHub.connect();
Set<GHTeam> o = gh.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
System.out.println(o);
}
public void testMembership() throws Exception {
GitHub gitHub = GitHub.connect();
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
System.out.println(members.contains("kohsuke"));
}
public void testApp() throws IOException {
GitHub gitHub = GitHub.connect();
// GHRepository r = gitHub.connect().getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
// r.fork();
// tryDisablingIssueTrackers(gitHub);
// tryDisablingWiki(gitHub);
// GHPullRequest i = gitHub.getOrganization("jenkinsci").getRepository("sandbox").getPullRequest(1);
// for (GHIssueComment c : i.getComments())
// System.out.println(c);
// System.out.println(i);
// gitHub.getMyself().getRepository("perforce-plugin").setEmailServiceHook("kk@kohsuke.org");
// tryRenaming(gitHub);
// tryOrgFork(gitHub);
// testOrganization(gitHub);
// testPostCommitHook(gitHub);
// tryTeamCreation(gitHub);
// t.add(gitHub.getMyself());
// System.out.println(t.getMembers());
// t.remove(gitHub.getMyself());
// System.out.println(t.getMembers());
// GHRepository r = GitHub.connect().getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// r.
// GitHub hub = GitHub.connectAnonymously();
//// hub.createRepository("test","test repository",null,true);
//// hub.getUser("kohsuke").getRepository("test").delete();
//
// System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators());
}
private void tryDisablingIssueTrackers(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasIssues()) {
if (r.getOpenIssueCount()==0) {
System.out.println("DISABLED "+r.getName());
r.enableIssueTracker(false);
} else {
System.out.println("UNTOUCHED "+r.getName());
}
}
}
}
private void tryDisablingWiki(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasWiki()) {
System.out.println("DISABLED "+r.getName());
r.enableWiki(false);
}
}
}
private void tryUpdatingIssueTracker(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("lib-task-reactor");
System.out.println(r.hasIssues());
System.out.println(r.getOpenIssueCount());
r.enableIssueTracker(false);
}
private void tryRenaming(GitHub gitHub) throws IOException {
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
}
private void tryOrgFork(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
System.out.println(gitHub.getUser("rtyler").getRepository("memcache-ada").forkTo(o).getUrl());
}
private void tryTeamCreation(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
GHTeam t = o.createTeam("auto team", Permission.PUSH);
t.add(o.getRepository("auto-test"));
}
private void testPostCommitHook(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getMyself().getRepository("foo");
Set<URL> hooks = r.getPostCommitHooks();
hooks.add(new URL("http://kohsuke.org/test"));
System.out.println(hooks);
hooks.remove(new URL("http://kohsuke.org/test"));
System.out.println(hooks);
}
public void testOrganization() throws IOException {
GitHub gitHub = GitHub.connect();
GHOrganization j = gitHub.getOrganization("jenkinsci");
GHTeam t = j.getTeams().get("Core Developers");
assertNotNull(j.getRepository("jenkins"));
// t.add(labs.getRepository("xyz"));
}
}