Compare commits

...

25 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
55e218ac37 [maven-release-plugin] prepare release github-api-1.48 2014-01-06 08:51:56 -08:00
Kohsuke Kawaguchi
7d1f636cdd Recover from earlier test failures 2014-01-01 21:12:17 -08:00
Kohsuke Kawaguchi
a4b8b9b09b Merge pull request #61 from lucamilanesio/master
Fetching of user's verified keys through standard OAuth scope.
2014-01-01 15:26:58 -08:00
Kohsuke Kawaguchi
4e8e28d27c Merge pull request #59 from farmdawgnation/contents-api
Contents API
2014-01-01 15:26:23 -08:00
Luca Milanesio
4b52414435 Fetching of user's verified keys through standard OAuth scope.
Allow the fetching of a user's verified key list without
requiring to have an OAuth user scope (which implies
user's profile READ/WRITE permissions).
This would relax the requirements of GitHub OAuth scope
when fetching public information such as their SSH
public keys.
2013-12-11 00:30:27 +00:00
Matt Farmer
811b96bcbe Implement the ability to retrieve content from a particular ref. 2013-11-28 10:02:37 -05:00
Matt Farmer
f3207855ca Implement a CRUD integration test for the content api. 2013-11-28 09:45:54 -05:00
Matt Farmer
9f3c5b93e3 Store encoded content in class level variable on update. 2013-11-28 09:45:21 -05:00
Matt Farmer
a56357f0c1 Implement the ability to retrieve conent if not populated.
The create and update API calls don't send back the content you just
sent, so that field is null. If this GHContent was instantiated from one
of those calls, we want to make sure we can retrieve the content (and
updated sha, etc) on the fly.
2013-11-28 09:44:27 -05:00
Matt Farmer
f8d14d2e56 Implement the ability to delete content. 2013-11-28 00:37:40 -05:00
Matt Farmer
b0c30759c8 Properly wrap content on directory listing retrieval. 2013-11-28 00:33:07 -05:00
Matt Farmer
54037e6e10 Add the ability to specify a branch name with GHContent updates. 2013-11-28 00:29:49 -05:00
Matt Farmer
fc260d4a37 Implement the ability to create content via content api. 2013-11-28 00:21:25 -05:00
Matt Farmer
387d4e5bc9 Return the new GHContentUpdateResponse from content changes. 2013-11-28 00:12:30 -05:00
Matt Farmer
4ffd46b391 Implement the ability to update existing content. 2013-11-28 00:03:43 -05:00
Matt Farmer
3b49370c06 Add the ability to retrieve the sha of the content. 2013-11-27 23:28:40 -05:00
Alexandre COLLIGNON
925d26e54c Add GitHub Contents API read methods 2013-11-27 22:35:41 -05:00
Kohsuke Kawaguchi
1283006b53 [maven-release-plugin] prepare for next development iteration 2013-11-27 11:57:40 -08:00
Kohsuke Kawaguchi
83a718c9db [maven-release-plugin] prepare release github-api-1.47 2013-11-27 11:57:35 -08:00
Kohsuke Kawaguchi
2abf03ccb7 Minor doc improvement 2013-11-27 11:52:47 -08:00
Kohsuke Kawaguchi
a59810a487 Merge pull request #56 from endeavor85/master
Use `PagedIterator<GHIssue>` to retrieve repository issues
2013-11-27 11:49:44 -08:00
Kohsuke Kawaguchi
9cc9e06ad1 Merge pull request #58 from rtholmes/master
Add support for PULL_REQUEST_REVIEW_COMMENT event types.
2013-11-27 11:49:17 -08:00
reid holmes
0108a0c146 Add support for PULL_REQUEST_REVIEW_COMMENT event types. 2013-11-26 15:13:36 -05:00
endeavor85
4188758d84 Use PagedIterator<GHIssue> to retrieve repository issues. Overcomes
default 30 item page size limit.
2013-11-24 02:54:18 -06:00
Kohsuke Kawaguchi
2144100f81 [maven-release-plugin] prepare for next development iteration 2013-11-13 13:05:29 -08:00
10 changed files with 382 additions and 6 deletions

View File

@@ -7,7 +7,7 @@
</parent>
<artifactId>github-api</artifactId>
<version>1.46</version>
<version>1.48</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -16,6 +16,7 @@
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
<url>http://${project.artifactId}.kohsuke.org/</url>
<tag>github-api-1.48</tag>
</scm>
<distributionManagement>

View File

@@ -0,0 +1,171 @@
package org.kohsuke.github;
import java.io.IOException;
import javax.xml.bind.DatatypeConverter;
/**
* A Content of a repository.
*
* @author Alexandre COLLIGNON
*/
public final class GHContent {
private GHRepository owner;
private String type;
private String encoding;
private long size;
private String sha;
private String name;
private String path;
private String content;
private String url; // this is the API url
private String git_url; // this is the Blob url
private String html_url; // this is the UI
public GHRepository getOwner() {
return owner;
}
public String getType() {
return type;
}
public String getEncoding() {
return encoding;
}
public long getSize() {
return size;
}
public String getSha() {
return sha;
}
public String getName() {
return name;
}
public String getPath() {
return path;
}
/**
* Retrieve the decoded content that is stored at this location.
*
* Due to the nature of GitHub's API, you're not guaranteed that
* the content will already be populated, so this may trigger
* network activity, and can throw an IOException.
**/
public String getContent() throws IOException {
return new String(DatatypeConverter.parseBase64Binary(getEncodedContent()));
}
/**
* Retrieve the raw content that is stored at this location.
*
* Due to the nature of GitHub's API, you're not guaranteed that
* the content will already be populated, so this may trigger
* network activity, and can throw an IOException.
**/
public String getEncodedContent() throws IOException {
if (content != null)
return content;
GHContent retrievedContent = owner.getFileContent(path);
this.size = retrievedContent.size;
this.sha = retrievedContent.sha;
this.content = retrievedContent.content;
this.url = retrievedContent.url;
this.git_url = retrievedContent.git_url;
this.html_url = retrievedContent.html_url;
return content;
}
public String getUrl() {
return url;
}
public String getGitUrl() {
return git_url;
}
public String getHtmlUrl() {
return html_url;
}
public boolean isFile() {
return "file".equals(type);
}
public boolean isDirectory() {
return "dir".equals(type);
}
public GHContentUpdateResponse update(String newContent, String commitMessage) throws IOException {
return update(newContent, commitMessage, null);
}
public GHContentUpdateResponse update(String newContent, String commitMessage, String branch) throws IOException {
String encodedContent = DatatypeConverter.printBase64Binary(newContent.getBytes());
Requester requester = new Requester(owner.root)
.with("path", path)
.with("message", commitMessage)
.with("sha", sha)
.with("content", encodedContent)
.method("PUT");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
response.getContent().wrap(owner);
response.getCommit().wrapUp(owner);
this.content = encodedContent;
return response;
}
public GHContentUpdateResponse delete(String message) throws IOException {
return delete(message, null);
}
public GHContentUpdateResponse delete(String commitMessage, String branch) throws IOException {
Requester requester = new Requester(owner.root)
.with("path", path)
.with("message", commitMessage)
.with("sha", sha)
.method("DELETE");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
response.getCommit().wrapUp(owner);
return response;
}
private String getApiRoute() {
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/contents/" + path;
}
GHContent wrap(GHRepository owner) {
this.owner = owner;
return this;
}
public static GHContent[] wrap(GHContent[] contents, GHRepository repository) {
for (GHContent unwrappedContent : contents) {
unwrappedContent.wrap(repository);
}
return contents;
}
}

View File

@@ -0,0 +1,18 @@
package org.kohsuke.github;
/**
* The response that is returned when updating
* repository content.
**/
public final class GHContentUpdateResponse {
private GHContent content;
private GHCommit commit;
public GHContent getContent() {
return content;
}
public GHCommit getCommit() {
return commit;
}
}

View File

@@ -6,6 +6,7 @@ package org.kohsuke.github;
* See http://developer.github.com/v3/events/types/
*
* @author Kohsuke Kawaguchi
* @see GHEventInfo
*/
public enum GHEvent {
COMMIT_COMMENT,
@@ -22,6 +23,7 @@ public enum GHEvent {
MEMBER,
PUBLIC,
PULL_REQUEST,
PULL_REQUEST_REVIEW_COMMENT,
PUSH,
TEAM_ADD,
WATCH

View File

@@ -10,9 +10,9 @@ import org.apache.commons.lang.builder.ToStringBuilder;
public class GHKey {
/*package almost final*/ GitHub root;
private String url, key, title;
private boolean verified;
private int id;
protected String url, key, title;
protected boolean verified;
protected int id;
public int getId() {
return id;

View File

@@ -34,6 +34,9 @@ public class GHMyself extends GHUser {
/**
* Returns the read-only list of all the pulic keys of the current user.
*
* NOTE: When using OAuth authenticaiton, the READ/WRITE User scope is
* required by the GitHub APIs, otherwise you will get a 404 NOT FOUND.
*
* @return
* Always non-null.
*/
@@ -41,6 +44,21 @@ public class GHMyself extends GHUser {
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to("/user/keys", GHKey[].class)));
}
/**
* Returns the read-only list of all the pulic verified keys of the current user.
*
* Differently from the getPublicKeys() method, the retrieval of the user's
* verified public keys does not require any READ/WRITE OAuth Scope to the
* user's profile.
*
* @return
* Always non-null.
*/
public List<GHVerifiedKey> getPublicVerifiedKeys() throws IOException {
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to(
"/users/" + getLogin() + "/keys", GHVerifiedKey[].class)));
}
/**
* Gets the organization that this user belongs to.
*/

View File

@@ -42,6 +42,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.xml.bind.DatatypeConverter;
import static java.util.Arrays.*;
@@ -143,7 +144,24 @@ public class GHRepository {
}
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
return Arrays.asList(GHIssue.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this));
return listIssues(state).asList();
}
/**
* Lists up all the issues in this repository.
*/
public PagedIterable<GHIssue> listIssues(final GHIssueState state) {
return new PagedIterable<GHIssue>() {
public PagedIterator<GHIssue> iterator() {
return new PagedIterator<GHIssue>(root.retrieve().asIterator(getApiTailUrl("issues?state="+state.toString().toLowerCase(Locale.ENGLISH)), GHIssue[].class)) {
@Override
protected void wrapUp(GHIssue[] page) {
for (GHIssue c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
public GHReleaseBuilder createRelease(String tag) {
@@ -716,7 +734,66 @@ public class GHRepository {
}
return m;
}
public GHContent getFileContent(String path) throws IOException {
return getFileContent(path, null);
}
public GHContent getFileContent(String path, String ref) throws IOException {
Requester requester = root.retrieve();
String target = String.format("/repos/%s/%s/contents/%s", owner.login, name, path);
if (ref != null)
target = target + "?ref=" + ref;
return requester.to(target, GHContent.class).wrap(this);
}
public List<GHContent> getDirectoryContent(String path) throws IOException {
return getDirectoryContent(path, null);
}
public List<GHContent> getDirectoryContent(String path, String ref) throws IOException {
Requester requester = root.retrieve();
String target = String.format("/repos/%s/%s/contents/%s", owner.login, name, path);
if (ref != null)
target = target + "?ref=" + ref;
GHContent[] files = requester.to(target, GHContent[].class);
GHContent.wrap(files, this);
return Arrays.asList(files);
}
public GHContent getReadme() throws Exception {
return getFileContent("readme");
}
public GHContentUpdateResponse createContent(String content, String commitMessage, String path) throws IOException {
return createContent(content, commitMessage, path, null);
}
public GHContentUpdateResponse createContent(String content, String commitMessage, String path, String branch) throws IOException {
Requester requester = new Requester(root)
.with("path", path)
.with("message", commitMessage)
.with("content", DatatypeConverter.printBase64Binary(content.getBytes()))
.method("PUT");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiTailUrl("contents/" + path), GHContentUpdateResponse.class);
response.getContent().wrap(this);
response.getCommit().wrapUp(this);
return response;
}
public GHMilestone createMilestone(String title, String description) throws IOException {
return new Requester(root)
.with("title", title).with("description", description).method("POST").to(getApiTailUrl("milestones"), GHMilestone.class).wrap(this);

View File

@@ -0,0 +1,13 @@
package org.kohsuke.github;
public class GHVerifiedKey extends GHKey {
public GHVerifiedKey() {
this.verified = true;
}
@Override
public String getTitle() {
return (title == null ? "key-" + id : title);
}
}

View File

@@ -46,6 +46,9 @@ public class AppTest extends TestCase {
}
public void testRepoCRUD() throws Exception {
GHRepository existing = gitHub.getMyself().getRepository("github-api-test");
if (existing!=null)
existing.delete();
GHRepository r = gitHub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true);
r.enableIssueTracker(false);
r.enableDownloads(false);
@@ -79,6 +82,12 @@ public class AppTest extends TestCase {
o.close();
}
public void testGetIssues() throws Exception {
List<GHIssue> closedIssues = gitHub.getUser("kohsuke").getRepository("github-api").getIssues(GHIssueState.CLOSED);
// prior to using PagedIterable GHRepository.getIssues(GHIssueState) would only retrieve 30 issues
assertTrue(closedIssues.size() > 30);
}
public void testRateLimit() throws IOException {
System.out.println(gitHub.getRateLimit());
}

View File

@@ -0,0 +1,67 @@
package org.kohsuke.github;
import junit.framework.TestCase;
import java.util.List;
import java.util.UUID;
/**
* Integration test for {@link GHContent}.
*/
public class GHContentIntegrationTest extends TestCase {
private GitHub gitHub;
private GHRepository repo;
private String createdFilename;
@Override
public void setUp() throws Exception {
super.setUp();
gitHub = GitHub.connect();
repo = gitHub.getRepository("acollign/github-api-test").fork();
createdFilename = UUID.randomUUID().toString();
}
public void testGetFileContent() throws Exception {
GHContent content = repo.getFileContent("ghcontent-ro/a-file-with-content");
assertTrue(content.isFile());
assertEquals("thanks for reading me\n", content.getContent());
}
public void testGetEmptyFileContent() throws Exception {
GHContent content = repo.getFileContent("ghcontent-ro/an-empty-file");
assertTrue(content.isFile());
assertEquals("", content.getContent());
}
public void testGetDirectoryContent() throws Exception {
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries");
assertTrue(entries.size() == 3);
}
public void testCRUDContent() throws Exception {
GHContentUpdateResponse created = repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename);
GHContent createdContent = created.getContent();
assertNotNull(created.getCommit());
assertNotNull(created.getContent());
assertNotNull(createdContent.getContent());
assertEquals("this is an awesome file I created\n", createdContent.getContent());
GHContentUpdateResponse updatedContentResponse = createdContent.update("this is some new content\n", "Updated file for integration tests.");
GHContent updatedContent = updatedContentResponse.getContent();
assertNotNull(updatedContentResponse.getCommit());
assertNotNull(updatedContentResponse.getContent());
assertEquals("this is some new content\n", updatedContent.getContent());
GHContentUpdateResponse deleteResponse = updatedContent.delete("Enough of this foolishness!");
assertNotNull(deleteResponse.getCommit());
assertNull(deleteResponse.getContent());
}
}