Compare commits

..

20 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
26b6a94e32 [maven-release-plugin] prepare release github-api-1.49 2014-01-06 08:56:34 -08:00
Kohsuke Kawaguchi
be6ef475ba [maven-release-plugin] prepare for next development iteration 2014-01-06 08:52:00 -08:00
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
9 changed files with 356 additions and 5 deletions

View File

@@ -7,7 +7,7 @@
</parent>
<artifactId>github-api</artifactId>
<version>1.47</version>
<version>1.49</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.49</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

@@ -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.*;
@@ -733,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);

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