From fc0871111124dbd2fcf11dc783e0f7f0d07f57c1 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Wed, 31 Jul 2019 11:51:25 +0200 Subject: [PATCH] Expanded GHProject with columns and cards. Also added tests for projects, columns and cards --- .../java/org/kohsuke/github/GHProject.java | 62 ++++++--- .../org/kohsuke/github/GHProjectCard.java | 123 ++++++++++++++++++ .../org/kohsuke/github/GHProjectColumn.java | 104 +++++++++++++++ src/main/java/org/kohsuke/github/GitHub.java | 36 +++-- .../org/kohsuke/github/GHProjectCardTest.java | 99 ++++++++++++++ .../kohsuke/github/GHProjectColumnTest.java | 67 ++++++++++ .../org/kohsuke/github/GHProjectTest.java | 80 ++++++++++++ 7 files changed, 536 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/kohsuke/github/GHProjectCard.java create mode 100644 src/main/java/org/kohsuke/github/GHProjectColumn.java create mode 100644 src/test/java/org/kohsuke/github/GHProjectCardTest.java create mode 100644 src/test/java/org/kohsuke/github/GHProjectColumnTest.java create mode 100644 src/test/java/org/kohsuke/github/GHProjectTest.java diff --git a/src/main/java/org/kohsuke/github/GHProject.java b/src/main/java/org/kohsuke/github/GHProject.java index 17129b146e..457cc17713 100644 --- a/src/main/java/org/kohsuke/github/GHProject.java +++ b/src/main/java/org/kohsuke/github/GHProject.java @@ -23,8 +23,11 @@ */ package org.kohsuke.github; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; +import java.util.Locale; + import static org.kohsuke.github.Previews.INERTIA; /** @@ -33,12 +36,11 @@ * @author Martin van Zijl */ public class GHProject extends GHObject { - private GitHub root; - private GHRepository owner; + protected GitHub root; + protected GHObject owner; private String owner_url; private String html_url; - private String columns_url; private String node_id; private String name; private String body; @@ -55,20 +57,25 @@ public GitHub getRoot() { return root; } - public GHRepository getOwner() { + public GHObject getOwner() throws IOException { + if(owner == null) { + try { + if(owner_url.contains("/orgs/")) { + owner = root.retrieve().to(getOwnerUrl().getPath(), GHOrganization.class).wrapUp(root); + } else if(owner_url.contains("/users/")) { + owner = root.retrieve().to(getOwnerUrl().getPath(), GHUser.class).wrapUp(root); + } else if(owner_url.contains("/repos/")) { + owner = root.retrieve().to(getOwnerUrl().getPath(), GHRepository.class).wrap(root); + } + } catch (FileNotFoundException e) { + return null; + } + } return owner; } - public String getOwner_url() { - return owner_url; - } - - public String getHtml_url() { - return html_url; - } - - public String getColumns_url() { - return columns_url; + public URL getOwnerUrl() { + return GitHub.parseURL(owner_url); } public String getNode_id() { @@ -87,8 +94,8 @@ public int getNumber() { return number; } - public String getState() { - return state; + public ProjectState getState() { + return Enum.valueOf(ProjectState.class, state.toUpperCase(Locale.ENGLISH)); } public GHUser getCreator() { @@ -156,4 +163,27 @@ public void setPublic(boolean isPublic) throws IOException { public void delete() throws IOException { new Requester(root).withPreview(INERTIA).method("DELETE").to(getApiRoute()); } + + public PagedIterable listColumns() throws IOException { + final GHProject project = this; + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator(root.retrieve().withPreview(INERTIA) + .asIterator(String.format("/projects/%d/columns", id), GHProjectColumn[].class, pageSize)) { + @Override + protected void wrapUp(GHProjectColumn[] page) { + for (GHProjectColumn c : page) + c.wrap(project); + } + }; + } + }; + } + + public GHProjectColumn createColumn(String name) throws IOException { + return root.retrieve().method("POST") + .withPreview(INERTIA) + .with("name", name) + .to(String.format("/projects/%d/columns", id), GHProjectColumn.class).wrap(this); + } } \ No newline at end of file diff --git a/src/main/java/org/kohsuke/github/GHProjectCard.java b/src/main/java/org/kohsuke/github/GHProjectCard.java new file mode 100644 index 0000000000..d6fb97cd87 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHProjectCard.java @@ -0,0 +1,123 @@ +package org.kohsuke.github; + +import org.apache.commons.lang3.StringUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; + +import static org.kohsuke.github.Previews.INERTIA; + +/** + * @author Gunnar Skjold + */ +public class GHProjectCard extends GHObject { + private GitHub root; + private GHProject project; + private GHProjectColumn column; + + private String note; + private GHUser creator; + private String content_url, project_url, column_url; + private boolean archived; + + public URL getHtmlUrl() throws IOException { + return null; + } + + public GHProjectCard wrap(GitHub root) { + this.root = root; + return this; + } + + public GHProjectCard wrap(GHProjectColumn column) { + this.column = column; + this.project = column.project; + this.root = column.root; + return this; + } + + public GitHub getRoot() { + return root; + } + + public GHProject getProject() throws IOException { + if(project == null) { + try { + project = root.retrieve().to(getProjectUrl().getPath(), GHProject.class).wrap(root); + } catch (FileNotFoundException e) { + return null; + } + } + return project; + } + + public GHProjectColumn getColumn() throws IOException { + if(column == null) { + try { + column = root.retrieve().to(getColumnUrl().getPath(), GHProjectColumn.class).wrap(root); + } catch (FileNotFoundException e) { + return null; + } + } + return column; + } + + public GHIssue getContent() throws IOException { + if(StringUtils.isEmpty(content_url)) + return null; + try { + if(content_url.contains("/pulls")) { + return root.retrieve().to(getContentUrl().getPath(), GHPullRequest.class).wrap(root); + } else { + return root.retrieve().to(getContentUrl().getPath(), GHIssue.class).wrap(root); + } + } catch (FileNotFoundException e) { + return null; + } + } + + public String getNote() { + return note; + } + + public GHUser getCreator() { + return creator; + } + + public URL getContentUrl() { + return GitHub.parseURL(content_url); + } + + public URL getProjectUrl() { + return GitHub.parseURL(project_url); + } + + public URL getColumnUrl() { + return GitHub.parseURL(column_url); + } + + public boolean isArchived() { + return archived; + } + + public void setNote(String note) throws IOException { + edit("note", note); + } + + public void setArchived(boolean archived) throws IOException { + edit("archived", archived); + } + + private void edit(String key, Object value) throws IOException { + new Requester(root).withPreview(INERTIA)._with(key, value).method("PATCH").to(getApiRoute()); + } + + protected String getApiRoute() { + return String.format("/projects/columns/cards/%d", id); + } + + public void delete() throws IOException { + new Requester(root).withPreview(INERTIA).method("DELETE").to(getApiRoute()); + } +} diff --git a/src/main/java/org/kohsuke/github/GHProjectColumn.java b/src/main/java/org/kohsuke/github/GHProjectColumn.java new file mode 100644 index 0000000000..01aeab530c --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHProjectColumn.java @@ -0,0 +1,104 @@ +package org.kohsuke.github; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; + +import static org.kohsuke.github.Previews.INERTIA; + +/** + * @author Gunnar Skjold + */ +public class GHProjectColumn extends GHObject { + protected GitHub root; + protected GHProject project; + + private String name; + private String project_url; + + @Override + public URL getHtmlUrl() throws IOException { + return null; + } + + public GHProjectColumn wrap(GitHub root) { + this.root = root; + return this; + } + + public GHProjectColumn wrap(GHProject project) { + this.project = project; + this.root = project.root; + return this; + } + + public GitHub getRoot() { + return root; + } + + public GHProject getProject() throws IOException { + if(project == null) { + try { + project = root.retrieve().to(getProjectUrl().getPath(), GHProject.class).wrap(root); + } catch (FileNotFoundException e) { + return null; + } + } + return project; + } + + public String getName() { + return name; + } + + public URL getProjectUrl() { + return GitHub.parseURL(project_url); + } + + public void setName(String name) throws IOException { + edit("name", name); + } + + private void edit(String key, Object value) throws IOException { + new Requester(root).withPreview(INERTIA)._with(key, value).method("PATCH").to(getApiRoute()); + } + + protected String getApiRoute() { + return String.format("/projects/columns/%d", id); + } + + public void delete() throws IOException { + new Requester(root).withPreview(INERTIA).method("DELETE").to(getApiRoute()); + } + + public PagedIterable listCards() throws IOException { + final GHProjectColumn column = this; + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator(root.retrieve().withPreview(INERTIA) + .asIterator(String.format("/projects/columns/%d/cards", id), GHProjectCard[].class, pageSize)) { + @Override + protected void wrapUp(GHProjectCard[] page) { + for (GHProjectCard c : page) + c.wrap(column); + } + }; + } + }; + } + + public GHProjectCard createCard(String note) throws IOException { + return root.retrieve().method("POST") + .withPreview(INERTIA) + .with("note", note) + .to(String.format("/projects/columns/%d/cards", id), GHProjectCard.class).wrap(this); + } + + public GHProjectCard createCard(GHIssue issue) throws IOException { + return root.retrieve().method("POST") + .withPreview(INERTIA) + .with("content_type", issue instanceof GHPullRequest ? "PullRequest" : "Issue") + .with("content_id", issue.getId()) + .to(String.format("/projects/columns/%d/cards", id), GHProjectCard.class).wrap(this); + } +} diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 6f6a9944fb..8bd42ce84a 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -34,33 +34,23 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.logging.Logger; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*; -import static java.net.HttpURLConnection.*; -import static java.util.logging.Level.*; -import static org.kohsuke.github.Previews.*; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; +import static java.util.logging.Level.FINE; +import static org.kohsuke.github.Previews.DRAX; +import static org.kohsuke.github.Previews.INERTIA; /** * Root of the GitHub API. @@ -746,7 +736,15 @@ public boolean isCredentialValid() { } public GHProject getProject(long id) throws IOException { - return retrieve().withPreview(INERTIA).to("/projects/"+id,GHProject.class).wrap(this); + return retrieve().withPreview(INERTIA).to("/projects/"+id, GHProject.class).wrap(this); + } + + public GHProjectColumn getProjectColumn(long id) throws IOException { + return retrieve().withPreview(INERTIA).to("/projects/columns/"+id, GHProjectColumn.class).wrap(this); + } + + public GHProjectCard getProjectCard(long id) throws IOException { + return retrieve().withPreview(INERTIA).to("/projects/columns/cards/"+id, GHProjectCard.class).wrap(this); } private static class GHApiInfo { diff --git a/src/test/java/org/kohsuke/github/GHProjectCardTest.java b/src/test/java/org/kohsuke/github/GHProjectCardTest.java new file mode 100644 index 0000000000..8131f763bb --- /dev/null +++ b/src/test/java/org/kohsuke/github/GHProjectCardTest.java @@ -0,0 +1,99 @@ +package org.kohsuke.github; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * @author Gunnar Skjold + */ +public class GHProjectCardTest extends AbstractGitHubApiTestBase { + private GHOrganization org; + private GHProject project; + private GHProjectColumn column; + private GHProjectCard card; + + @Override public void setUp() throws Exception { + super.setUp(); + org = gitHub.getOrganization("github-api-test-org"); + project = org.createProject("test-project", "This is a test project"); + column = project.createColumn("column-one"); + card = column.createCard("This is a card"); + } + + @Test + public void testCreatedCard() { + Assert.assertEquals("This is a card", card.getNote()); + Assert.assertFalse(card.isArchived()); + } + + @Test + public void testEditCardNote() throws IOException { + card.setNote("New note"); + card = gitHub.getProjectCard(card.getId()); + Assert.assertEquals("New note", card.getNote()); + Assert.assertFalse(card.isArchived()); + } + + @Test + public void testArchiveCard() throws IOException { + card.setArchived(true); + card = gitHub.getProjectCard(card.getId()); + Assert.assertEquals("This is a card", card.getNote()); + Assert.assertTrue(card.isArchived()); + } + + @Test + public void testCreateCardFromIssue() throws IOException { + GHRepository repo = org.createRepository("repo-for-project-card").create(); + try { + GHIssue issue = repo.createIssue("new-issue").body("With body").create(); + GHProjectCard card = column.createCard(issue); + Assert.assertEquals(issue.getUrl(), card.getContentUrl()); + } finally { + repo.delete(); + } + } + + @Test + public void testDeleteCard() throws IOException { + card.delete(); + try { + card = gitHub.getProjectCard(card.getId()); + Assert.assertNull(card); + } catch (FileNotFoundException e) { + card = null; + } + } + + @After + public void after() throws IOException { + if(card != null) { + try { + card.delete(); + card = null; + } catch (FileNotFoundException e) { + card = null; + } + } + if(column != null) { + try { + column.delete(); + column = null; + } catch (FileNotFoundException e) { + column = null; + } + } + if(project != null) { + try { + project.delete(); + project = null; + } catch (FileNotFoundException e) { + project = null; + } + } + } +} diff --git a/src/test/java/org/kohsuke/github/GHProjectColumnTest.java b/src/test/java/org/kohsuke/github/GHProjectColumnTest.java new file mode 100644 index 0000000000..27e8460b23 --- /dev/null +++ b/src/test/java/org/kohsuke/github/GHProjectColumnTest.java @@ -0,0 +1,67 @@ +package org.kohsuke.github; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * @author Gunnar Skjold + */ +public class GHProjectColumnTest extends AbstractGitHubApiTestBase { + private GHProject project; + private GHProjectColumn column; + + @Override public void setUp() throws Exception { + super.setUp(); + project = gitHub + .getOrganization("github-api-test-org") + .createProject("test-project", "This is a test project"); + column = project.createColumn("column-one"); + } + + @Test + public void testCreatedColumn() { + Assert.assertEquals("column-one", column.getName()); + } + + @Test + public void testEditColumnName() throws IOException { + column.setName("new-name"); + column = gitHub.getProjectColumn(column.getId()); + Assert.assertEquals("new-name", column.getName()); + } + + @Test + public void testDeleteColumn() throws IOException { + column.delete(); + try { + column = gitHub.getProjectColumn(column.getId()); + Assert.assertNull(column); + } catch (FileNotFoundException e) { + column = null; + } + } + + @After + public void after() throws IOException { + if(column != null) { + try { + column.delete(); + column = null; + } catch (FileNotFoundException e) { + column = null; + } + } + if(project != null) { + try { + project.delete(); + project = null; + } catch (FileNotFoundException e) { + project = null; + } + } + } +} diff --git a/src/test/java/org/kohsuke/github/GHProjectTest.java b/src/test/java/org/kohsuke/github/GHProjectTest.java new file mode 100644 index 0000000000..b5e1aef122 --- /dev/null +++ b/src/test/java/org/kohsuke/github/GHProjectTest.java @@ -0,0 +1,80 @@ +package org.kohsuke.github; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * @author Gunnar Skjold + */ +public class GHProjectTest extends AbstractGitHubApiTestBase { + private GHProject project; + + @Override public void setUp() throws Exception { + super.setUp(); + project = gitHub + .getOrganization("github-api-test-org") + .createProject("test-project", "This is a test project"); + } + + @Test + public void testCreatedProject() { + Assert.assertNotNull(project); + Assert.assertEquals("test-project", project.getName()); + Assert.assertEquals("This is a test project", project.getBody()); + Assert.assertEquals(GHProject.ProjectState.OPEN, project.getState()); + } + + @Test + public void testEditProjectName() throws IOException { + project.setName("new-name"); + project = gitHub.getProject(project.getId()); + Assert.assertEquals("new-name", project.getName()); + Assert.assertEquals("This is a test project", project.getBody()); + Assert.assertEquals(GHProject.ProjectState.OPEN, project.getState()); + } + + @Test + public void testEditProjectBody() throws IOException { + project.setBody("New body"); + project = gitHub.getProject(project.getId()); + Assert.assertEquals("test-project", project.getName()); + Assert.assertEquals("New body", project.getBody()); + Assert.assertEquals(GHProject.ProjectState.OPEN, project.getState()); + } + + @Test + public void testEditProjectState() throws IOException { + project.setState(GHProject.ProjectState.CLOSED); + project = gitHub.getProject(project.getId()); + Assert.assertEquals("test-project", project.getName()); + Assert.assertEquals("This is a test project", project.getBody()); + Assert.assertEquals(GHProject.ProjectState.CLOSED, project.getState()); + } + + @Test + public void testDeleteProject() throws IOException { + project.delete(); + try { + project = gitHub.getProject(project.getId()); + Assert.assertNull(project); + } catch (FileNotFoundException e) { + project = null; + } + } + + @After + public void after() throws IOException { + if(project != null) { + try { + project.delete(); + project = null; + } catch (FileNotFoundException e) { + project = null; + } + } + } +}