Compare commits

..

50 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
ermau
70d18631b0 Added basic GitHub issue support 2011-05-27 19:09:16 -04:00
22 changed files with 796 additions and 309 deletions

112
pom.xml
View File

@@ -1,110 +1,30 @@
<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"> <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> <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> <artifactId>github-api</artifactId>
<packaging>jar</packaging> <version>1.15</version>
<version>1.8</version>
<name>GitHub API for Java</name> <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> <description>GitHub API for Java</description>
<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> <distributionManagement>
<repository>
<id>maven.jenkins-ci.org</id>
<url>http://maven.jenkins-ci.org:8081/content/repositories/releases/</url>
</repository>
<site> <site>
<id>kohsuke.org</id> <id>github-pages</id>
<url>scp://kohsuke.org/home/kohsuke/kohsuke.org/github-api/</url> <url>gitsite:git@github.com/kohsuke/${project.artifactId}.git</url>
</site> </site>
</distributionManagement> </distributionManagement>
<repositories>
<repository>
<id>m.g.o-public</id>
<url>http://maven.glassfish.org/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>m.g.o-public</id>
<url>http://maven.glassfish.org/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<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>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
<version>1.0-beta-7</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> <dependencies>
<dependency> <dependency>
<groupId>org.jvnet.hudson</groupId> <groupId>org.jvnet.hudson</groupId>

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

