diff --git a/.gitignore b/.gitignore index 097367121c..cb9b4d6a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target +.idea/ *.iml *.ipr *.iws diff --git a/pom.xml b/pom.xml index b05beb55d0..41e1a14fb6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,11 +3,11 @@ org.kohsuke pom - 3 + 4 github-api - 1.34 + 1.35 GitHub API for Java http://github-api.kohsuke.org/ GitHub API for Java @@ -44,16 +44,14 @@ - org.jvnet.hudson - htmlunit - 2.6-hudson-2 - - - - xml-apis - xml-apis - - + commons-lang + commons-lang + 2.6 + + + commons-codec + commons-codec + 1.7 junit diff --git a/src/main/java/org/kohsuke/github/GHOrganization.java b/src/main/java/org/kohsuke/github/GHOrganization.java index 188f26d930..a27497fcc0 100644 --- a/src/main/java/org/kohsuke/github/GHOrganization.java +++ b/src/main/java/org/kohsuke/github/GHOrganization.java @@ -1,9 +1,5 @@ package org.kohsuke.github; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlAnchor; -import com.gargoylesoftware.htmlunit.html.HtmlPage; - import java.io.IOException; import java.util.AbstractList; import java.util.ArrayList; @@ -50,6 +46,30 @@ public Map getTeams() throws IOException { return r; } + /** + * Checks if this organization has the specified user as a member. + */ + public boolean hasMember(GHUser user) { + try { + root.retrieve().to("/orgs/" + login + "/members/" + user.getLogin()); + return true; + } catch (IOException ignore) { + return false; + } + } + + /** + * Checks if this organization has the specified user as a public member. + */ + public boolean hasPublicMember(GHUser user) { + try { + root.retrieve().to("/orgs/" + login + "/public_members/" + user.getLogin()); + return true; + } catch (IOException ignore) { + return false; + } + } + /** * Publicizes the membership. */ @@ -112,13 +132,12 @@ public GHTeam createTeam(String name, Permission p, GHRepository... repositories * List up repositories that has some open pull requests. */ public List getRepositoriesWithOpenPullRequests() throws IOException { - WebClient wc = root.createWebClient(); - HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/dashboard/pulls"); List r = new ArrayList(); - for (HtmlAnchor e : pg.getElementById("js-issue-list").selectNodes(".//UL[@class='smallnav']/LI[not(@class='zeroed')]/A")) { - String a = e.getHrefAttribute(); - String name = a.substring(a.lastIndexOf('/')+1); - r.add(getRepository(name)); + for (GHRepository repository : root.retrieve().to("/orgs/" + login + "/repos", GHRepository[].class)) { + List pullRequests = repository.getPullRequests(GHIssueState.OPEN); + if (pullRequests.size() > 0) { + r.add(repository); + } } return r; } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 0260bb888a..703ff2378f 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -23,12 +23,6 @@ */ package org.kohsuke.github; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlButton; -import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput; -import com.gargoylesoftware.htmlunit.html.HtmlForm; -import com.gargoylesoftware.htmlunit.html.HtmlInput; -import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.infradna.tool.bridge_method_injector.WithBridgeMethods; import java.io.IOException; @@ -266,15 +260,10 @@ private void modifyCollaborators(Collection users, String method) throws } public void setEmailServiceHook(String address) throws IOException { - WebClient wc = root.createWebClient(); - HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin"); - HtmlInput email = (HtmlInput)pg.getElementById("email_address"); - email.setValueAttribute(address); - HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("email[active]"); - active.setChecked(true); - - final HtmlForm f = email.getEnclosingFormOrDie(); - f.submit((HtmlButton) f.getElementsByTagName("button").get(0)); + Map config = new HashMap(); + config.put("address", address); + new Requester(root).method("POST").with("name", "email").with("config", config).with("active", "true") + .to(String.format("/repos/%s/%s/hooks", owner.login, name)); } private void edit(String key, String value) throws IOException { diff --git a/src/main/java/org/kohsuke/github/GHUser.java b/src/main/java/org/kohsuke/github/GHUser.java index 41fe9a24bb..fb43cbd6d3 100644 --- a/src/main/java/org/kohsuke/github/GHUser.java +++ b/src/main/java/org/kohsuke/github/GHUser.java @@ -69,6 +69,20 @@ public GHPersonSet getFollowers() throws IOException { return new GHPersonSet(Arrays.asList(wrap(followers,root))); } + /** + * Returns true if this user belongs to the specified organization. + */ + public boolean isMemberOf(GHOrganization org) { + return org.hasMember(this); + } + + /** + * Returns true if this user belongs to the specified organization as a public member. + */ + public boolean isPublicMemberOf(GHOrganization org) { + return org.hasPublicMember(this); + } + /*package*/ static GHUser[] wrap(GHUser[] users, GitHub root) { for (GHUser f : users) f.root = root; diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 4253d17e86..a24fac3f5c 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -23,15 +23,12 @@ */ package org.kohsuke.github; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlForm; -import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.infradna.tool.bridge_method_injector.WithBridgeMethods; +import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.codehaus.jackson.map.DeserializationConfig.Feature; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.introspect.VisibilityChecker.Std; -import sun.misc.BASE64Encoder; import java.io.File; import java.io.FileInputStream; @@ -58,8 +55,9 @@ */ public class GitHub { /*package*/ final String login; + + /*package*/ final String encodedAuthorization; - /*package*/ final String password; /*package*/ final String apiToken; private final Map users = new HashMap(); @@ -69,7 +67,7 @@ public class GitHub { private final String apiUrl; private GitHub(String login, String apiToken, String password) { - this ("https://api.github.com", login, apiToken, password); + this (GITHUB_URL, login, apiToken, password); } /** @@ -78,18 +76,17 @@ private GitHub(String login, String apiToken, String password) { * The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or * "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has /api/v3 in the URL. * For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated. + * Password is also considered deprecated as it is no longer required for api usage. */ private GitHub(String apiUrl, String login, String apiToken, String password) { if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize this.apiUrl = apiUrl; this.login = login; this.apiToken = apiToken; - this.password = password; - BASE64Encoder enc = new sun.misc.BASE64Encoder(); if (apiToken!=null || password!=null) { - String userpassword = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password); - encodedAuthorization = enc.encode(userpassword.getBytes()); + String authorization = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password); + encodedAuthorization = new String(Base64.encodeBase64(authorization.getBytes())); } else encodedAuthorization = null; } @@ -97,7 +94,6 @@ private GitHub(String apiUrl, String login, String apiToken, String password) { private GitHub (String apiUrl, String oauthAccessToken) throws IOException { this.apiUrl = apiUrl; - this.password = null; this.encodedAuthorization = null; this.oauthAccessToken = oauthAccessToken; @@ -118,7 +114,11 @@ public static GitHub connect() throws IOException { } finally { IOUtils.closeQuietly(in); } - return new GitHub(props.getProperty("login"),props.getProperty("token"),props.getProperty("password")); + String oauth = props.getProperty("oauth"); + if (oauth!=null) + return new GitHub(GITHUB_URL,oauth); + else + return new GitHub(props.getProperty("login"),props.getProperty("token"),props.getProperty("password")); } /** @@ -130,9 +130,6 @@ public static GitHub connect() throws IOException { * For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated. */ public static GitHub connectToEnterprise(String apiUrl, String login, String apiToken) { - // not exposing password because the login process still assumes https://github.com/ - // if we are to fix this, fix that by getting rid of createWebClient() and replace the e-mail service hook - // with GitHub API. return new GitHub(apiUrl,login,apiToken,null); } @@ -173,7 +170,7 @@ public static GitHub connectAnonymously() { if (tailApiUrl.startsWith("/")) { if ("github.com".equals(apiUrl)) {// backward compatibility - return new URL("https://api.github.com" + tailApiUrl); + return new URL(GITHUB_URL + tailApiUrl); } else { return new URL(apiUrl + tailApiUrl); } @@ -273,7 +270,7 @@ public Map getMyOrganizations() throws IOException { * Public events visible to you. Equivalent of what's displayed on https://github.com/ */ public List getEvents() throws IOException { - // TODO: pagenation + // TODO: pagination GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class); for (GHEventInfo e : events) e.wrapUp(this); @@ -318,18 +315,6 @@ public boolean isCredentialValid() throws IOException { } } - WebClient createWebClient() throws IOException { - WebClient wc = new WebClient(); - wc.setJavaScriptEnabled(false); - wc.setCssEnabled(false); - HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/login"); - HtmlForm f = pg.getForms().get(0); - f.getInputByName("login").setValueAttribute(login); - f.getInputByName("password").setValueAttribute(password); - f.submit(); - return wc; - } - /*package*/ static URL parseURL(String s) { try { return s==null ? null : new URL(s); @@ -360,4 +345,6 @@ WebClient createWebClient() throws IOException { MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY)); MAPPER.getDeserializationConfig().set(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); } + + private static final String GITHUB_URL = "https://api.github.com"; } diff --git a/src/main/java/org/kohsuke/github/Requester.java b/src/main/java/org/kohsuke/github/Requester.java index c6bf92639c..feaa25e23a 100644 --- a/src/main/java/org/kohsuke/github/Requester.java +++ b/src/main/java/org/kohsuke/github/Requester.java @@ -107,6 +107,10 @@ public Requester with(String key, Collection value) { return _with(key, value); } + public Requester with(String key, Map value) { + return _with(key, value); + } + public Requester _with(String key, Object value) { if (value!=null) { args.add(new Entry(key,value)); diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt deleted file mode 100644 index 28254de23e..0000000000 --- a/src/site/apt/index.apt +++ /dev/null @@ -1,25 +0,0 @@ -What is this? - - This library defines an object oriented representation of the GitHub API. The library doesn't yet cover the entirety of the GitHub API, but it's implemented with the right abstractions and libraries to make it very easy to improve the coverage. - -Sample Usage - ------------------- -GitHub github = GitHub.connect(); -GHRepository repo = github.createRepository( - "new-repository","this is my new repository", - "http://www.kohsuke.org/",true/*public*/); -repo.addCollaborators(github.getUser("abayer"),github.getUser("rtyler")); -repo.delete(); ------------------- - -Credential - - This library allows the caller to supply the credential as parameters, but it also defines a common convention - so that applications using this library will look at the consistent location. In this convention, the library - looks at "~/.github" property file, which should have the following two values: - ------------------- -login=kohsuke -token=012345678 ------------------- diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md new file mode 100644 index 0000000000..ba9fbb5287 --- /dev/null +++ b/src/site/markdown/index.md @@ -0,0 +1,36 @@ +What is this? +===== + +This library defines an object oriented representation of the GitHub API. By "object oriented" we mean +there are classes that correspond to the domain model of GitHub (such as `GHUser` and `GHRepository`), +operations that act on them as defined as methods (such as `GHUser.follow()`), and those object references +are used in favor of using string handle (such as `GHUser.isMemberOf(GHOrganization)` instead of +`GHUser.isMemberOf(String)`) + +There are some corners of the GitHub API that's not yet implemented, but +the library is implemented with the right abstractions and libraries to make it very easy to improve the coverage. + +Sample Usage +----- + + GitHub github = GitHub.connect(); + GHRepository repo = github.createRepository( + "new-repository","this is my new repository", + "http://www.kohsuke.org/",true/*public*/); + repo.addCollaborators(github.getUser("abayer"),github.getUser("rtyler")); + repo.delete(); + +Credential +---- + +This library allows the caller to supply the credential as parameters, but it also defines a common convention +so that applications using this library will look at the consistent location. In this convention, the library +looks at `~/.github` property file, which should have the following two values: + + login=kohsuke + password=012345678 + +Alternatively, you can have just the OAuth token in this file: + + oauth=4d98173f7c075527cb64878561d1fe70 + diff --git a/src/test/java/org/kohsuke/AppTest.java b/src/test/java/org/kohsuke/AppTest.java index b272ee7a4d..efe3dc4ae1 100644 --- a/src/test/java/org/kohsuke/AppTest.java +++ b/src/test/java/org/kohsuke/AppTest.java @@ -36,34 +36,42 @@ * Unit test for simple App. */ public class AppTest extends TestCase { + + private GitHub gitHub; + + @Override + public void setUp() throws Exception { + super.setUp(); + gitHub = GitHub.connect(); + } + public void testRepoCRUD() throws Exception { - GitHub hub = GitHub.connect(); - GHRepository r = hub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true); + GHRepository r = gitHub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true); r.enableIssueTracker(false); r.enableDownloads(false); r.enableWiki(false); r.renameTo("github-api-test2"); - hub.getMyself().getRepository("github-api-test2").delete(); + gitHub.getMyself().getRepository("github-api-test2").delete(); } public void testCredentialValid() throws IOException { - assertTrue(GitHub.connect().isCredentialValid()); + assertTrue(gitHub.isCredentialValid()); assertFalse(GitHub.connect("totally", "bogus").isCredentialValid()); } public void testIssueWithNoComment() throws IOException { - GHRepository repository = GitHub.connect().getRepository("kohsuke/test"); + GHRepository repository = gitHub.getRepository("kohsuke/test"); List v = repository.getIssue(4).getComments(); System.out.println(v); assertTrue(v.isEmpty()); v = repository.getIssue(3).getComments(); System.out.println(v); - assertTrue(v.size()==3); + assertTrue(v.size() == 3); } public void testCreateIssue() throws IOException { - GHUser u = GitHub.connect().getUser("kohsuke"); + GHUser u = gitHub.getUser("kohsuke"); GHRepository r = u.getRepository("test"); GHMilestone someMilestone = r.listMilestones(GHIssueState.CLOSED).iterator().next(); GHIssue o = r.createIssue("testing").body("this is body").assignee(u).label("bug").label("question").milestone(someMilestone).create(); @@ -72,26 +80,24 @@ public void testCreateIssue() throws IOException { } public void testRateLimit() throws IOException { - System.out.println(GitHub.connect().getRateLimit()); + System.out.println(gitHub.getRateLimit()); } public void testMyOrganizations() throws IOException { - Map org = GitHub.connect().getMyOrganizations(); + Map org = gitHub.getMyOrganizations(); assertFalse(org.keySet().contains(null)); System.out.println(org); } public void testFetchPullRequest() throws Exception { - GitHub gh = GitHub.connect(); - GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins"); + GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins"); assertEquals("master",r.getMasterBranch()); r.getPullRequest(1); r.getPullRequests(GHIssueState.OPEN); } public void testFetchPullRequestAsList() throws Exception { - GitHub gh = GitHub.connect(); - GHRepository r = gh.getOrganization("symfony").getRepository("symfony-docs"); + GHRepository r = gitHub.getOrganization("symfony").getRepository("symfony-docs"); assertEquals("master", r.getMasterBranch()); PagedIterable i = r.listPullRequests(GHIssueState.CLOSED); List prs = i.asList(); @@ -100,19 +106,17 @@ public void testFetchPullRequestAsList() throws Exception { } public void testRepoPermissions() throws Exception { - GitHub gh = GitHub.connect(); - GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins"); + GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins"); assertTrue(r.hasPullAccess()); - r = gh.getOrganization("github").getRepository("tire"); + r = gitHub.getOrganization("github").getRepository("tire"); assertFalse(r.hasAdminAccess()); } public void testGetMyself() throws Exception { - GitHub hub = GitHub.connect(); - GHMyself me = hub.getMyself(); + GHMyself me = gitHub.getMyself(); System.out.println(me); - GHUser u = hub.getUser("kohsuke2"); + GHUser u = gitHub.getUser("kohsuke2"); System.out.println(u); for (List lst : me.iterateRepositories(100)) { for (GHRepository r : lst) { @@ -122,36 +126,30 @@ public void testGetMyself() throws Exception { } public void testPublicKeys() throws Exception { - GitHub gh = GitHub.connect(); - List keys = gh.getMyself().getPublicKeys(); + List keys = gitHub.getMyself().getPublicKeys(); System.out.println(keys); } public void tryOrgFork() throws Exception { - GitHub gh = GitHub.connect(); - gh.getUser("kohsuke").getRepository("rubywm").forkTo(gh.getOrganization("jenkinsci")); + gitHub.getUser("kohsuke").getRepository("rubywm").forkTo(gitHub.getOrganization("jenkinsci")); } public void tryGetTeamsForRepo() throws Exception { - GitHub gh = GitHub.connect(); - Set o = gh.getOrganization("jenkinsci").getRepository("rubywm").getTeams(); + Set o = gitHub.getOrganization("jenkinsci").getRepository("rubywm").getTeams(); System.out.println(o); } public void testMembership() throws Exception { - GitHub gitHub = GitHub.connect(); Set members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames(); System.out.println(members.contains("kohsuke")); } public void testMemberOrgs() throws Exception { - GitHub gitHub = GitHub.connect(); Set o = gitHub.getUser("kohsuke").getOrganizations(); System.out.println(o); } public void testCommit() throws Exception { - GitHub gitHub = GitHub.connect(); GHCommit commit = gitHub.getUser("jenkinsci").getRepository("jenkins").getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7"); System.out.println(commit); assertEquals(1, commit.getParents().size()); @@ -160,11 +158,10 @@ public void testCommit() throws Exception { File f = commit.getFiles().get(0); assertEquals(48,f.getLinesChanged()); assertEquals("modified",f.getStatus()); - assertEquals("changelog.html",f.getFileName()); + assertEquals("changelog.html", f.getFileName()); } public void testListCommits() throws Exception { - GitHub gitHub = GitHub.connect(); List sha1 = new ArrayList(); for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) { System.out.println(c.getSHA1()); @@ -175,14 +172,12 @@ public void testListCommits() throws Exception { } public void testBranches() throws Exception { - GitHub gitHub = GitHub.connect(); Map b = gitHub.getUser("jenkinsci").getRepository("jenkins").getBranches(); System.out.println(b); } public void testCommitComment() throws Exception { - GitHub gitHub = GitHub.connect(); GHRepository r = gitHub.getUser("jenkinsci").getRepository("jenkins"); PagedIterable comments = r.listCommitComments(); List batch = comments.iterator().nextPage(); @@ -193,7 +188,6 @@ public void testCommitComment() throws Exception { } public void testCreateCommitComment() throws Exception { - GitHub gitHub = GitHub.connect(); GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000"); GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)"); System.out.println(c); @@ -203,7 +197,6 @@ public void testCreateCommitComment() throws Exception { } public void tryHook() throws Exception { - GitHub gitHub = GitHub.connect(); GHRepository r = gitHub.getMyself().getRepository("test2"); GHHook hook = r.createWebHook(new URL("http://www.google.com/")); System.out.println(hook); @@ -213,7 +206,6 @@ public void tryHook() throws Exception { } public void testEventApi() throws Exception { - GitHub gitHub = GitHub.connect(); for (GHEventInfo ev : gitHub.getEvents()) { System.out.println(ev); if (ev.getType()==GHEvent.PULL_REQUEST) { @@ -225,10 +217,9 @@ public void testEventApi() throws Exception { } public void testApp() throws IOException { - GitHub gitHub = GitHub.connect(); System.out.println(gitHub.getMyself().getEmails()); -// GHRepository r = gitHub.connect().getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true); +// GHRepository r = gitHub.getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true); // r.fork(); // tryDisablingIssueTrackers(gitHub); @@ -255,7 +246,7 @@ public void testApp() throws IOException { // t.remove(gitHub.getMyself()); // System.out.println(t.getMembers()); -// GHRepository r = GitHub.connect().getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true); +// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true); // r. // GitHub hub = GitHub.connectAnonymously(); @@ -314,7 +305,6 @@ private void testPostCommitHook(GitHub gitHub) throws IOException { } public void testOrgRepositories() throws IOException { - GitHub gitHub = GitHub.connect(); GHOrganization j = gitHub.getOrganization("jenkinsci"); long start = System.currentTimeMillis(); Map repos = j.getRepositories(); @@ -323,7 +313,6 @@ public void testOrgRepositories() throws IOException { } public void testOrganization() throws IOException { - GitHub gitHub = GitHub.connect(); GHOrganization j = gitHub.getOrganization("jenkinsci"); GHTeam t = j.getTeams().get("Core Developers"); @@ -333,7 +322,6 @@ public void testOrganization() throws IOException { } public void testCommitStatus() throws Exception { - GitHub gitHub = GitHub.connect(); GHRepository r = gitHub.getUser("kohsuke").getRepository("test"); GHCommitStatus state; // state = r.createCommitStatus("edacdd76b06c5f3f0697a22ca75803169f25f296", GHCommitState.FAILURE, "http://jenkins-ci.org/", "oops!"); @@ -346,10 +334,21 @@ public void testCommitStatus() throws Exception { } public void testPullRequestPopulate() throws Exception { - GitHub gitHub = GitHub.connect(); GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api"); GHPullRequest p = r.getPullRequest(17); GHUser u = p.getUser(); assertNotNull(u.getName()); } + + public void testCheckMembership() throws Exception { + GHOrganization j = gitHub.getOrganization("jenkinsci"); + GHUser kohsuke = gitHub.getUser("kohsuke"); + GHUser a = gitHub.getUser("a"); + + assertTrue(j.hasMember(kohsuke)); + assertFalse(j.hasMember(a)); + + assertTrue(j.hasPublicMember(kohsuke)); + assertFalse(j.hasPublicMember(a)); + } }