@@ -0,0 +1,30 @@
/*
* 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;
public enum GHIssueState {
OPEN,
CLOSED
}

View File

@@ -4,15 +4,17 @@ import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor; import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.kohsuke.github.GHPullRequest.State;
import java.io.IOException; import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.kohsuke.github.ApiVersion.V3;
/** /**
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
@@ -24,23 +26,14 @@ public class GHOrganization extends GHPerson {
* Newly created repository. * Newly created repository.
*/ */
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException { 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 // such API doesn't exist, so fall back to HTML scraping
WebClient wc = root.createWebClient(); return new Poster(root,V3).withCredential()
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/repositories/new"); .with("name", name).with("description", description).with("homepage", homepage)
HtmlForm f = pg.getForms().get(1); .with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root);
f.getInputByName("repository[name]").setValueAttribute(name);
f.getInputByName("repository[description]").setValueAttribute(description);
f.getInputByName("repository[homepage]").setValueAttribute(homepage);
f.getSelectByName("team_id").getOptionByText(team).setSelected(true);
f.submit(f.getButtonByCaption("Create Repository"));
return refreshRepository(name);
// GHRepository r = new Poster(root).withCredential()
// .with("name", name).with("description", description).with("homepage", homepage)
// .with("public", isPublic ? 1 : 0).to(root.getApiURL("/organizations/"+login+"/repos/create"), JsonRepository.class).repository;
// r.root = root;
// return r;
} }
/** /**
@@ -50,6 +43,45 @@ public class GHOrganization extends GHPerson {
return root.retrieveWithAuth("/organizations/"+login+"/teams",JsonTeams.class).toMap(this); 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 } public enum Permission { ADMIN, PUSH, PULL }
/** /**
@@ -88,7 +120,7 @@ public class GHOrganization extends GHPerson {
public List<GHPullRequest> getPullRequests() throws IOException { public List<GHPullRequest> getPullRequests() throws IOException {
List<GHPullRequest> all = new ArrayList<GHPullRequest>(); List<GHPullRequest> all = new ArrayList<GHPullRequest>();
for (GHRepository r : getRepositoriesWithOpenPullRequests()) { for (GHRepository r : getRepositoriesWithOpenPullRequests()) {
all.addAll(r.getPullRequests(State.OPEN)); all.addAll(r.getPullRequests(GHIssueState.OPEN));
} }
return all; return all;
} }

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github; package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@@ -17,43 +18,31 @@ public abstract class GHPerson {
protected int public_gist_count,public_repo_count,following_count,id; protected int public_gist_count,public_repo_count,following_count,id;
/**
* Repositories that this user owns.
*/
private transient Map<String,GHRepository> repositories;
/** /**
* Gets the repositories this user owns. * Gets the repositories this user owns.
*/ */
public synchronized Map<String,GHRepository> getRepositories() throws IOException { public synchronized Map<String,GHRepository> getRepositories() throws IOException {
if (repositories==null) { Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
repositories = Collections.synchronizedMap(new TreeMap<String, GHRepository>()); for (int i=1; ; i++) {
for (int i=1; ; i++) { Map<String, GHRepository> map = root.retrieve3("/user/" + login + "/repos?per_page=100&page=" + i, JsonRepositories.class).wrap(root);
Map<String, GHRepository> map = root.retrieve("/repos/show/" + login + "?page=" + i, JsonRepositories.class).wrap(root); repositories.putAll(map);
repositories.putAll(map); if (map.isEmpty()) break;
if (map.isEmpty()) break;
}
} }
return Collections.unmodifiableMap(repositories); return Collections.unmodifiableMap(repositories);
} }
/** /**
* Fetches the repository of the given name from GitHub, and return it. *
* @return
* null if the repository was not found
*/ */
protected GHRepository refreshRepository(String name) throws IOException {
if (repositories==null) getRepositories(); // fetch the base first
GHRepository r = fetchRepository(name);
repositories.put(name,r);
return r;
}
protected GHRepository fetchRepository(String name) throws IOException {
return root.retrieve("/repos/show/" + login + '/' + name, JsonRepository.class).wrap(root);
}
public GHRepository getRepository(String name) throws IOException { public GHRepository getRepository(String name) throws IOException {
return getRepositories().get(name); try {
return root.retrieve3("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
} }
/** /**

View File

@@ -33,26 +33,12 @@ import java.util.Locale;
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
@SuppressWarnings({"UnusedDeclaration"}) @SuppressWarnings({"UnusedDeclaration"})
public class GHPullRequest { public class GHPullRequest extends GHIssue {
/*package almost final*/ GitHub root; private String closed_at, patch_url, issue_updated_at;
private String gravatar_id, closed_at, state, body, created_at, patch_url, issue_updated_at;
private int number, position, comments, votes;
private GHUser issue_user, user; private GHUser issue_user, user;
// labels?? // labels??
private GHCommitPointer base, head; private GHCommitPointer base, head;
private String mergeable, updated_at, html_url, title, diff_url; private String mergeable, diff_url;
public enum State {
OPEN, CLOSED
}
/**
* The description of this pull request.
*/
public String getBody() {
return body;
}
/** /**
* The URL of the patch file. * The URL of the patch file.
@@ -62,13 +48,6 @@ public class GHPullRequest {
return GitHub.parseURL(patch_url); return GitHub.parseURL(patch_url);
} }
/**
* ID.
*/
public int getNumber() {
return number;
}
/** /**
* User who submitted a pull request. * User who submitted a pull request.
*/ */
@@ -76,13 +55,6 @@ public class GHPullRequest {
return user; return user;
} }
/**
* Repository to which the pull request was sent.
*/
public GHRepository getRepository() {
return getBase().getRepository();
}
/** /**
* This points to where the change should be pulled into, * This points to where the change should be pulled into,
* but I'm not really sure what exactly it means. * but I'm not really sure what exactly it means.
@@ -98,16 +70,16 @@ public class GHPullRequest {
return head; return head;
} }
public Date getIssueUpdatedAt() {
return GitHub.parseDate(issue_updated_at);
}
/** /**
* The HTML page of this pull request, * The HTML page of this pull request,
* like https://github.com/jenkinsci/jenkins/pull/100 * like https://github.com/jenkinsci/jenkins/pull/100
*/ */
public URL getUrl() { public URL getUrl() {
return GitHub.parseURL(html_url); return super.getUrl();
}
public String getTitle() {
return title;
} }
/** /**
@@ -121,16 +93,4 @@ public class GHPullRequest {
public Date getClosedAt() { public Date getClosedAt() {
return GitHub.parseDate(closed_at); return GitHub.parseDate(closed_at);
} }
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
public State getState() {
return State.valueOf(state.toUpperCase(Locale.ENGLISH));
}
} }

View File

@@ -32,6 +32,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.net.URL; import java.net.URL;
@@ -47,6 +48,7 @@ import java.util.Locale;
import java.util.Set; import java.util.Set;
import static java.util.Arrays.*; import static java.util.Arrays.*;
import static org.kohsuke.github.ApiVersion.V3;
/** /**
* A repository on GitHub. * A repository on GitHub.
@@ -57,9 +59,12 @@ import static java.util.Arrays.*;
public class GHRepository { public class GHRepository {
/*package almost final*/ GitHub root; /*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 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; private String created_at, pushed_at;
public String getDescription() { public String getDescription() {
@@ -74,7 +79,7 @@ public class GHRepository {
* URL of this repository, like 'http://github.com/kohsuke/hudson' * URL of this repository, like 'http://github.com/kohsuke/hudson'
*/ */
public String getUrl() { public String getUrl() {
return url; return html_url;
} }
public String getName() { public String getName() {
@@ -82,11 +87,15 @@ public class GHRepository {
} }
public GHUser getOwner() throws IOException { 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() { protected String getOwnerName() {
return owner; return owner.login;
} }
public boolean hasIssues() { public boolean hasIssues() {
@@ -117,6 +126,10 @@ public class GHRepository {
return watchers; return watchers;
} }
public int getOpenIssueCount() {
return open_issues;
}
public Date getPushedAt() { public Date getPushedAt() {
return GitHub.parseDate(pushed_at); return GitHub.parseDate(pushed_at);
} }
@@ -132,11 +145,28 @@ public class GHRepository {
*/ */
public Set<GHUser> getCollaborators() throws IOException { public Set<GHUser> getCollaborators() throws IOException {
Set<GHUser> r = new HashSet<GHUser>(); 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)); r.add(root.getUser(u));
return Collections.unmodifiableSet(r); 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 { public void addCollaborators(GHUser... users) throws IOException {
addCollaborators(asList(users)); addCollaborators(asList(users));
} }
@@ -163,21 +193,37 @@ public class GHRepository {
public void setEmailServiceHook(String address) throws IOException { public void setEmailServiceHook(String address) throws IOException {
WebClient wc = root.createWebClient(); WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin"); HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlInput email = (HtmlInput)pg.getElementById("Email_address"); HtmlInput email = (HtmlInput)pg.getElementById("email_address");
email.setValueAttribute(address); email.setValueAttribute(address);
HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("Email[active]"); HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("email[active]");
active.setChecked(true); active.setChecked(true);
final HtmlForm f = email.getEnclosingFormOrDie(); final HtmlForm f = email.getEnclosingFormOrDie();
f.submit((HtmlButton) f.getElementsByTagName("button").get(0)); 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. * Deletes this repository.
*/ */
public void delete() throws IOException { public void delete() throws IOException {
Poster poster = new Poster(root).withCredential(); Poster poster = new Poster(root).withCredential();
String url = "/repos/delete/" + owner +"/"+name; String url = "/repos/delete/" + owner.login +"/"+name;
DeleteToken token = poster.to(url, DeleteToken.class); DeleteToken token = poster.to(url, DeleteToken.class);
poster.with("delete_token",token.delete_token).to(url); poster.with("delete_token",token.delete_token).to(url);
@@ -190,7 +236,7 @@ public class GHRepository {
* Newly forked repository that belong to you. * Newly forked repository that belong to you.
*/ */
public GHRepository fork() throws IOException { public GHRepository fork() throws IOException {
return new Poster(root).withCredential().to("/repos/fork/" + owner + "/" + name, JsonRepository.class).wrap(root); return new Poster(root,V3).withCredential().to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class, "POST").wrap(root);
} }
/** /**
@@ -200,22 +246,8 @@ public class GHRepository {
* Newly forked repository that belong to you. * Newly forked repository that belong to you.
*/ */
public GHRepository forkTo(GHOrganization org) throws IOException { public GHRepository forkTo(GHOrganization org) throws IOException {
WebClient wc = root.createWebClient(); new Poster(root, V3).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin()));
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()); return org.getRepository(name);
for (HtmlForm f : pg.getForms()) {
if (!f.getActionAttribute().endsWith("/fork")) continue;
try {
if (org.getLogin().equals(f.getInputByName("organization").getValueAttribute())) {
// found it
f.submit((HtmlButton)f.getElementsByTagName("button").get(0));
return org.refreshRepository(name);
}
} catch (ElementNotFoundException e) {
// continue
}
}
throw new IllegalArgumentException("Either you don't have the privilege to fork into "+org.getLogin()+" or there's a bug in HTML scraping");
} }
/** /**
@@ -231,7 +263,7 @@ public class GHRepository {
f.submit((HtmlButton)f.getElementsByTagName("button").get(0)); f.submit((HtmlButton)f.getElementsByTagName("button").get(0));
// overwrite fields // overwrite fields
final GHRepository r = getOwner().fetchRepository(newName); final GHRepository r = getOwner().getRepository(newName);
for (Field fi : getClass().getDeclaredFields()) { for (Field fi : getClass().getDeclaredFields()) {
if (Modifier.isStatic(fi.getModifiers())) continue; if (Modifier.isStatic(fi.getModifiers())) continue;
fi.setAccessible(true); fi.setAccessible(true);
@@ -248,21 +280,21 @@ public class GHRepository {
} }
} }
throw new IllegalArgumentException("Either you don't have the privilege to rename "+owner+'/'+name+" or there's a bug in HTML scraping"); 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. * Retrieves a specified pull request.
*/ */
public GHPullRequest getPullRequest(int i) throws IOException { public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieveWithAuth("/pulls/" + owner + '/' + name + "/" + i, JsonPullRequest.class).wrap(root); return root.retrieveWithAuth("/pulls/" + owner.login + '/' + name + "/" + i, JsonPullRequest.class).wrap(this);
} }
/** /**
* Retrieves all the pull requests of a particular state. * Retrieves all the pull requests of a particular state.
*/ */
public List<GHPullRequest> getPullRequests(GHPullRequest.State state) throws IOException { public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return root.retrieveWithAuth("/pulls/"+owner+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(root); return root.retrieveWithAuth("/pulls/"+owner.login+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(this);
} }
// this is no different from getPullRequests(OPEN) // this is no different from getPullRequests(OPEN)
@@ -274,8 +306,8 @@ public class GHRepository {
// } // }
private void verifyMine() throws IOException { private void verifyMine() throws IOException {
if (!root.login.equals(owner)) if (!root.login.equals(owner.login))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner); throw new IOException("Operation not applicable to a repository owned by someone else: "+owner.login);
} }
/** /**
@@ -377,10 +409,14 @@ public class GHRepository {
} }
}; };
/*package*/ GHRepository wrap(GitHub root) {
this.root = root;
return this;
}
@Override @Override
public String toString() { public String toString() {
return "Repository:"+owner+":"+name; return "Repository:"+owner.login+":"+name;
} }
@Override @Override
@@ -392,7 +428,7 @@ public class GHRepository {
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof GHRepository) { if (obj instanceof GHRepository) {
GHRepository that = (GHRepository) obj; GHRepository that = (GHRepository) obj;
return this.owner.equals(that.owner) return this.owner.login.equals(that.owner.login)
&& this.name.equals(that.name); && this.name.equals(that.name);
} }
return false; return false;

View File

@@ -35,7 +35,7 @@ public class GHTeam {
} }
public Map<String,GHRepository> getRepositories() throws IOException { public Map<String,GHRepository> getRepositories() throws IOException {
return org.root.retrieveWithAuth(api("/repositories"),JsonRepositories.class).wrap(org.root); return org.root.retrieveWithAuth3(api("/repos"),JsonRepositories.class).wrap(org.root);
} }
/** /**

View File

@@ -34,7 +34,9 @@ import sun.misc.BASE64Encoder;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@@ -48,6 +50,7 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*; import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
import static org.kohsuke.github.ApiVersion.*;
/** /**
* Root of the GitHub API. * Root of the GitHub API.
@@ -57,23 +60,37 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
public class GitHub { public class GitHub {
/*package*/ final String login; /*package*/ final String login;
/*package*/ final String encodedAuthorization; /*package*/ final String encodedAuthorization;
final String password; /*package*/ final String password;
/*package*/ final String apiToken;
private final Map<String,GHUser> users = new HashMap<String, GHUser>(); private final Map<String,GHUser> users = new HashMap<String, GHUser>();
private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>(); private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>();
private String oauthAccessToken;
private GitHub(String login, String apiToken, String password) { private GitHub(String login, String apiToken, String password) {
this.login = login; this.login = login;
this.apiToken = apiToken;
this.password = password; this.password = password;
BASE64Encoder enc = new sun.misc.BASE64Encoder(); BASE64Encoder enc = new sun.misc.BASE64Encoder();
if (apiToken!=null || password!=null) { if (apiToken!=null || password!=null) {
String userpassword = apiToken!=null ? (login + "/token" + ":" + apiToken) : (login + ':'+password); String userpassword = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
encodedAuthorization = enc.encode(userpassword.getBytes()); encodedAuthorization = enc.encode(userpassword.getBytes());
} else } else
encodedAuthorization = null; 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" * Obtains the credential from "~/.github"
*/ */
@@ -97,6 +114,9 @@ public class GitHub {
return new GitHub(login,apiToken,password); return new GitHub(login,apiToken,password);
} }
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
return new GitHub(accessToken);
}
/** /**
* Connects to GitHub anonymously. * Connects to GitHub anonymously.
* *
@@ -107,33 +127,58 @@ public class GitHub {
} }
/*package*/ void requireCredential() { /*package*/ void requireCredential() {
if (login==null || encodedAuthorization==null) if ((login==null || encodedAuthorization==null) && oauthAccessToken == null)
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor"); throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
} }
/*package*/ URL getApiURL(String tailApiUrl) throws IOException { /*package*/ URL getApiURL(ApiVersion v, String tailApiUrl) throws IOException {
return new URL("http://github.com/api/v2/json"+tailApiUrl); if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + "?access_token=" + oauthAccessToken;
}
return new URL(v.url+tailApiUrl);
} }
/*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException { /*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false); 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 { /*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type) throws IOException {
return retrieveWithAuth(tailApiUrl,type,"GET"); return retrieveWithAuth(tailApiUrl,type,"GET");
} }
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException { /*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, method, true); return _retrieve(tailApiUrl, type, "GET", true, V3);
} }
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth) throws IOException { /*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException {
while (true) {// loop while API rate limit is hit return _retrieve(tailApiUrl, type, method, true, V2);
HttpURLConnection uc = (HttpURLConnection) getApiURL(tailApiUrl).openConnection(); }
if (withAuth) /*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.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method); uc.setRequestMethod(method);
if (method.equals("PUT")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-Length","0");
uc.getOutputStream().close();
}
try { try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8"); InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
@@ -163,21 +208,61 @@ public class GitHub {
} }
} }
throw (IOException)new IOException(IOUtils.toString(uc.getErrorStream(),"UTF-8")).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;
} }
/** /**
* Obtains the object that represents the named user. * Gets the {@link GHUser} that represents yourself.
*/ */
public GHUser getUser(String login) throws IOException { public GHUser getMyself() throws IOException {
GHUser u = users.get(login); requireCredential();
if (u==null) {
u = retrieve("/user/show/"+login,JsonUser.class).user; GHUser u = null;
u.root = this;
users.put(login,u); if (oauthAccessToken != null) {
}
return u; 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}. * Interns the given {@link GHUser}.
@@ -202,14 +287,11 @@ public class GitHub {
return o; return o;
} }
/** public Map<String, GHOrganization> getMyOrganizations() throws IOException {
* Gets the {@link GHUser} that represents yourself. return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
*/
public GHUser getMyself() throws IOException {
requireCredential();
return getUser(login);
} }
/** /**
* Creates a new repository. * Creates a new repository.
* *
@@ -217,9 +299,9 @@ public class GitHub {
* Newly created repository. * Newly created repository.
*/ */
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException { public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
return new Poster(this).withCredential() return new Poster(this,V3).withCredential()
.with("name", name).with("description", description).with("homepage", homepage) .with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic ? 1 : 0).to("/repos/create", JsonRepository.class).wrap(this); .with("public", isPublic ? 1 : 0).to("/user/repos", GHRepository.class,"POST").wrap(this);
} }
/** /**

View File

@@ -1,7 +1,7 @@
/* /*
* The MIT License * 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,16 +21,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package org.kohsuke.github; package org.kohsuke.github;
/** /**
* @author Kohsuke Kawaguchi * @author Eric Maupin
*/ */
class JsonRepository { class JsonIssue {
public GHRepository repository; GHIssue issue;
public GHRepository wrap(GitHub root) { GHIssue wrap(GHRepository r) {
repository.root = root; issue.owner = r;
return repository; 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,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

@@ -29,8 +29,9 @@ package org.kohsuke.github;
class JsonPullRequest { class JsonPullRequest {
public GHPullRequest pull; public GHPullRequest pull;
public GHPullRequest wrap(GitHub root) { public GHPullRequest wrap(GHRepository owner) {
pull.root = root; pull.owner = owner;
pull.root = owner.root;
return pull; return pull;
} }
} }

View File

@@ -31,9 +31,11 @@ import java.util.List;
class JsonPullRequests { class JsonPullRequests {
public List<GHPullRequest> pulls; public List<GHPullRequest> pulls;
public List<GHPullRequest> wrap(GitHub root) { public List<GHPullRequest> wrap(GHRepository owner) {
for (GHPullRequest pull : pulls) for (GHPullRequest pull : pulls) {
pull.root = root; pull.owner = owner;
pull.root = owner.root;
}
return pulls; return pulls;
} }
} }

View File

@@ -3,7 +3,7 @@ package org.kohsuke.github;
/** /**
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
public class JsonTeam { class JsonTeam {
public GHTeam team; public GHTeam team;
GHTeam wrap(GHOrganization org) { GHTeam wrap(GHOrganization org) {

View File

@@ -1,7 +1,9 @@
package org.kohsuke.github; package org.kohsuke.github;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
/** /**
@@ -18,4 +20,13 @@ class JsonTeams {
} }
return r; 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

@@ -24,6 +24,9 @@
package org.kohsuke.github; package org.kohsuke.github;
import org.apache.commons.io.IOUtils; 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.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@@ -33,7 +36,9 @@ import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.kohsuke.github.GitHub.*; import static org.kohsuke.github.GitHub.*;
@@ -43,11 +48,32 @@ import static org.kohsuke.github.GitHub.*;
*/ */
class Poster { class Poster {
private final GitHub root; private final GitHub root;
private final List<String> args = new ArrayList<String>(); private final List<Entry> args = new ArrayList<Entry>();
private boolean authenticate; private boolean authenticate;
Poster(GitHub root) { 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.root = root;
this.v = v;
}
Poster(GitHub root) {
this(root,ApiVersion.V2);
} }
public Poster withCredential() { public Poster withCredential() {
@@ -57,16 +83,20 @@ class Poster {
} }
public Poster with(String key, int value) { public Poster with(String key, int value) {
return with(key,String.valueOf(value)); return _with(key, value);
}
public Poster with(String key, boolean value) {
return _with(key, value);
} }
public Poster with(String key, String value) { public Poster with(String key, String value) {
return _with(key, value);
}
private Poster _with(String key, Object value) {
if (value!=null) { if (value!=null) {
try { args.add(new Entry(key,value));
args.add(URLEncoder.encode(key,"UTF-8")+'='+URLEncoder.encode(value,"UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new Error(e); // impossible
}
} }
return this; return this;
} }
@@ -89,32 +119,47 @@ class Poster {
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException { public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
while (true) {// loop while API rate limit is hit while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) root.getApiURL(tailApiUrl).openConnection(); HttpURLConnection uc = (HttpURLConnection) root.getApiURL(v,tailApiUrl).openConnection();
uc.setDoOutput(true); uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded"); uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
if (authenticate) if (authenticate) {
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization); 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); uc.setRequestMethod(method);
StringBuilder body = new StringBuilder(); if (v==ApiVersion.V2) {
for (String e : args) { StringBuilder body = new StringBuilder();
if (body.length()>0) body.append('&'); for (Entry e : args) {
body.append(e); if (body.length()>0) body.append('&');
} body.append(e.toFormArg());
}
OutputStreamWriter o = new OutputStreamWriter(uc.getOutputStream(), "UTF-8"); OutputStreamWriter o = new OutputStreamWriter(uc.getOutputStream(), "UTF-8");
o.write(body.toString()); o.write(body.toString());
o.close(); o.close();
} else {
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(),json);
}
try { try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8"); InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
String data = IOUtils.toString(r);
if (type==null) { if (type==null) {
String data = IOUtils.toString(r);
return null; return null;
} }
return MAPPER.readValue(r,type); return MAPPER.readValue(data,type);
} catch (IOException e) { } catch (IOException e) {
root.handleApiError(e,uc); root.handleApiError(e,uc);
} }

View File

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

View File

@@ -4,19 +4,14 @@ import junit.framework.TestCase;
import org.kohsuke.github.GHOrganization; import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHOrganization.Permission; import org.kohsuke.github.GHOrganization.Permission;
import org.kohsuke.github.GHPullRequest; import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHPullRequest.State;
import org.kohsuke.github.GHRepository; import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTeam; import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub; import org.kohsuke.github.GitHub;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
/** /**
* Unit test for simple App. * Unit test for simple App.
@@ -27,9 +22,38 @@ public class AppTest extends TestCase {
assertFalse(GitHub.connect("totally","bogus").isCredentialValid()); assertFalse(GitHub.connect("totally","bogus").isCredentialValid());
} }
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 { public void testApp() throws IOException {
GitHub gitHub = GitHub.connect(); 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"); // gitHub.getMyself().getRepository("perforce-plugin").setEmailServiceHook("kk@kohsuke.org");
// tryRenaming(gitHub); // tryRenaming(gitHub);
@@ -55,6 +79,35 @@ public class AppTest extends TestCase {
// System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators()); // 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 { private void tryRenaming(GitHub gitHub) throws IOException {
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2"); gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
} }
@@ -79,10 +132,13 @@ public class AppTest extends TestCase {
System.out.println(hooks); System.out.println(hooks);
} }
private void testOrganization(GitHub gitHub) throws IOException { public void testOrganization() throws IOException {
GHOrganization labs = gitHub.getOrganization("HudsonLabs"); GitHub gitHub = GitHub.connect();
GHTeam t = labs.getTeams().get("Core Developers"); GHOrganization j = gitHub.getOrganization("jenkinsci");
GHTeam t = j.getTeams().get("Core Developers");
t.add(labs.getRepository("xyz")); assertNotNull(j.getRepository("jenkins"));
// t.add(labs.getRepository("xyz"));
} }
} }