diff --git a/.gitignore b/.gitignore index 39ab9cd..c203071 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,8 @@ nb-configuration.xml Session.vim .netrwhist *~ + +### Acquisio related test ### +*AcquisioTest.java +*AcquisioTest.groovy +credentials.groovy diff --git a/AUTHORS.md b/AUTHORS.md index 7defe0b..ff88050 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -3,3 +3,4 @@ Kyle Chaplin @chaplinkyle Alesandro Lang @alesandroLang Javier Molina @javinovich Joseph McCarthy +Pierre-Luc Dupont @pldupont diff --git a/README.md b/README.md index 483eeea..093f382 100644 --- a/README.md +++ b/README.md @@ -273,4 +273,68 @@ public class Example { System.err.println(ex.getCause().getMessage()); } } +} ``` + +## Agile API ## +https://docs.atlassian.com/jira-software/REST/cloud/ + +### Agile supported calls ### +| Class | Method | REST Call | +| ----- | ------ | --------- | +| [AgileClient](src/main/java/net/rcarz/jiraclient/agile/AgileClient.java) | ```List getBoards()``` | GET /rest/agile/1.0/board | +| | ```Board getBoard(long id)``` | GET /rest/agile/1.0/board/{boardId} | +| | ```Sprint getSprint(long id)``` | GET /rest/agile/1.0/sprint/{sprintId} | +| | ```Epic getEpic(long id)``` | GET /rest/agile/1.0/epic/{epicId} | +| | ```Issue getIssue(long id)``` | GET /rest/agile/1.0/issue/{issueId} | +| | ```Issue getIssue(String key)``` | GET /rest/agile/1.0/issue/{issueKey} | +| [Board](src/main/java/net/rcarz/jiraclient/agile/Board.java) | ``` static List getAll(RestClient restclient)``` | GET /rest/agile/1.0/board | +| | ```static Board get(RestClient restclient, long id)``` | GET /rest/agile/1.0/board/{boardId} | +| | ```List getSprints()``` | GET /rest/agile/1.0/board/{boardId}/sprint | +| * | ```List getEpics()``` | GET /rest/agile/1.0/board/{boardId}/epic +| * | ```List getBacklog()``` | GET /rest/agile/1.0/board/{boardId}/backlog +| * | ```List getIssuesWithoutEpic()``` | GET /rest/agile/1.0/board/{boardId}/epic/none/issue +| [Sprint](src/main/java/net/rcarz/jiraclient/agile/Sprint.java) | ``` static Sprint get(RestClient restclient, long sprintId)``` | GET /rest/agile/1.0/sprint/{sprintId} | +| | ```static List getAll(RestClient restclient, long boardId)``` | GET /rest/agile/1.0/board/{boardId}/sprint | +| * | ```List getIssues()``` | GET /rest/agile/1.0/sprint/{sprintId}/issue | +| [Epic](src/main/java/net/rcarz/jiraclient/agile/Epic.java) | ```static Epic get(RestClient restclient, long id)``` | GET /rest/agile/1.0/epic/{epicId} | +| * | ```List getIssues()``` | GET /rest/agile/1.0/epic/{epicId}/issue | +| [Issue](src/main/java/net/rcarz/jiraclient/agile/Issue.java) | ```static Issue get(RestClient restclient, long id)``` | GET /rest/agile/1.0/issue/{issueId} | +| | ```static Issue get(RestClient restclient, String key)``` | GET /rest/agile/1.0/issue/{issueKey} | + + + +### Agile Example ### +To see more examples, look at [AgileClientDemoTest](test/groovy/AgileClientDemoTest.groovy) +```java +import java.util.List; + +import net.rcarz.jiraclient.BasicCredentials; +import net.rcarz.jiraclient.Issue; +import net.rcarz.jiraclient.JiraClient; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.agile.Board; +import net.rcarz.jiraclient.agile.AgileClient; + +public class Example { + + public static void main(String[] args) { + + BasicCredentials creds = new BasicCredentials("batman", "pow! pow!"); + JiraClient jira = new JiraClient("https://jira.example.com/jira", creds); + AgileClient agileClient = new AgileClient(jira); + + try { + /* Retrieve all Boards */ + List allBoards = agileClient.getBoards(); + } catch (JiraException ex) { + System.err.println(ex.getMessage()); + + if (ex.getCause() != null) { + System.err.println(ex.getCause().getMessage()); + } + } + } +} +``` + diff --git a/pom.xml b/pom.xml index 31d727c..2e92a80 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,12 @@ test + + org.codehaus.groovy + groovy-all + 2.4.6 + test + diff --git a/src/main/java/net/rcarz/jiraclient/Field.java b/src/main/java/net/rcarz/jiraclient/Field.java index 87c0bdc..f38fb86 100644 --- a/src/main/java/net/rcarz/jiraclient/Field.java +++ b/src/main/java/net/rcarz/jiraclient/Field.java @@ -19,107 +19,24 @@ package net.rcarz.jiraclient; -import java.lang.Iterable; -import java.lang.UnsupportedOperationException; +import net.sf.json.JSONArray; +import net.sf.json.JSONNull; +import net.sf.json.JSONObject; + import java.sql.Timestamp; import java.text.ParsePosition; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.sf.json.JSONArray; -import net.sf.json.JSONObject; -import net.sf.json.JSONNull; +import java.util.*; /** * Utility functions for translating between JSON and fields. */ public final class Field { - /** - * Field metadata structure. - */ - public static final class Meta { - public boolean required; - public String type; - public String items; - public String name; - public String system; - public String custom; - public int customId; - } - - /** - * Field update operation. - */ - public static final class Operation { - public String name; - public Object value; - - /** - * Initialises a new update operation. - * - * @param name Operation name - * @param value Field value - */ - public Operation(String name, Object value) { - this.name = name; - this.value = value; - } - } - - /** - * Allowed value types. - */ - public enum ValueType { - KEY("key"), NAME("name"), ID_NUMBER("id"), VALUE("value"); - private String typeName; - - private ValueType(String typeName) { - this.typeName = typeName; - } - - @Override - public String toString() { - return typeName; - } - }; - - /** - * Value and value type pair. - */ - public static final class ValueTuple { - public final String type; - public final Object value; - - /** - * Initialises the value tuple. - * - * @param type - * @param value - */ - public ValueTuple(String type, Object value) { - this.type = type; - this.value = (value != null ? value : JSONNull.getInstance()); - } - - /** - * Initialises the value tuple. - * - * @param type - * @param value - */ - public ValueTuple(ValueType type, Object value) { - this(type.toString(), value); - } - } - public static final String ASSIGNEE = "assignee"; public static final String ATTACHMENT = "attachment"; public static final String CHANGE_LOG = "changelog"; + ; public static final String CHANGE_LOG_ENTRIES = "histories"; public static final String CHANGE_LOG_ITEMS = "items"; public static final String COMMENT = "comment"; @@ -149,10 +66,8 @@ public final class Field { public static final String CREATED_DATE = "created"; public static final String UPDATED_DATE = "updated"; public static final String TRANSITION_TO_STATUS = "to"; - public static final String DATE_FORMAT = "yyyy-MM-dd"; public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - private Field() { } /** @@ -204,7 +119,7 @@ public final class Field { return results; } - + /** * Gets a list of remote links from the given object. * @@ -290,6 +205,24 @@ public final class Field { return result; } + /** + * Gets a long from the given object. + * + * @param i a Long or an Integer instance + * + * @return a long primitive or 0 if i isn't a Long or an Integer instance + */ + public static long getLong(Object i) { + long result = 0; + + if (i instanceof Long) + result = ((Long) i).longValue(); + if (i instanceof Integer) + result = ((Integer) i).intValue(); + + return result; + } + /** * Gets a generic map from the given object. * @@ -553,7 +486,7 @@ public final class Field { } else if (type.equals("string") && custom != null && (custom.equals("com.atlassian.jira.plugin.system.customfieldtypes:multicheckboxes") || custom.equals("com.atlassian.jira.plugin.system.customfieldtypes:multiselect"))) { - + realResult = new JSONObject(); ((JSONObject)realResult).put(ValueType.VALUE.toString(), realValue.toString()); } else if (type.equals("string")) @@ -658,7 +591,7 @@ public final class Field { else if (value instanceof TimeTracking) return ((TimeTracking) value).toJsonObject(); } else if (m.type.equals("number")) { - if(!(value instanceof java.lang.Integer) && !(value instanceof java.lang.Double) && !(value + if (!(value instanceof java.lang.Integer) && !(value instanceof java.lang.Double) && !(value instanceof java.lang.Float) && !(value instanceof java.lang.Long) ) { throw new JiraException("Field '" + name + "' expects a Numeric value"); @@ -722,5 +655,83 @@ public final class Field { public static ValueTuple valueById(String id) { return new ValueTuple(ValueType.ID_NUMBER, id); } + + /** + * Allowed value types. + */ + public enum ValueType { + KEY("key"), NAME("name"), ID_NUMBER("id"), VALUE("value"); + private String typeName; + + private ValueType(String typeName) { + this.typeName = typeName; + } + + @Override + public String toString() { + return typeName; + } + } + + /** + * Field metadata structure. + */ + public static final class Meta { + public boolean required; + public String type; + public String items; + public String name; + public String system; + public String custom; + public int customId; + } + + /** + * Field update operation. + */ + public static final class Operation { + public String name; + public Object value; + + /** + * Initialises a new update operation. + * + * @param name Operation name + * @param value Field value + */ + public Operation(String name, Object value) { + this.name = name; + this.value = value; + } + } + + /** + * Value and value type pair. + */ + public static final class ValueTuple { + public final String type; + public final Object value; + + /** + * Initialises the value tuple. + * + * @param type + * @param value + */ + public ValueTuple(String type, Object value) { + this.type = type; + this.value = (value != null ? value : JSONNull.getInstance()); + } + + /** + * Initialises the value tuple. + * + * @param type + * @param value + */ + public ValueTuple(ValueType type, Object value) { + this(type.toString(), value); + } + } } diff --git a/src/main/java/net/rcarz/jiraclient/agile/AgileClient.java b/src/main/java/net/rcarz/jiraclient/agile/AgileClient.java new file mode 100644 index 0000000..b137b9f --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/AgileClient.java @@ -0,0 +1,116 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.JiraClient; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; + +import java.util.List; + +/** + * An Agile extension to the JIRA client. + * + * @author pldupont + * @see "https://docs.atlassian.com/jira-software/REST/cloud/" + */ +public class AgileClient { + + private RestClient restclient = null; + + /** + * Creates an Agile client. + * + * @param jira JIRA client + */ + public AgileClient(JiraClient jira) { + restclient = jira.getRestClient(); + } + + /** + * Retrieves the board with the given ID. + * + * @param id Board ID + * @return a Board instance + * @throws JiraException when something goes wrong + */ + public Board getBoard(long id) throws JiraException { + return Board.get(restclient, id); + } + + /** + * Retrieves all boards visible to the session user. + * + * @return a list of boards + * @throws JiraException when something goes wrong + */ + public List getBoards() throws JiraException { + return Board.getAll(restclient); + } + + /** + * Retrieves the sprint with the given ID. + * + * @param id Sprint ID + * @return a Sprint instance + * @throws JiraException when something goes wrong + */ + public Sprint getSprint(long id) throws JiraException { + return Sprint.get(restclient, id); + } + + /** + * Retrieves the issue with the given ID. + * + * @param id Issue ID + * @return an Issue instance + * @throws JiraException when something goes wrong + */ + public Issue getIssue(long id) throws JiraException { + return Issue.get(restclient, id); + } + + /** + * Retrieves the issue with the given Key. + * + * @param key Issue Key + * @return an Issue instance + * @throws JiraException when something goes wrong + */ + public Issue getIssue(String key) throws JiraException { + return Issue.get(restclient, key); + } + + /** + * Retrieves the epic with the given ID. + * + * @param id Epic ID + * @return an Epic instance + * @throws JiraException when something goes wrong + */ + public Epic getEpic(long id) throws JiraException { + return Epic.get(restclient, id); + } + + public RestClient getRestclient() { + return restclient; + } +} + diff --git a/src/main/java/net/rcarz/jiraclient/agile/AgileResource.java b/src/main/java/net/rcarz/jiraclient/agile/AgileResource.java new file mode 100644 index 0000000..8700618 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/AgileResource.java @@ -0,0 +1,318 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSON; +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import org.apache.commons.lang.math.NumberUtils; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +/** + * A base class for Agile resources. + * + * @author pldupont + * @see "https://docs.atlassian.com/jira-software/REST/cloud/" + */ +public abstract class AgileResource { + + public static final String ATTR_ID = "id"; + public static final String ATTR_NAME = "name"; + public static final String ATTR_SELF = "self"; + + public static final String RESOURCE_URI = "/rest/agile/1.0/"; + + private RestClient restclient = null; + private long id = 0; + private String name; + private String self; + private JSONObject attributes = new JSONObject(); + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + * @throws JiraException when the retrieval fails + */ + public AgileResource(RestClient restclient, JSONObject json) throws JiraException { + this.restclient = restclient; + if (json != null) { + deserialize(json); + } + } + + /** + * Gets an Agile resource from the given object. + * + * @param type Resource data type + * @param r a JSONObject instance + * @param restclient REST client instance + * @return a Resource instance or null if r isn't a JSONObject instance + * @throws JiraException when the retrieval fails + */ + protected static T getResource( + Class type, Object r, RestClient restclient) throws JiraException { + + if (!(r instanceof JSONObject)) { + throw new JiraException("JSON payload is malformed"); + } + + T result = null; + + if (!((JSONObject) r).isNullObject()) { + try { + Constructor constructor = type.getDeclaredConstructor(RestClient.class, JSONObject.class); + result = constructor.newInstance(restclient, r); + } catch (Exception e) { + throw new JiraException("Failed to deserialize object.", e); + } + } + + return result; + } + + /** + * Gets a list of GreenHopper resources from the given object. + * + * @param type Resource data type + * @param ra a JSONArray instance + * @param restclient REST client instance + * @param listName The name of the list of items from the JSON result. + * @return a list of Resources found in ra + * @throws JiraException when the retrieval fails + */ + protected static List getResourceArray( + Class type, Object ra, RestClient restclient, String listName) throws JiraException { + if (!(ra instanceof JSONObject)) { + throw new JiraException("JSON payload is malformed"); + } + + JSONObject jo = (JSONObject) ra; + + if (!jo.containsKey(listName) || !(jo.get(listName) instanceof JSONArray)) { + throw new JiraException("No array found for name '" + listName + "'"); + } + + List results = new ArrayList(); + + for (Object v : (JSONArray) jo.get(listName)) { + T item = getResource(type, v, restclient); + if (item != null) { + results.add(item); + } + } + + return results; + } + + /** + * Retrieves all boards visible to the session user. + * + * @param restclient REST client instance + * @param type The type of the object to deserialize. + * @param url The URL to call. + * @return a list of boards + * @throws JiraException when the retrieval fails + */ + static List list( + RestClient restclient, Class type, String url) throws JiraException { + return list(restclient, type, url, "values"); + } + + /** + * Retrieves all boards visible to the session user. + * + * @param restclient REST client instance + * @param type The type of the object to deserialize. + * @param url The URL to call. + * @param listName The name of the list of items in the JSON response. + * @return a list of boards + * @throws JiraException when the retrieval fails + */ + static List list( + RestClient restclient, Class type, String url, String listName) throws JiraException { + + JSON result; + try { + result = restclient.get(url); + } catch (Exception ex) { + throw new JiraException("Failed to retrieve a list of " + type.getSimpleName() + " : " + url, ex); + } + + return getResourceArray( + type, + result, + restclient, + listName + ); + } + + /** + * Retrieves all boards visible to the session user. + * + * @param restclient REST client instance + * @return a list of boards + * @throws JiraException when the retrieval fails + */ + static T get(RestClient restclient, Class type, String url) throws JiraException { + + JSON result; + try { + result = restclient.get(url); + } catch (Exception ex) { + throw new JiraException("Failed to retrieve " + type.getSimpleName() + " : " + url, ex); + } + + return getResource( + type, + result, + restclient + ); + } + + /** + * Extract from a sub list the Resource array, if present. + * + * @param type Resource data type + * @param subJson a JSONObject instance + * @param resourceName The name of the list of items from the JSON result. + * @param The type of Agile resource to return. + * @return The list of resources if present. + * @throws JiraException when the retrieval fails + */ + List getSubResourceArray( + Class type, JSONObject subJson, String resourceName) throws JiraException { + List result = null; + if (subJson.containsKey(resourceName)) { + result = getResourceArray(type, subJson.get(resourceName), getRestclient(), resourceName + "s"); + } + return result; + } + + /** + * Extract from a sub list the Resource, if present. + * + * @param type Resource data type + * @param subJson a JSONObject instance + * @param resourceName The name of the item from the JSON result. + * @param The type of Agile resource to return. + * @return The resource if present. + * @throws JiraException when the retrieval fails + */ + T getSubResource( + Class type, JSONObject subJson, String resourceName) throws JiraException { + T result = null; + if (subJson.containsKey(resourceName) && !subJson.get(resourceName).equals("null")) { + result = getResource(type, subJson.get(resourceName), getRestclient()); + } + return result; + } + + /** + * @return Internal JIRA ID. + */ + public long getId() { + return id; + } + + /** + * @return The resource name. + */ + public String getName() { + return name; + } + + /** + * @param name Setter for the resource name. In some case, the name is called something else. + */ + void setName(String name) { + this.name = name; + } + + /** + * @return The resource URL. + */ + public String getSelfURL() { + return self; + } + + /** + * @return The REST client used to access the current resource. + */ + protected RestClient getRestclient() { + return restclient; + } + + /** + * Retrieve the specified attribute as a generic object. + * + * @param name The name of the attribute to retrieve. + * @return The value of the attribute. + */ + public Object getAttribute(String name) { + return attributes.get(name); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + void deserialize(JSONObject json) throws JiraException { + + id = getLong(json.get("id")); + name = Field.getString(json.get("name")); + self = Field.getString(json.get("self")); + addAttributes(json); + } + + /** + * Allow to add more attributes. + * + * @param json The json object to extract attributes from. + */ + void addAttributes(JSONObject json) { + attributes.putAll(json); + } + + long getLong(Object o) { + if (o instanceof Integer || o instanceof Long) { + return Field.getLong(o); + } else if (o instanceof String && NumberUtils.isDigits((String) o)) { + return NumberUtils.toLong((String) o, 0L); + } else { + return 0L; + } + } + + @Override + public String toString() { + return String.format("%s{id=%s, name='%s'}", getClass().getSimpleName(), id, name); + } +} + diff --git a/src/main/java/net/rcarz/jiraclient/agile/Board.java b/src/main/java/net/rcarz/jiraclient/agile/Board.java new file mode 100644 index 0000000..4db6aec --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Board.java @@ -0,0 +1,116 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +import java.util.List; + +/** + * Represents an Agile Board. + * + * @author pldupont + */ +public class Board extends AgileResource { + + private String type; + + /** + * Creates a Board from a JSON payload. + * + * @param restclient REST client instance + * @param json JSON payload + */ + protected Board(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Retrieves the given rapid view. + * + * @param restclient REST client instance + * @param id Internal JIRA ID of the rapid view + * @return a rapid view instance + * @throws JiraException when the retrieval fails + */ + public static Board get(RestClient restclient, long id) throws JiraException { + return AgileResource.get(restclient, Board.class, RESOURCE_URI + "board/" + id); + } + + /** + * Retrieves all boards visible to the session user. + * + * @param restclient REST client instance + * @return a list of boards + * @throws JiraException when the retrieval fails + */ + public static List getAll(RestClient restclient) throws JiraException { + return AgileResource.list(restclient, Board.class, RESOURCE_URI + "board"); + } + + @Override + protected void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + type = Field.getString(json.get("type")); + } + + /** + * @return The board type. + */ + public String getType() { + return this.type; + } + + /** + * @return All sprints related to the current board. + * @throws JiraException when the retrieval fails + */ + public List getSprints() throws JiraException { + return Sprint.getAll(getRestclient(), getId()); + } + + /** + * @return All issues in the Board backlog. + * @throws JiraException when the retrieval fails + */ + public List getBacklog() throws JiraException { + return AgileResource.list(getRestclient(), Issue.class, RESOURCE_URI + "board/" + getId() + "/backlog", "issues"); + } + + /** + * @return All issues without epic in the Board . + * @throws JiraException when the retrieval fails + */ + public List getIssuesWithoutEpic() throws JiraException { + return AgileResource.list(getRestclient(), Issue.class, RESOURCE_URI + "board/" + getId() + "/epic/none/issue", "issues"); + } + + /** + * @return All epics associated to the Board. + * @throws JiraException when the retrieval fails + */ + public List getEpics() throws JiraException { + return AgileResource.list(getRestclient(), Epic.class, RESOURCE_URI + "board/" + getId() + "/epic"); + } +} + diff --git a/src/main/java/net/rcarz/jiraclient/agile/Comment.java b/src/main/java/net/rcarz/jiraclient/agile/Comment.java new file mode 100644 index 0000000..c42b757 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Comment.java @@ -0,0 +1,93 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +import java.util.Date; + +/** + * Represents an Agile Comment. + * + * @author pldupont + */ +public class Comment extends AgileResource { + + private User author; + private String body; + private User updateAuthor; + private Date created; + private Date updated; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Comment(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + + this.author = getSubResource(User.class, json, "author"); + this.body = Field.getString(json.get("body")); + this.updateAuthor = getSubResource(User.class, json, "updateAuthor"); + this.created = Field.getDateTime(json.get("created")); + this.updated = Field.getDateTime(json.get("updated")); + } + + @Override + public String toString() { + return String.format("%s{id=%s, body='%s'}", getClass().getSimpleName(), getId(), getBody()); + } + + public User getAuthor() { + return author; + } + + public String getBody() { + return body; + } + + public User getUpdateAuthor() { + return updateAuthor; + } + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Epic.java b/src/main/java/net/rcarz/jiraclient/agile/Epic.java new file mode 100644 index 0000000..6531572 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Epic.java @@ -0,0 +1,109 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +import java.util.List; + +/** + * Represents an Agile Epic. + * + * @author pldupont + */ +public class Epic extends AgileResource { + + private Issue issue; + private String key; + private String summary; + private boolean done; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Epic(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Retrieves the epic matching the ID. + * + * @param restclient REST client instance + * @param id Internal JIRA ID of the epic + * @return an epic instance + * @throws JiraException when the retrieval fails + */ + public static Epic get(RestClient restclient, long id) throws JiraException { + return AgileResource.get(restclient, Epic.class, RESOURCE_URI + "epic/" + id); + } + + /** + * @param refresh If true, will fetch the information from JIRA, otherwise use the cached info. + * @return The Issue representation of this Epic. + * @throws JiraException when the retrieval fails + */ + public Issue asIssue(boolean refresh) throws JiraException { + if (this.issue == null || refresh) { + this.issue = Issue.get(getRestclient(), getId()); + } + return this.issue; + } + + /** + * @return All issues in the Epic. + * @throws JiraException when the retrieval fails + */ + public List getIssues() throws JiraException { + return AgileResource.list(getRestclient(), Issue.class, RESOURCE_URI + "epic/" + getId() + "/issue", "issues"); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + + this.key = Field.getString(json.get("key")); + this.summary = Field.getString(json.get("summary")); + this.done = Field.getBoolean(json.get("done")); + } + + public String getKey() { + return key; + } + + public String getSummary() { + return summary; + } + + public boolean isDone() { + return done; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Issue.java b/src/main/java/net/rcarz/jiraclient/agile/Issue.java new file mode 100644 index 0000000..5b2c4a8 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Issue.java @@ -0,0 +1,205 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +import java.util.Date; +import java.util.List; + +/** + * Represents an Agile Issue. + * + * @author pldupont + */ +public class Issue extends AgileResource { + + private String key; + private boolean flagged; + private Sprint sprint; + private List closedSprints; + private String description; + private Project project; + private List comments; + private Epic epic; + private List worklogs; + private TimeTracking timeTracking; + private IssueType issueType; + private Status status; + private Resolution resolution; + private Date created; + private Date updated; + private Priority priority; + private User assignee; + private User creator; + private User reporter; + private String environment; + + + /** + * Creates a new Agile Issue resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Issue(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Retrieves the issue matching the ID. + * + * @param restclient REST client instance + * @param id Internal JIRA ID of the issue + * @return an issue instance + * @throws JiraException when the retrieval fails + */ + public static Issue get(RestClient restclient, long id) throws JiraException { + return AgileResource.get(restclient, Issue.class, RESOURCE_URI + "issue/" + id); + } + + /** + * Retrieves the issue matching the ID. + * + * @param restclient REST client instance + * @param key JIRA key of the issue + * @return an issue instance + * @throws JiraException when the retrieval fails + */ + public static Issue get(RestClient restclient, String key) throws JiraException { + return AgileResource.get(restclient, Issue.class, RESOURCE_URI + "issue/" + key); + } + + @Override + protected void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + this.key = Field.getString(json.get("key")); + + // Extract from Field sub JSONObject + if (json.containsKey("fields")) { + JSONObject fields = (JSONObject) json.get("fields"); + setName(Field.getString(fields.get("summary"))); + this.flagged = Field.getBoolean(fields.get("flagged")); + this.sprint = getSubResource(Sprint.class, fields, "sprint"); + this.closedSprints = getSubResourceArray(Sprint.class, fields, "closedSprint"); + this.description = Field.getString(fields.get("description")); + this.project = getSubResource(Project.class, fields, "project"); + this.comments = getSubResourceArray(Comment.class, fields, "comment"); + this.epic = getSubResource(Epic.class, fields, "epic"); + this.worklogs = getSubResourceArray(Worklog.class, fields, "worklog"); + this.timeTracking = getSubResource(TimeTracking.class, fields, "timetracking"); + this.environment = Field.getString(fields.get("environment")); + this.issueType = getSubResource(IssueType.class, fields, "issuetype"); + this.status = getSubResource(Status.class, fields, "status"); + this.resolution = getSubResource(Resolution.class, fields, "resolution"); + this.created = Field.getDateTime(fields.get("created")); + this.updated = Field.getDateTime(fields.get("updated")); + this.priority = getSubResource(Priority.class, fields, "priority"); + this.assignee = getSubResource(User.class, fields, "assignee"); + this.creator = getSubResource(User.class, fields, "creator"); + this.reporter = getSubResource(User.class, fields, "reporter"); + + addAttributes(fields); + } + } + + public String getKey() { + return key; + } + + public boolean isFlagged() { + return flagged; + } + + public Sprint getSprint() { + return sprint; + } + + public List getClosedSprints() { + return closedSprints; + } + + public String getDescription() { + return description; + } + + public Project getProject() { + return project; + } + + public List getComments() { + return comments; + } + + public Epic getEpic() { + return epic; + } + + public List getWorklogs() { + return worklogs; + } + + public TimeTracking getTimeTracking() { + return timeTracking; + } + + public IssueType getIssueType() { + return issueType; + } + + public Status getStatus() { + return status; + } + + public Resolution getResolution() { + return resolution; + } + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } + + public Priority getPriority() { + return priority; + } + + public User getAssignee() { + return assignee; + } + + public User getCreator() { + return creator; + } + + public User getReporter() { + return reporter; + } + + public String getEnvironment() { + return environment; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/IssueType.java b/src/main/java/net/rcarz/jiraclient/agile/IssueType.java new file mode 100644 index 0000000..47c97e8 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/IssueType.java @@ -0,0 +1,68 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile IssueType. + * + * @author pldupont + */ +public class IssueType extends AgileResource { + + private String description; + private boolean subTask; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public IssueType(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + + this.description = Field.getString(json.get("description")); + this.subTask = Field.getBoolean(json.get("subtask")); + } + + public String getDescription() { + return description; + } + + public boolean isSubTask() { + return subTask; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Priority.java b/src/main/java/net/rcarz/jiraclient/agile/Priority.java new file mode 100644 index 0000000..9cc7df0 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Priority.java @@ -0,0 +1,42 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile Priority. + * + * @author pldupont + */ +public class Priority extends AgileResource { + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Priority(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Project.java b/src/main/java/net/rcarz/jiraclient/agile/Project.java new file mode 100644 index 0000000..499758b --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Project.java @@ -0,0 +1,62 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile Project. + * + * @author pldupont + */ +public class Project extends AgileResource { + + private String key; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Project(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + + this.key = Field.getString(json.get("key")); + } + + public String getKey() { + return key; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Resolution.java b/src/main/java/net/rcarz/jiraclient/agile/Resolution.java new file mode 100644 index 0000000..aa15aa7 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Resolution.java @@ -0,0 +1,62 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile Resolution. + * + * @author pldupont + */ +public class Resolution extends AgileResource { + + private String description; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Resolution(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + + this.description = Field.getString(json.get("description")); + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Sprint.java b/src/main/java/net/rcarz/jiraclient/agile/Sprint.java new file mode 100644 index 0000000..ac20f63 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Sprint.java @@ -0,0 +1,115 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +import java.util.Date; +import java.util.List; + +/** + * Represents an Agile Sprint. + * + * @author pldupont + */ +public class Sprint extends AgileResource { + + private String state; + private long originBoardId; + private Date startDate; + private Date endDate; + private Date completeDate; + + /** + * Creates a rapid view from a JSON payload. + * + * @param restclient REST client instance + * @param json JSON payload + */ + protected Sprint(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Retrieve all sprints related to the specified board. + * + * @param restclient REST client instance + * @param sprintId The Internal JIRA sprint ID. + * @return The sprint for the specified ID. + * @throws JiraException when the retrieval fails + */ + public static Sprint get(RestClient restclient, long sprintId) throws JiraException { + return AgileResource.get(restclient, Sprint.class, RESOURCE_URI + "sprint/" + sprintId); + } + + /** + * Retrieve all sprints related to the specified board. + * + * @param restclient REST client instance + * @param boardId The Internal JIRA board ID. + * @return The list of sprints associated to the board. + * @throws JiraException when the retrieval fails + */ + public static List getAll(RestClient restclient, long boardId) throws JiraException { + return AgileResource.list(restclient, Sprint.class, RESOURCE_URI + "board/" + boardId + "/sprint"); + } + + /** + * @return All issues in the Sprint. + * @throws JiraException when the retrieval fails + */ + public List getIssues() throws JiraException { + return AgileResource.list(getRestclient(), Issue.class, RESOURCE_URI + "sprint/" + getId() + "/issue", "issues"); + } + + @Override + protected void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + state = Field.getString(json.get("state")); + originBoardId = getLong(json.get("originBoardId")); + startDate = Field.getDateTime(json.get("startDate")); + endDate = Field.getDateTime(json.get("endDate")); + completeDate = Field.getDateTime(json.get("completeDate")); + } + + public String getState() { + return state; + } + + public long getOriginBoardId() { + return originBoardId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Date getCompleteDate() { + return completeDate; + } +} + diff --git a/src/main/java/net/rcarz/jiraclient/agile/Status.java b/src/main/java/net/rcarz/jiraclient/agile/Status.java new file mode 100644 index 0000000..88d802d --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Status.java @@ -0,0 +1,62 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile Status. + * + * @author pldupont + */ +public class Status extends AgileResource { + + private String description; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Status(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + + this.description = Field.getString(json.get("description")); + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/TimeTracking.java b/src/main/java/net/rcarz/jiraclient/agile/TimeTracking.java new file mode 100644 index 0000000..98f19fd --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/TimeTracking.java @@ -0,0 +1,97 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile TimeTracking. + * + * @author pldupont + */ +public class TimeTracking extends AgileResource { + + private String originalEstimate; + private String remainingEstimate; + private String timeSpent; + private long originalEstimateSeconds; + private long remainingEstimateSeconds; + private long timeSpentSeconds; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public TimeTracking(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + this.originalEstimate = Field.getString(json.get("originalEstimate")); + this.remainingEstimate = Field.getString(json.get("remainingEstimate")); + this.timeSpent = Field.getString(json.get("timeSpent")); + this.originalEstimateSeconds = Field.getLong(json.get("originalEstimateSeconds")); + this.remainingEstimateSeconds = Field.getLong(json.get("remainingEstimateSeconds")); + this.timeSpentSeconds = Field.getLong(json.get("timeSpentSeconds")); + } + + @Override + public String toString() { + return String.format("%s{original='%s', remaining='%s', timeSpent='%s'}", + getClass().getSimpleName(), getOriginalEstimate(), getRemainingEstimate(), getTimeSpent()); + } + + public String getOriginalEstimate() { + return originalEstimate; + } + + public String getRemainingEstimate() { + return remainingEstimate; + } + + public String getTimeSpent() { + return timeSpent; + } + + public long getOriginalEstimateSeconds() { + return originalEstimateSeconds; + } + + public long getRemainingEstimateSeconds() { + return remainingEstimateSeconds; + } + + public long getTimeSpentSeconds() { + return timeSpentSeconds; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/User.java b/src/main/java/net/rcarz/jiraclient/agile/User.java new file mode 100644 index 0000000..e81a58f --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/User.java @@ -0,0 +1,84 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +/** + * Represents an Agile User. + * + * @author pldupont + */ +public class User extends AgileResource { + + private String emailAddress; + private String displayName; + private boolean active; + private String timeZone; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public User(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + this.emailAddress = Field.getString(json.get("emailAddress")); + this.displayName = Field.getString(json.get("displayName")); + this.active = Field.getBoolean(json.get("active")); + this.timeZone = Field.getString(json.get("timeZone")); + } + + @Override + public String toString() { + return String.format("%s{name='%s', Display Name='%s'}", getClass().getSimpleName(), getName(), getDisplayName()); + } + + public String getEmailAddress() { + return emailAddress; + } + + public String getDisplayName() { + return displayName; + } + + public boolean isActive() { + return active; + } + + public String getTimeZone() { + return timeZone; + } +} diff --git a/src/main/java/net/rcarz/jiraclient/agile/Worklog.java b/src/main/java/net/rcarz/jiraclient/agile/Worklog.java new file mode 100644 index 0000000..c1a2389 --- /dev/null +++ b/src/main/java/net/rcarz/jiraclient/agile/Worklog.java @@ -0,0 +1,110 @@ +/** + * jira-client - a simple JIRA REST client + * Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu) + *

+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.rcarz.jiraclient.agile; + +import net.rcarz.jiraclient.Field; +import net.rcarz.jiraclient.JiraException; +import net.rcarz.jiraclient.RestClient; +import net.sf.json.JSONObject; + +import java.util.Date; + +/** + * Represents an Agile Worklog. + * + * @author pldupont + */ +public class Worklog extends AgileResource { + + private User author; + private String comment; + private Date created; + private Date updated; + private User updateAuthor; + private Date started; + private String timeSpent; + private long timeSpentSeconds; + + /** + * Creates a new Agile resource. + * + * @param restclient REST client instance + * @param json JSON payload + */ + public Worklog(RestClient restclient, JSONObject json) throws JiraException { + super(restclient, json); + } + + /** + * Deserialize the json to extract standard attributes and keep a reference of + * other attributes. + * + * @param json The JSON object to read. + */ + @Override + void deserialize(JSONObject json) throws JiraException { + super.deserialize(json); + this.author = getSubResource(User.class, json, "author"); + this.comment = Field.getString(json.get("comment")); + this.created = Field.getDateTime(json.get("created")); + this.updated = Field.getDateTime(json.get("updated")); + this.updateAuthor = getSubResource(User.class, json, "updateAuthor"); + this.started = Field.getDateTime(json.get("started")); + this.timeSpent = Field.getString(json.get("timeSpent")); + this.timeSpentSeconds = Field.getLong(json.get("timeSpentSeconds")); + } + + @Override + public String toString() { + return String.format("%s{id=%s, comment='%s'}", getClass().getSimpleName(), getId(), getComment()); + } + + public User getAuthor() { + return author; + } + + public String getComment() { + return comment; + } + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } + + public User getUpdateAuthor() { + return updateAuthor; + } + + public Date getStarted() { + return started; + } + + public String getTimeSpent() { + return timeSpent; + } + + public long getTimeSpentSeconds() { + return timeSpentSeconds; + } +} diff --git a/src/test/groovy/AgileClientDemoTest.groovy b/src/test/groovy/AgileClientDemoTest.groovy new file mode 100644 index 0000000..52c2d9d --- /dev/null +++ b/src/test/groovy/AgileClientDemoTest.groovy @@ -0,0 +1,137 @@ +import net.rcarz.jiraclient.BasicCredentials +import net.rcarz.jiraclient.JiraClient +import net.rcarz.jiraclient.agile.* +import org.junit.Ignore +import org.junit.Test + +/** + * Demo test, used to show how to use the AgileClient API. + * @author pldupont + */ +class AgileClientDemoTest { + + private static final long BOARD_ID = 507L; + private static final long SPRINT_ID = 1165L; + private static final long EPIC_ID = 62133L; + private static final long ISSUE_ID = 63080L; + private static final String ISSUE_KEY = "TEST-1551"; + + @Test + @Ignore("Demo to use the AgileClient") + public void demoUsingAgileClient() { + // Init Agile client + AgileClient agileClient = new AgileClient(new JiraClient("https://jira.example.com/jira", new BasicCredentials("batman", "pow! pow!"))) + + demoBoard(agileClient) + demoSprint(agileClient) + demoEpic(agileClient) + demoIssue(agileClient) + } + + static void demoSprint(AgileClient agileClient) { + println "********** Sprint demo" + // Retrieve all sprints + List sprints = Sprint.getAll(agileClient.getRestclient(), BOARD_ID) + println sprints + + // Retrieve a specific Sprint + Sprint sprint1 = agileClient.getSprint(SPRINT_ID) + println sprint1 + Sprint sprint2 = Sprint.get(agileClient.getRestclient(), SPRINT_ID) + println sprint2 + println sprint1.toString() == sprint2.toString() + println sprint1.getSelfURL() + + // Retrieve issues associated to the sprint + List issues = sprint1.getIssues() + println issues + } + + static void demoIssue(AgileClient agileClient) { + println "********** Issue demo" + // Retrieve a specific Issue + Issue issue1 = agileClient.getIssue(ISSUE_ID) + println issue1 + Issue issue2 = Issue.get(agileClient.getRestclient(), ISSUE_ID) + println issue2 + println issue1.toString() == issue2.toString() + Issue issue3 = agileClient.getIssue(ISSUE_KEY) + println issue3 + println issue1.toString() == issue3.toString() + println issue1.getSelfURL() + + // Retrieve the issue attribute + println issue1.getProject() + println issue1.getEpic() + println issue1.getSprint() + println issue1.getKey(); + println issue1.isFlagged(); + println issue1.getDescription(); + println issue1.getComments(); + println issue1.getWorklogs(); + println issue1.getTimeTracking(); + println issue1.getIssueType(); + println issue1.getStatus(); + println issue1.getResolution(); + println issue1.getCreated(); + println issue1.getUpdated(); + println issue1.getPriority(); + println issue1.getAssignee(); + println issue1.getCreator(); + println issue1.getReporter(); + println issue1.getEnvironment(); + } + + static void demoEpic(AgileClient agileClient) { + println "********** Epic demo" + // Retrieve a specific Epic + Epic epic1 = agileClient.getEpic(EPIC_ID) + println epic1 + Epic epic2 = Epic.get(agileClient.getRestclient(), EPIC_ID) + println epic2 + println epic1.toString() == epic2.toString() + println epic1.getSelfURL() + + // Retrieve the epic as a normal Issue + Issue issue = epic1.asIssue(true) + println issue + + // pldupont: Doesn't work with my version of JIRA, but the doc says otherwise. +// // Retrieve issues associated to the Epic +// List issues = epic1.getIssues() +// println issues + } + + static void demoBoard(AgileClient agileClient) { + println "********** Board demo" + // Retrieve all board + List boards = agileClient.getBoards() + println boards + + // Retrieve a specific Board + Board board1 = agileClient.getBoard(BOARD_ID) + println board1 + Board board2 = Board.get(agileClient.getRestclient(), BOARD_ID) + println board2 + println board1.toString() == board2.toString() + println board1.getSelfURL() + + // Retrieve sprints associated to the board + List sprints = board1.getSprints() + println sprints + + // Retrieve sprints associated to the board + List epics = board1.getEpics() + println epics + + // Retrieve sprints associated to the board + List backlog = board1.getBacklog() + println backlog + + // pldupont: Doesn't work with my version of JIRA, but the doc says otherwise. +// // Retrieve sprints associated to the board +// List issuesWithoutEpics = board1.getIssuesWithoutEpic() +// println sprints + + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/AbstractResourceTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/AbstractResourceTest.groovy new file mode 100644 index 0000000..d60ec19 --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/AbstractResourceTest.groovy @@ -0,0 +1,246 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.JiraClient +import net.rcarz.jiraclient.RestClient +import net.sf.json.JSONObject +import net.sf.json.JSONSerializer +import org.hamcrest.core.IsEqual +import org.hamcrest.core.IsNot +import org.hamcrest.core.IsNull + +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +/** + * Created on 2016-05-19. + * @author pldupont + */ +class AbstractResourceTest { + AgileClient agileClient; + RestClient mockRestClient; + + void "given an Agile Client"() { + mockRestClient = mock RestClient.class + def mockJiraClient = mock JiraClient.class + when(mockJiraClient.getRestClient()).thenReturn(mockRestClient) + agileClient = new AgileClient(mockJiraClient) + } + + RestClient "given a REST Client"() { + mockRestClient = mock RestClient.class + return mockRestClient + } + + Issue "given an Issue"() { + mockRestClient = mock RestClient.class + return new Issue(mockRestClient, JSONSerializer.toJSON(JSONResources.ISSUE) as JSONObject) + } + + static void "Assert equals to Board"(Board board) { + assertThat board, new IsNot<>(new IsNull()) + assertThat board.getId(), new IsEqual(JSONResources.BOARD_ID) + assertThat board.getName(), new IsEqual(JSONResources.BOARD_NAME) + assertThat board.getType(), new IsEqual(JSONResources.BOARD_TYPE) + assertThat board.getSelfURL(), new IsEqual(JSONResources.BOARD_SELF) + assertThat board.toString(), new IsEqual( + String.format("Board{id=%s, name='%s'}", + JSONResources.BOARD_ID, JSONResources.BOARD_NAME)) + + } + + static void "Assert equals to Sprint"(Sprint sprint) { + assertThat sprint, new IsNot<>(new IsNull()) + assertThat sprint.getId(), new IsEqual(JSONResources.SPRINT_ID) + assertThat sprint.getName(), new IsEqual(JSONResources.SPRINT_NAME) + assertThat sprint.getSelfURL(), new IsEqual(JSONResources.SPRINT_SELF) + assertThat sprint.getState(), new IsEqual(JSONResources.SPRINT_STATE) + assertThat sprint.getOriginBoardId(), new IsEqual(JSONResources.SPRINT_ORIGIN_BOARD_ID) + assertThat sprint.getStartDate(), new IsEqual(JSONResources.SPRINT_START_DATE) + assertThat sprint.getEndDate(), new IsEqual(JSONResources.SPRINT_END_DATE) + assertThat sprint.getCompleteDate(), new IsEqual(JSONResources.SPRINT_COMPLETE_DATE) + assertThat sprint.toString(), new IsEqual( + String.format("Sprint{id=%s, name='%s'}", + JSONResources.SPRINT_ID, JSONResources.SPRINT_NAME)) + } + + static void "Assert equals to Epic"(Epic epic) { + assertThat epic, new IsNot<>(new IsNull()) + assertThat epic.getId(), new IsEqual(JSONResources.EPIC_ID) + assertThat epic.getName(), new IsEqual(JSONResources.EPIC_NAME) + assertThat epic.getSelfURL(), new IsEqual(JSONResources.EPIC_SELF) + assertThat epic.getKey(), new IsEqual(JSONResources.EPIC_KEY) + assertThat epic.getSummary(), new IsEqual(JSONResources.EPIC_SUMMARY) + assertThat epic.isDone(), new IsEqual(JSONResources.EPIC_DONE) + assertThat epic.toString(), new IsEqual( + String.format("Epic{id=%s, name='%s'}", + JSONResources.EPIC_ID, JSONResources.EPIC_NAME)) + } + + static void "Assert equals to Project"(Project project) { + assertThat project, new IsNot<>(new IsNull()) + assertThat project.getId(), new IsEqual(JSONResources.PROJECT_ID) + assertThat project.getName(), new IsEqual(JSONResources.PROJECT_NAME) + assertThat project.getSelfURL(), new IsEqual(JSONResources.PROJECT_SELF) + assertThat project.getKey(), new IsEqual(JSONResources.PROJECT_KEY) + assertThat project.toString(), new IsEqual( + String.format("Project{id=%s, name='%s'}", + JSONResources.PROJECT_ID, JSONResources.PROJECT_NAME)) + } + + static void "Assert equals to Comment"(Comment comment) { + assertThat comment, new IsNot<>(new IsNull()) + assertThat comment.getId(), new IsEqual(JSONResources.ISSUE_COMMENT_ID) + assertThat comment.getName(), new IsNull() + assertThat comment.getSelfURL(), new IsEqual(JSONResources.ISSUE_COMMENT_SELF) + assertThat comment.getBody(), new IsEqual(JSONResources.ISSUE_COMMENT_BODY) + assertThat comment.getCreated(), new IsEqual(JSONResources.ISSUE_COMMENT_CREATED) + assertThat comment.getUpdated(), new IsEqual(JSONResources.ISSUE_COMMENT_UPDATED) + "Assert equals to User"(comment.getAuthor()) + "Assert equals to User"(comment.getUpdateAuthor()) + assertThat comment.toString(), new IsEqual( + String.format("Comment{id=%s, body='%s'}", + JSONResources.ISSUE_COMMENT_ID, JSONResources.ISSUE_COMMENT_BODY)) + } + + static void "Assert equals to TimeTracking"(TimeTracking timeTracking) { + assertThat timeTracking, new IsNot<>(new IsNull()) + assertThat timeTracking.getId(), new IsEqual(0L) + assertThat timeTracking.getName(), new IsNull() + assertThat timeTracking.getSelfURL(), new IsNull() + assertThat timeTracking.getOriginalEstimate(), new IsEqual(JSONResources.ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE) + assertThat timeTracking.getRemainingEstimate(), new IsEqual(JSONResources.ISSUE_TIMETRACKING_REMAINING_ESTIMATE) + assertThat timeTracking.getTimeSpent(), new IsEqual(JSONResources.ISSUE_TIMETRACKING_TIME_SPENT) + assertThat timeTracking.getOriginalEstimateSeconds(), new IsEqual(JSONResources.ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE_SECONDS) + assertThat timeTracking.getRemainingEstimateSeconds(), new IsEqual(JSONResources.ISSUE_TIMETRACKING_REMAINING_ESTIMATE_SECONDS) + assertThat timeTracking.getTimeSpentSeconds(), new IsEqual(JSONResources.ISSUE_TIMETRACKING_TIME_SPENT_SECONDS) + assertThat timeTracking.toString(), new IsEqual( + String.format("TimeTracking{original='%s', remaining='%s', timeSpent='%s'}", + JSONResources.ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE, + JSONResources.ISSUE_TIMETRACKING_REMAINING_ESTIMATE, + JSONResources.ISSUE_TIMETRACKING_TIME_SPENT)) + } + + static void "Assert equals to IssueType"(IssueType issueType) { + assertThat issueType, new IsNot<>(new IsNull()) + assertThat issueType.getId(), new IsEqual(JSONResources.ISSUE_TYPE_ID) + assertThat issueType.getName(), new IsEqual(JSONResources.ISSUE_TYPE_NAME) + assertThat issueType.getSelfURL(), new IsEqual(JSONResources.ISSUE_TYPE_SELF) + assertThat issueType.getDescription(), new IsEqual(JSONResources.ISSUE_TYPE_DESCRIPTION) + assertThat issueType.isSubTask(), new IsEqual(JSONResources.ISSUE_TYPE_SUB_TASK) + assertThat issueType.toString(), new IsEqual( + String.format("IssueType{id=%s, name='%s'}", + JSONResources.ISSUE_TYPE_ID, JSONResources.ISSUE_TYPE_NAME)) + } + + static void "Assert equals to Status"(Status status) { + assertThat status, new IsNot<>(new IsNull()) + assertThat status.getId(), new IsEqual(JSONResources.ISSUE_STATUS_ID) + assertThat status.getName(), new IsEqual(JSONResources.ISSUE_STATUS_NAME) + assertThat status.getSelfURL(), new IsEqual(JSONResources.ISSUE_STATUS_SELF) + assertThat status.getDescription(), new IsEqual(JSONResources.ISSUE_STATUS_DESCRIPTION) + assertThat status.toString(), new IsEqual( + String.format("Status{id=%s, name='%s'}", + JSONResources.ISSUE_STATUS_ID, JSONResources.ISSUE_STATUS_NAME)) + } + + static void "Assert equals to Resolution"(Resolution resolution) { + assertThat resolution, new IsNot<>(new IsNull()) + assertThat resolution.getId(), new IsEqual(JSONResources.ISSUE_RESOLUTION_ID) + assertThat resolution.getName(), new IsEqual(JSONResources.ISSUE_RESOLUTION_NAME) + assertThat resolution.getSelfURL(), new IsEqual(JSONResources.ISSUE_RESOLUTION_SELF) + assertThat resolution.getDescription(), new IsEqual(JSONResources.ISSUE_RESOLUTION_DESCRIPTION) + assertThat resolution.toString(), new IsEqual( + String.format("Resolution{id=%s, name='%s'}", + JSONResources.ISSUE_RESOLUTION_ID, JSONResources.ISSUE_RESOLUTION_NAME)) + } + + static void "Assert equals to Priority"(Priority priority) { + assertThat priority, new IsNot<>(new IsNull()) + assertThat priority.getId(), new IsEqual(JSONResources.ISSUE_PRIORITY_ID) + assertThat priority.getName(), new IsEqual(JSONResources.ISSUE_PRIORITY_NAME) + assertThat priority.getSelfURL(), new IsEqual(JSONResources.ISSUE_PRIORITY_SELF) + assertThat priority.toString(), new IsEqual( + String.format("Priority{id=%s, name='%s'}", + JSONResources.ISSUE_PRIORITY_ID, JSONResources.ISSUE_PRIORITY_NAME)) + } + + static void "Assert equals to User"(User user) { + assertThat user, new IsNot<>(new IsNull()) + assertThat user.getId(), new IsEqual(0L) + assertThat user.getName(), new IsEqual(JSONResources.USER_NAME) + assertThat user.getSelfURL(), new IsEqual(JSONResources.USER_SELF) + assertThat user.getEmailAddress(), new IsEqual(JSONResources.USER_EMAIL_ADDRESS) + assertThat user.getDisplayName(), new IsEqual(JSONResources.USER_DISPLAY_NAME) + assertThat user.isActive(), new IsEqual(JSONResources.USER_ACTIVE) + assertThat user.getTimeZone(), new IsEqual(JSONResources.USER_TIME_ZONE) + assertThat user.toString(), new IsEqual( + String.format("User{name='%s', Display Name='%s'}", + JSONResources.USER_NAME, JSONResources.USER_DISPLAY_NAME)) + } + + static void "Assert equals to Worklog"(Worklog worklog) { + assertThat worklog, new IsNot<>(new IsNull()) + assertThat worklog.getId(), new IsEqual(JSONResources.ISSUE_WORKLOG_ID) + assertThat worklog.getName(), new IsNull() + assertThat worklog.getSelfURL(), new IsEqual(JSONResources.ISSUE_WORKLOG_SELF) + assertThat worklog.getComment(), new IsEqual(JSONResources.ISSUE_WORKLOG_COMMENT) + assertThat worklog.getCreated(), new IsEqual(JSONResources.ISSUE_WORKLOG_CREATED) + assertThat worklog.getUpdated(), new IsEqual(JSONResources.ISSUE_WORKLOG_UPDATED) + assertThat worklog.getStarted(), new IsEqual(JSONResources.ISSUE_WORKLOG_STARTED) + assertThat worklog.getTimeSpent(), new IsEqual(JSONResources.ISSUE_WORKLOG_TIMESPEND) + assertThat worklog.getTimeSpentSeconds(), new IsEqual(JSONResources.ISSUE_WORKLOG_TIMESPEND_SECONDS) + "Assert equals to User"(worklog.getAuthor()) + "Assert equals to User"(worklog.getUpdateAuthor()) + assertThat worklog.toString(), new IsEqual( + String.format("Worklog{id=%s, comment='%s'}", + JSONResources.ISSUE_WORKLOG_ID, JSONResources.ISSUE_WORKLOG_COMMENT)) + } + + static void "Assert equals to Issue"(Issue issue) { + assertThat issue, new IsNot<>(new IsNull()) + assertThat issue.getAttribute("fields"), new IsNot<>(new IsNull()) + assertThat issue.getId(), new IsEqual(JSONResources.ISSUE_ID) + assertThat issue.getName(), new IsEqual(JSONResources.ISSUE_SUMMARY) + assertThat issue.getSelfURL(), new IsEqual(JSONResources.ISSUE_SELF) + assertThat issue.getKey(), new IsEqual(JSONResources.ISSUE_KEY) + assertThat issue.isFlagged(), new IsEqual(JSONResources.ISSUE_FLAGGED) + "Assert equals to Sprint"(issue.getSprint()) + assertThat issue.getClosedSprints(), new IsNot<>(new IsNull<>()) + assertThat issue.getClosedSprints().size(), new IsEqual(3) + assertThat issue.getDescription(), new IsEqual(JSONResources.ISSUE_DESCRIPTION) + "Assert equals to Project"(issue.getProject()) + assertThat issue.getComments(), new IsNot<>(new IsNull<>()) + assertThat issue.getComments().size(), new IsEqual(1) + "Assert equals to Comment"(issue.getComments().get(0)) + "Assert equals to Epic"(issue.getEpic()) + "Assert equals to TimeTracking"(issue.getTimeTracking()) + assertThat issue.getWorklogs(), new IsNot<>(new IsNull<>()) + assertThat issue.getWorklogs().size(), new IsEqual(1) + "Assert equals to Worklog"(issue.getWorklogs().get(0)) + assertThat issue.getEnvironment(), new IsEqual(JSONResources.ISSUE_ENVIRONMENT) + "Assert equals to IssueType"(issue.getIssueType()) + "Assert equals to Status"(issue.getStatus()) + "Assert equals to Resolution"(issue.getResolution()) + assertThat issue.getCreated(), new IsEqual(JSONResources.ISSUE_CREATED) + assertThat issue.getUpdated(), new IsEqual(JSONResources.ISSUE_UPDATED) + "Assert equals to User"(issue.getAssignee()) + "Assert equals to User"(issue.getCreator()) + "Assert equals to User"(issue.getReporter()) + "Assert equals to Priority"(issue.getPriority()) + assertThat issue.toString(), new IsEqual( + String.format("Issue{id=%s, name='%s'}", + JSONResources.ISSUE_ID, JSONResources.ISSUE_SUMMARY)) + } + + static void "Assert equals to Issue Blank"(Issue issue) { + assertThat issue, new IsNot<>(new IsNull()) + assertThat issue.getAttribute("fields"), new IsNull() + assertThat issue.getId(), new IsEqual(JSONResources.BLANK_ISSUE1_ID) + assertThat issue.getName(), new IsNull() + assertThat issue.getSelfURL(), new IsEqual(JSONResources.BLANK_ISSUE1_SELF) + assertThat issue.toString(), new IsEqual( + String.format("Issue{id=%s, name='%s'}", + JSONResources.BLANK_ISSUE1_ID, null)) + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/AgileClientTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/AgileClientTest.groovy new file mode 100644 index 0000000..c64db74 --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/AgileClientTest.groovy @@ -0,0 +1,87 @@ +package net.rcarz.jiraclient.agile + +import net.sf.json.JSONSerializer +import org.hamcrest.core.IsEqual +import org.hamcrest.core.IsNot +import org.hamcrest.core.IsNull +import org.junit.Test + +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.when + +/** + * Created on 2016-05-19. + * @author pldupont + */ +class AgileClientTest extends AbstractResourceTest { + + @Test + void "Given an agileClient, when calling getBoards(), then receive a list of Board."() { + "given an Agile Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_BOARDS)) + + List boards = agileClient.getBoards(); + + assertThat boards, new IsNot<>(new IsNull()) + assertThat boards.size(), new IsEqual(2) + "Assert equals to Board"(boards.get(0)) + } + + @Test + void "Given an agileClient, when calling getBoard(boardId), then receive one Board."() { + "given an Agile Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/" + JSONResources.BOARD_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.BOARD)) + + Board board = agileClient.getBoard(JSONResources.BOARD_ID); + + assertThat board, new IsNot<>(new IsNull()) + "Assert equals to Board"(board) + } + + @Test + void "Given an agileClient, when calling getSprint(sprintId), then receive one Sprint."() { + "given an Agile Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "sprint/" + JSONResources.SPRINT_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.SPRINT)) + + Sprint sprint = agileClient.getSprint(JSONResources.SPRINT_ID); + + assertThat sprint, new IsNot<>(new IsNull()) + "Assert equals to Sprint"(sprint) + } + + @Test + void "Given an agileClient, when calling getIssue(id), then receive one Issue."() { + "given an Agile Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.ISSUE_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.ISSUE)) + + Issue issue = agileClient.getIssue(JSONResources.ISSUE_ID); + + "Assert equals to Issue"(issue) + } + + @Test + void "Given an agileClient, when calling getIssue(key), then receive one Issue."() { + "given an Agile Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.ISSUE_KEY)) + .thenReturn(JSONSerializer.toJSON(JSONResources.ISSUE)) + + Issue issue = agileClient.getIssue(JSONResources.ISSUE_KEY); + + "Assert equals to Issue"(issue) + } + + @Test + void "Given an agileClient, when calling getEpic(id), then receive one Epic."() { + "given an Agile Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "epic/" + JSONResources.EPIC_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.EPIC)) + + Epic epic = agileClient.getEpic(JSONResources.EPIC_ID); + + "Assert equals to Epic"(epic) + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/AgileResourceTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/AgileResourceTest.groovy new file mode 100644 index 0000000..88eeb2b --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/AgileResourceTest.groovy @@ -0,0 +1,91 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.JiraException +import net.sf.json.JSONSerializer +import org.hamcrest.core.IsNot +import org.hamcrest.core.IsNull +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException + +import static org.junit.Assert.assertThat + +/** + * Test for edge cases on deserialization. + * @author pldupont + */ +class AgileResourceTest extends AbstractResourceTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + void "given a valid single resource JSON and a valid type, when calling getResource(), then should return an object"() { + def aRESTClient = "given a REST Client"() + def aValidResource = JSONSerializer.toJSON(JSONResources.BOARD) + + def resource = AgileResource.getResource(Board.class, aValidResource, aRESTClient) + + assertThat resource, new IsNot<>(new IsNull()) + } + + @Test + void "given an invalid single resource JSON and a valid type, when calling getResource(), then should throw an exception"() { + def aRESTClient = "given a REST Client"() + def anInvalidResource = JSONResources.BOARD + expectedException.expectMessage("JSON payload is malformed") + expectedException.expect(JiraException.class) + + AgileResource.getResource(Board.class, anInvalidResource, aRESTClient) + } + + @Test + void "given a valid single resource JSON and an invalid type, when calling getResource(), then should throw an exception"() { + def aRESTClient = "given a REST Client"() + def aValidResource = JSONSerializer.toJSON(JSONResources.BOARD) + expectedException.expectMessage("Failed to deserialize object.") + expectedException.expect(JiraException.class) + + AgileResource.getResource(String.class, aValidResource, aRESTClient) + } + + @Test + void "given a valid resource array JSON and a valid type, when calling getResource(), then should return an object"() { + def aRESTClient = "given a REST Client"() + def aValidResource = JSONSerializer.toJSON(JSONResources.LIST_OF_BOARDS) + + def resource = AgileResource.getResourceArray(Board.class, aValidResource, aRESTClient, "values") + + assertThat resource, new IsNot<>(new IsNull()) + } + + @Test + void "given a valid resource array JSON and a valid type but list name invalid, when calling getResource(), then should return an object"() { + def aRESTClient = "given a REST Client"() + def aValidResource = JSONSerializer.toJSON(JSONResources.LIST_OF_BOARDS) + expectedException.expectMessage("No array found for name 'v'") + expectedException.expect(JiraException.class) + + AgileResource.getResourceArray(Board.class, aValidResource, aRESTClient, "v") + } + + @Test + void "given an invalid resource array JSON and a valid type, when calling getResource(), then should throw an exception"() { + def aRESTClient = "given a REST Client"() + def anInvalidResource = JSONResources.LIST_OF_BOARDS + expectedException.expectMessage("JSON payload is malformed") + expectedException.expect(JiraException.class) + + AgileResource.getResourceArray(Board.class, anInvalidResource, aRESTClient, "values") + } + + @Test + void "given a valid resource array JSON and an invalid type, when calling getResource(), then should throw an exception"() { + def aRESTClient = "given a REST Client"() + def aValidResource = JSONSerializer.toJSON(JSONResources.LIST_OF_BOARDS) + expectedException.expectMessage("Failed to deserialize object.") + expectedException.expect(JiraException.class) + + AgileResource.getResourceArray(String.class, aValidResource, aRESTClient, "values") + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/BoardTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/BoardTest.groovy new file mode 100644 index 0000000..eaba815 --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/BoardTest.groovy @@ -0,0 +1,130 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.JiraException +import net.rcarz.jiraclient.RestClient +import net.rcarz.jiraclient.RestException +import net.sf.json.JSONObject +import net.sf.json.JSONSerializer +import org.hamcrest.core.IsEqual +import org.hamcrest.core.IsNot +import org.hamcrest.core.IsNull +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException + +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.when + +/** + * Created on 2016-05-19. + * @author pldupont + */ +class BoardTest extends AbstractResourceTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + void "Given a RestClient, when calling getAll(), then receive a list of Board."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_BOARDS)) + + List boards = Board.getAll(mockRestClient); + + assertThat boards, new IsNot<>(new IsNull()) + assertThat boards.size(), new IsEqual(2) + "Assert equals to Board"(boards.get(0)) + } + + @Test + void "Given a RestClient, when calling getAll() and use doesn't have access, then throws an 401 error."() { + RestException unauthorized = new RestException("Do not have access", 401, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve a list of Board : /rest/agile/1.0/board"); + + Board.getAll(mockRestClient); + } + + @Test + void "Given a RestClient, when calling getBoard(boardId), then receive one Board."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/${JSONResources.BOARD_ID}")) + .thenReturn(JSONSerializer.toJSON(JSONResources.BOARD)) + + Board board = Board.get(mockRestClient, JSONResources.BOARD_ID); + + "Assert equals to Board"(board) + } + + @Test + void "Given a RestClient, when calling getBoard(666), then throws an 404 error."() { + RestException unauthorized = new RestException("Do not have access", 404, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/666")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve Board : /rest/agile/1.0/board/666"); + + Board.get(mockRestClient, 666); + } + + @Test + void "Given a valid Board, when calling getSprints(), then receive a list of Sprints."() { + RestClient mockRestClient = "given a REST Client"() + Board mockBoard = new Board(mockRestClient, JSONSerializer.toJSON(JSONResources.BOARD) as JSONObject) + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/${JSONResources.BOARD_ID}/sprint")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_SPRINTS)) + + List sprints = mockBoard.getSprints(); + + assertThat sprints, new IsNot<>(new IsNull()) + assertThat sprints.size(), new IsEqual(2) + "Assert equals to Sprint"(sprints.get(0)) + } + + @Test + void "Given a valid Board, when calling getEpics(), then receive a list of Epics."() { + RestClient mockRestClient = "given a REST Client"() + Board mockBoard = new Board(mockRestClient, JSONSerializer.toJSON(JSONResources.BOARD) as JSONObject) + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/${JSONResources.BOARD_ID}/epic")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_EPICS)) + + List epics = mockBoard.getEpics(); + + assertThat epics, new IsNot<>(new IsNull()) + assertThat epics.size(), new IsEqual(2) + "Assert equals to Epic"(epics.get(0)) + } + + @Test + void "Given a valid Board, when calling getBacklog(), then receive a list of Issues."() { + RestClient mockRestClient = "given a REST Client"() + Board mockBoard = new Board(mockRestClient, JSONSerializer.toJSON(JSONResources.BOARD) as JSONObject) + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/${JSONResources.BOARD_ID}/backlog")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_ISSUES)) + + List backlog = mockBoard.getBacklog(); + + assertThat backlog, new IsNot<>(new IsNull()) + assertThat backlog.size(), new IsEqual(4) + "Assert equals to Issue"(backlog.get(0)) + } + + @Test + void "Given a valid Board, when calling getIssuesWithoutEpic(), then receive a list of Issues."() { + RestClient mockRestClient = "given a REST Client"() + Board mockBoard = new Board(mockRestClient, JSONSerializer.toJSON(JSONResources.BOARD) as JSONObject) + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/${JSONResources.BOARD_ID}/epic/none/issue")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_ISSUES)) + + List issues = mockBoard.getIssuesWithoutEpic(); + + assertThat issues, new IsNot<>(new IsNull()) + assertThat issues.size(), new IsEqual(4) + "Assert equals to Issue"(issues.get(0)) + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/EpicTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/EpicTest.groovy new file mode 100644 index 0000000..9034615 --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/EpicTest.groovy @@ -0,0 +1,109 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.JiraException +import net.rcarz.jiraclient.RestClient +import net.rcarz.jiraclient.RestException +import net.sf.json.JSONObject +import net.sf.json.JSONSerializer +import org.hamcrest.core.IsEqual +import org.hamcrest.core.IsNot +import org.hamcrest.core.IsNull +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException + +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.when + +/** + * Created on 2016-05-20. + * @author pldupont + */ +class EpicTest extends AbstractResourceTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + void "Given a valid Epic ID, when calling Epic.get(id), then receive one Epic."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "epic/" + JSONResources.EPIC_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.EPIC)) + + Epic epic = Epic.get(mockRestClient, JSONResources.EPIC_ID); + + "Assert equals to Epic"(epic) + } + + @Test + void "Given an invalid epic ID, when calling getEpic(666), then throws an 404 error."() { + RestException unauthorized = new RestException("Do not have access", 404, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "epic/666")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve Epic : /rest/agile/1.0/epic/666"); + + Epic.get(mockRestClient, 666); + } + + @Test + void "Given an epic without the issue cache, when calling asIssue(false), then call the REST Api."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.EPIC_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.ISSUE)) + Epic mockEpic = new Epic(mockRestClient, JSONSerializer.toJSON(JSONResources.EPIC) as JSONObject) + + assertThat mockEpic.issue, new IsNull() + Issue issue = mockEpic.asIssue(false) + + "Assert equals to Issue"(issue) + assertThat mockEpic.issue, new IsNot<>(new IsNull()) + } + + @Test + void "Given an epic with the issue cache, when calling asIssue(false), then use the cache version."() { + RestClient mockRestClient = "given a REST Client"() + Epic mockEpic = new Epic(mockRestClient, JSONSerializer.toJSON(JSONResources.EPIC) as JSONObject) + Issue mockIssue = new Issue(mockRestClient, JSONSerializer.toJSON(JSONResources.ISSUE) as JSONObject) + mockEpic.issue = mockIssue + + assertThat mockEpic.issue, new IsNot<>(new IsNull()) + Issue issue = mockEpic.asIssue(false) + + "Assert equals to Issue"(issue) + assertThat mockEpic.issue, new IsNot<>(new IsNull()) + assert mockEpic.issue == mockIssue + } + + @Test + void "Given an epic with the issue cache, when calling asIssue(true), then call the REST Api."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.EPIC_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.ISSUE)) + Epic mockEpic = new Epic(mockRestClient, JSONSerializer.toJSON(JSONResources.EPIC) as JSONObject) + Issue mockIssue = new Issue(mockRestClient, JSONSerializer.toJSON(JSONResources.ISSUE) as JSONObject) + mockEpic.issue = mockIssue + + assertThat mockEpic.issue, new IsNot<>(new IsNull()) + Issue issue = mockEpic.asIssue(true) + + "Assert equals to Issue"(issue) + assertThat mockEpic.issue, new IsNot<>(new IsNull()) + assert mockEpic.issue != mockIssue + } + + @Test + void "Given a valid Epic, when calling getIssues(), then receive a list of Issues."() { + RestClient mockRestClient = "given a REST Client"() + Epic mockEpic = new Epic(mockRestClient, JSONSerializer.toJSON(JSONResources.EPIC) as JSONObject) + when(mockRestClient.get(AgileResource.RESOURCE_URI + "epic/${JSONResources.EPIC_ID}/issue")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_ISSUES)) + + List issues = mockEpic.getIssues(); + + assertThat issues, new IsNot<>(new IsNull()) + assertThat issues.size(), new IsEqual(4) + "Assert equals to Issue"(issues.get(0)) + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/IssueTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/IssueTest.groovy new file mode 100644 index 0000000..65f232f --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/IssueTest.groovy @@ -0,0 +1,78 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.JiraException +import net.rcarz.jiraclient.RestClient +import net.rcarz.jiraclient.RestException +import net.sf.json.JSONSerializer +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException + +import static org.mockito.Mockito.when + +/** + * Created on 2016-05-20. + * @author pldupont + */ +class IssueTest extends AbstractResourceTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + void "Given a valid issue ID, when calling Issue.get(id), then receive one Issue."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.ISSUE_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.ISSUE)) + + Issue issue = Issue.get(mockRestClient, JSONResources.ISSUE_ID); + + "Assert equals to Issue"(issue) + } + + @Test + void "Given an invalid issue ID, when calling getIssue(666), then throws an 404 error."() { + RestException unauthorized = new RestException("Do not have access", 404, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/666")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve Issue : /rest/agile/1.0/issue/666"); + + Issue.get(mockRestClient, 666); + } + + @Test + void "Given a valid issue Key, when calling Issue.get(key), then receive one Issue."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.ISSUE_KEY)) + .thenReturn(JSONSerializer.toJSON(JSONResources.ISSUE)) + + Issue issue = Issue.get(mockRestClient, JSONResources.ISSUE_KEY); + + "Assert equals to Issue"(issue) + } + + @Test + void "Given an invalid issue Key, when calling getIssue('HSP-2'), then throws an 404 error."() { + RestException unauthorized = new RestException("Do not have access", 404, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/HSP-2")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve Issue : /rest/agile/1.0/issue/HSP-2"); + + Issue.get(mockRestClient, "HSP-2"); + } + + @Test + void "Given an issue empty, when calling Issue.get(id), then deserialize properly."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "issue/" + JSONResources.BLANK_ISSUE1_ID)) + .thenReturn(JSONSerializer.toJSON(JSONResources.BLANK_ISSUE1)) + + Issue issue = Issue.get(mockRestClient, JSONResources.BLANK_ISSUE1_ID); + + "Assert equals to Issue Blank"(issue) + } +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/JSONResources.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/JSONResources.groovy new file mode 100644 index 0000000..173a6db --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/JSONResources.groovy @@ -0,0 +1,495 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.Field + +/** + * Created on 2016-05-19. + * @author pldupont + */ +interface JSONResources { + + long BOARD_ID = 84L + String BOARD_SELF = "http://www.example.com/jira/rest/agile/1.0/board/${BOARD_ID}" + String BOARD_NAME = "scrum board" + String BOARD_TYPE = "scrum" + String BOARD = """{ + "id": ${BOARD_ID}, + "self": "${BOARD_SELF}", + "name": "${BOARD_NAME}", + "type": "${BOARD_TYPE}" +}""" + + String LIST_OF_BOARDS = """{ + "maxResults": 2, + "startAt": 1, + "total": 2, + "isLast": true, + "values": [ + ${BOARD}, + { + "id": 92, + "self": "http://www.example.com/jira/rest/agile/1.0/board/92", + "name": "kanban board", + "type": "kanban" + } + ] +}""" + + long SPRINT_ID = 37L + String SPRINT_NAME = "sprint 1" + String SPRINT_SELF = "http://www.example.com/jira/rest/agile/1.0/sprint/${SPRINT_ID}" + String SPRINT_STATE = "closed" + long SPRINT_ORIGIN_BOARD_ID = BOARD_ID + String SPRINT_START_DATE_STR = "2015-04-11T15:22:00.000+10:00" + Date SPRINT_START_DATE = Field.getDateTime(SPRINT_START_DATE_STR) + String SPRINT_END_DATE_STR = "2015-04-20T01:22:00.000+10:00" + Date SPRINT_END_DATE = Field.getDateTime(SPRINT_END_DATE_STR) + String SPRINT_COMPLETE_DATE_STR = "2015-04-20T11:04:00.000+10:00" + Date SPRINT_COMPLETE_DATE = Field.getDateTime(SPRINT_COMPLETE_DATE_STR) + String SPRINT = """{ + "id": ${SPRINT_ID}, + "self": "${SPRINT_SELF}", + "state": "${SPRINT_STATE}", + "name": "${SPRINT_NAME}", + "startDate": "${SPRINT_START_DATE_STR}", + "endDate": "${SPRINT_END_DATE_STR}", + "completeDate": "${SPRINT_COMPLETE_DATE_STR}", + "originBoardId": ${BOARD_ID} +}""" + + String LIST_OF_SPRINTS = """{ + "maxResults": 2, + "startAt": 1, + "total": 2, + "isLast": true, + "values": [ + ${SPRINT}, + { + "id": 72, + "self": "http://www.example.com/jira/rest/agile/1.0/sprint/73", + "state": "future", + "name": "sprint 2" + } + ] +}""" + + long EPIC_ID = 23 + String EPIC_SELF = "http://www.example.com/jira/rest/agile/1.0/epic/${EPIC_ID}" + String EPIC_NAME = "epic 1" + String EPIC_KEY = "EX" + String EPIC_SUMMARY = "epic 1 summary" + boolean EPIC_DONE = true + String EPIC = """{ + "id": ${EPIC_ID}, + "self": "${EPIC_SELF}", + "name": "${EPIC_NAME}", + "key": "${EPIC_KEY}", + "summary": "${EPIC_SUMMARY}", + "color": { + "key": "color_4" + }, + "done": ${EPIC_DONE} +}""" + String LIST_OF_EPICS = """{ + "maxResults": 2, + "startAt": 1, + "total": 5, + "isLast": false, + "values": [ + ${EPIC}, + { + "id": 37, + "self": "http://www.example.com/jira/rest/agile/1.0/epic/13", + "name": "epic 2", + "summary": "epic 2 summary", + "color": { + "key": "color_2" + }, + "done": false + } + ] +}""" + + long PROJECT_ID = 10000L + String PROJECT_KEY = "EX" + String PROJECT_NAME = "Example" + String PROJECT_SELF = "http://www.example.com/jira/rest/api/2/project/${PROJECT_KEY}" + String PROJECT = """{ + "self": "${PROJECT_SELF}", + "id": "${PROJECT_ID}", + "key": "${PROJECT_KEY}", + "name": "${PROJECT_NAME}", + "avatarUrls": { + "48x48": "http://www.example.com/jira/secure/projectavatar?size=large&pid=10000", + "24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000", + "16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "http://www.example.com/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + } + }""" + + String USER_NAME = "Example" + String USER_SELF = "https://www.example.com/rest/api/2/user?username=${USER_NAME}" + String USER_EMAIL_ADDRESS = "pldupont@example.com" + String USER_DISPLAY_NAME = "Pierre-Luc Dupont" + boolean USER_ACTIVE = true + String USER_TIME_ZONE = "America/New_York" + + String USER = """{ + "self" : "${USER_SELF}", + "name" : "${USER_NAME}", + "key" : "pldupont", + "emailAddress" : "${USER_EMAIL_ADDRESS}", + "avatarUrls" : { + "48x48" : "https://www.example.com/secure/useravatar?ownerId=pldupont&avatarId=11828", + "24x24" : "https://www.example.com/secure/useravatar?size=small&ownerId=pldupont&avatarId=11828", + "16x16" : "https://www.example.com/secure/useravatar?size=xsmall&ownerId=pldupont&avatarId=11828", + "32x32" : "https://www.example.com/secure/useravatar?size=medium&ownerId=pldupont&avatarId=11828" + }, + "displayName" : "${USER_DISPLAY_NAME}", + "active" : ${USER_ACTIVE}, + "timeZone" : "${USER_TIME_ZONE}" + }""" + + String ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE = "10m" + String ISSUE_TIMETRACKING_REMAINING_ESTIMATE = "3m" + String ISSUE_TIMETRACKING_TIME_SPENT = "6m" + long ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE_SECONDS = 600L + long ISSUE_TIMETRACKING_REMAINING_ESTIMATE_SECONDS = 200L + long ISSUE_TIMETRACKING_TIME_SPENT_SECONDS = 400L + String ISSUE_TIMETRACKING = """{ + "originalEstimate": "${ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE}", + "remainingEstimate": "${ISSUE_TIMETRACKING_REMAINING_ESTIMATE}", + "timeSpent": "${ISSUE_TIMETRACKING_TIME_SPENT}", + "originalEstimateSeconds": ${ISSUE_TIMETRACKING_ORIGINAL_ESTIMATE_SECONDS}, + "remainingEstimateSeconds": ${ISSUE_TIMETRACKING_REMAINING_ESTIMATE_SECONDS}, + "timeSpentSeconds": ${ISSUE_TIMETRACKING_TIME_SPENT_SECONDS} + }""" + + long ISSUE_WORKLOG_ID = 100028L + String ISSUE_WORKLOG_SELF = "http://www.example.com/jira/rest/api/2/issue/10010/worklog${ISSUE_WORKLOG_ID}" + String ISSUE_WORKLOG_COMMENT = "I did some work here." + String ISSUE_WORKLOG_CREATED_STR = "2016-03-21T15:25:17.882+0100" + Date ISSUE_WORKLOG_CREATED = Field.getDateTime(ISSUE_WORKLOG_CREATED_STR) + String ISSUE_WORKLOG_UPDATED_STR = "2016-03-21T15:26:17.882+0100" + Date ISSUE_WORKLOG_UPDATED = Field.getDateTime(ISSUE_WORKLOG_UPDATED_STR) + String ISSUE_WORKLOG_STARTED_STR = "2016-03-21T15:26:17.881+0100" + Date ISSUE_WORKLOG_STARTED = Field.getDateTime(ISSUE_WORKLOG_STARTED_STR) + String ISSUE_WORKLOG_TIMESPEND = "3h 20m" + long ISSUE_WORKLOG_TIMESPEND_SECONDS = 12000 + String ISSUE_WORKLOG = """{ + "self": "${ISSUE_WORKLOG_SELF}", + "author": ${USER}, + "updateAuthor": ${USER}, + "comment": "${ISSUE_WORKLOG_COMMENT}", + "created": "${ISSUE_WORKLOG_CREATED_STR}", + "updated": "${ISSUE_WORKLOG_UPDATED_STR}", + "visibility": { + "type": "group", + "value": "jira-developers" + }, + "started": "${ISSUE_WORKLOG_STARTED_STR}", + "timeSpent": "${ISSUE_WORKLOG_TIMESPEND}", + "timeSpentSeconds": ${ISSUE_WORKLOG_TIMESPEND_SECONDS}, + "id": "${ISSUE_WORKLOG_ID}", + "issueId": "10002" + }""" + + long ISSUE_COMMENT_ID = 9999L + String ISSUE_COMMENT_SELF = "http://www.example.com/jira/rest/api/2/issue/10010/comment/${ISSUE_COMMENT_ID}" + String ISSUE_COMMENT_BODY = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper." + String ISSUE_COMMENT_CREATED_STR = "2016-05-11T10:58:01.000-0400" + Date ISSUE_COMMENT_CREATED = Field.getDateTime(ISSUE_COMMENT_CREATED_STR) + String ISSUE_COMMENT_UPDATED_STR = "2016-05-30T14:20:29.000-0400" + Date ISSUE_COMMENT_UPDATED = Field.getDateTime(ISSUE_COMMENT_UPDATED_STR) + String ISSUE_COMMENT = """{ + "self": "${ISSUE_COMMENT_SELF}", + "id": "${ISSUE_COMMENT_ID}", + "author": ${USER}, + "body": "${ISSUE_COMMENT_BODY}", + "updateAuthor": ${USER}, + "created": "${ISSUE_COMMENT_CREATED_STR}", + "updated": "${ISSUE_COMMENT_UPDATED_STR}", + "visibility": { + "type": "role", + "value": "Administrators" + } + }""" + long ISSUE_TYPE_ID = 1L + String ISSUE_TYPE_NAME = "Bug" + String ISSUE_TYPE_SELF = "https://jira.acquisio.com/rest/api/2/issuetype/${ISSUE_TYPE_ID}" + String ISSUE_TYPE_DESCRIPTION = "A problem which impairs or prevents the functions of the product." + boolean ISSUE_TYPE_SUB_TASK = true + String ISSUE_TYPE = """{ + "self" : "${ISSUE_TYPE_SELF}", + "id" : "${ISSUE_TYPE_ID}", + "description" : "${ISSUE_TYPE_DESCRIPTION}", + "iconUrl" : "https://www.example.com/images/icons/issuetypes/bug.png", + "name" : "${ISSUE_TYPE_NAME}", + "subtask" : ${ISSUE_TYPE_SUB_TASK} + }""" + long ISSUE_RESOLUTION_ID = 6L + String ISSUE_RESOLUTION_NAME = "Not a bug" + String ISSUE_RESOLUTION_SELF = "https://jira.acquisio.com/rest/api/2/resolution/${ISSUE_RESOLUTION_ID}" + String ISSUE_RESOLUTION_DESCRIPTION = "The problem is not a problem" + String ISSUE_RESOLUTION = """{ + "self" : "${ISSUE_RESOLUTION_SELF}", + "id" : "${ISSUE_RESOLUTION_ID}", + "description" : "${ISSUE_RESOLUTION_DESCRIPTION}", + "name" : "${ISSUE_RESOLUTION_NAME}" + }""" + + long ISSUE_STATUS_ID = 6L + String ISSUE_STATUS_NAME = "Closed" + String ISSUE_STATUS_DESCRIPTION = "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened." + String ISSUE_STATUS_SELF = "https://www.example.com/rest/api/2/status/${ISSUE_STATUS_ID}" + String ISSUE_STATUS = """{ + "self" : "${ISSUE_STATUS_SELF}", + "description" : "${ISSUE_STATUS_DESCRIPTION}", + "iconUrl" : "https://www.example.com/images/icons/statuses/closed.png", + "name" : "${ISSUE_STATUS_NAME}", + "id" : "${ISSUE_STATUS_ID}", + "statusCategory" : { + "self" : "https://www.example.com/rest/api/2/statuscategory/3", + "id" : 3, + "key" : "done", + "colorName" : "green", + "name" : "Done" + } + }""" + long ISSUE_PRIORITY_ID = 2L + String ISSUE_PRIORITY_NAME = "Critical" + String ISSUE_PRIORITY_SELF = "https://www.example.com/rest/api/2/priority/${ISSUE_PRIORITY_ID}" + String ISSUE_PRIORITY = """{ + "self" : "${ISSUE_PRIORITY_SELF}", + "iconUrl" : "https://www.example.com/images/icons/priorities/critical.png", + "name" : "${ISSUE_PRIORITY_NAME}", + "id" : "${ISSUE_PRIORITY_ID}" + }""" + long ISSUE_ID = 10001L + String ISSUE_SELF = "http://www.example.com/jira/rest/agile/1.0/board/92/issue/10001" + String ISSUE_KEY = "HSP-1" + String ISSUE_SUMMARY = "Issue summary" + boolean ISSUE_FLAGGED = true + String ISSUE_DESCRIPTION = "example bug report" + String ISSUE_ENVIRONMENT = "PROD" + String ISSUE_CREATED_STR = "2016-05-11T10:58:01.000-0400" + Date ISSUE_CREATED = Field.getDateTime(ISSUE_CREATED_STR) + String ISSUE_UPDATED_STR = "2016-05-30T14:20:29.000-0400" + Date ISSUE_UPDATED = Field.getDateTime(ISSUE_UPDATED_STR) + String ISSUE = """{ + "expand": "", + "id": "${ISSUE_ID}", + "self": "${ISSUE_SELF}", + "key": "${ISSUE_KEY}", + "fields": { + "flagged": ${ISSUE_FLAGGED}, + "sprint": ${SPRINT}, + "summary": "${ISSUE_SUMMARY}", + "closedSprint": { + "closedSprints": [ + { + "id": 21, + "self": "http://www.example.com/jira/rest/agile/1.0/sprint/21", + "state": "closed", + "name": "sprint 1", + "startDate": "2015-04-11T15:22:00.000+10:00", + "endDate": "2015-04-20T01:22:00.000+10:00", + "completeDate": "2015-04-20T11:04:00.000+10:00" + }, + { + "id": 22, + "self": "http://www.example.com/jira/rest/agile/1.0/sprint/22", + "state": "closed", + "name": "sprint 1", + "startDate": "2015-04-11T15:22:00.000+10:00", + "endDate": "2015-04-20T01:22:00.000+10:00", + "completeDate": "2015-04-20T11:04:00.000+10:00" + }, + { + "id": 23, + "self": "http://www.example.com/jira/rest/agile/1.0/sprint/23", + "state": "closed", + "name": "sprint 1", + "startDate": "2015-04-11T15:22:00.000+10:00", + "endDate": "2015-04-20T01:22:00.000+10:00", + "completeDate": "2015-04-20T11:04:00.000+10:00" + } + ] + }, + "description": "${ISSUE_DESCRIPTION}", + "project": ${PROJECT}, + "comment": { + "comments" : [ + ${ISSUE_COMMENT} + ] + }, + "epic": ${EPIC}, + "worklog": { + "worklogs": [ + ${ISSUE_WORKLOG} + ] + }, + "timetracking": ${ISSUE_TIMETRACKING}, + "environment": "${ISSUE_ENVIRONMENT}", + "issuetype" : ${ISSUE_TYPE}, + "resolution" : ${ISSUE_RESOLUTION}, + "assignee" : ${USER}, + "creator" : ${USER}, + "reporter" : ${USER}, + "created" : "${ISSUE_CREATED_STR}", + "updated" : "${ISSUE_UPDATED_STR}", + "status" : ${ISSUE_STATUS}, + "priority" : ${ISSUE_PRIORITY}, + } +}""" + + long BLANK_ISSUE1_ID = 10010 + String BLANK_ISSUE1_SELF = "http://www.example.com/jira/rest/agile/1.0/board/92/issue/${BLANK_ISSUE1_ID}" + String BLANK_ISSUE1_KEY = "HSP-1" + String BLANK_ISSUE1 = """{ + "expand": "", + "id": "${BLANK_ISSUE1_ID}", + "self": "${BLANK_ISSUE1_SELF}", + "key": "${BLANK_ISSUE1_KEY}", +}""" + + long BLANK_ISSUE2_ID = 10011 + String BLANK_ISSUE2_SELF = "http://www.example.com/jira/rest/agile/1.0/board/92/issue/${BLANK_ISSUE2_ID}" + String BLANK_ISSUE2_KEY = "HSP-1" + String BLANK_ISSUE2 = """{ + "expand": "", + "id": "${BLANK_ISSUE2_ID}", + "self": "${BLANK_ISSUE2_SELF}", + "key": "${BLANK_ISSUE2_KEY}", + "fields": { + "flagged": false, + } +}""" + + String LIST_OF_ISSUES = """{ + "expand": "names,schema", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + ${ISSUE}, + ${BLANK_ISSUE1}, + ${BLANK_ISSUE2}, + { + "expand": "", + "id": "10005", + "self": "http://www.example.com/jira/rest/agile/1.0/board/92/issue/10005", + "key": "HSP-9", + "fields": { + "flagged": true, + "sprint": ${SPRINT}, + "closedSprint" : { + "closedSprints": [ + { + "id": 37, + "self": "http://www.example.com/jira/rest/agile/1.0/sprint/23", + "state": "closed", + "name": "sprint 1", + "startDate": "2015-04-11T15:22:00.000+10:00", + "endDate": "2015-04-20T01:22:00.000+10:00", + "completeDate": "2015-04-20T11:04:00.000+10:00" + } + ] + }, + "description": "example bug report", + "project": { + "self": "http://www.example.com/jira/rest/api/2/project/EX", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "http://www.example.com/jira/secure/projectavatar?size=large&pid=10000", + "24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000", + "16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "http://www.example.com/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + } + }, + "comment": { + "comments": [ + { + "self": "http://www.example.com/jira/rest/api/2/issue/10010/comment/10000", + "id": "10000", + "author": { + "self": "http://www.example.com/jira/rest/api/2/user?username=fred", + "name": "fred", + "displayName": "Fred F. User", + "active": false + }, + "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.", + "updateAuthor": { + "self": "http://www.example.com/jira/rest/api/2/user?username=fred", + "name": "fred", + "displayName": "Fred F. User", + "active": false + }, + "created": "2016-03-21T15:26:17.875+0100", + "updated": "2016-03-21T15:26:17.878+0100", + "visibility": { + "type": "role", + "value": "Administrators" + } + } + ] + }, + "epic": ${EPIC}, + "worklog": { + "worklogs": [ + { + "self": "http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000", + "author": { + "self": "http://www.example.com/jira/rest/api/2/user?username=fred", + "name": "fred", + "displayName": "Fred F. User", + "active": false + }, + "updateAuthor": { + "self": "http://www.example.com/jira/rest/api/2/user?username=fred", + "name": "fred", + "displayName": "Fred F. User", + "active": false + }, + "comment": "I did some work here.", + "updated": "2016-03-21T15:26:17.882+0100", + "visibility": { + "type": "group", + "value": "jira-developers" + }, + "started": "2016-03-21T15:26:17.881+0100", + "timeSpent": "3h 20m", + "timeSpentSeconds": 12000, + "id": "100028", + "issueId": "10002" + } + ] + }, + "updated": 1, + "timetracking": { + "originalEstimate": "10m", + "remainingEstimate": "3m", + "timeSpent": "6m", + "originalEstimateSeconds": 600, + "remainingEstimateSeconds": 200, + "timeSpentSeconds": 400 + } + } + } + ] +}""" +} diff --git a/src/test/groovy/net/rcarz/jiraclient/agile/SprintTest.groovy b/src/test/groovy/net/rcarz/jiraclient/agile/SprintTest.groovy new file mode 100644 index 0000000..330ee22 --- /dev/null +++ b/src/test/groovy/net/rcarz/jiraclient/agile/SprintTest.groovy @@ -0,0 +1,88 @@ +package net.rcarz.jiraclient.agile + +import net.rcarz.jiraclient.JiraException +import net.rcarz.jiraclient.RestClient +import net.rcarz.jiraclient.RestException +import net.sf.json.JSONObject +import net.sf.json.JSONSerializer +import org.hamcrest.core.IsEqual +import org.hamcrest.core.IsNot +import org.hamcrest.core.IsNull +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException + +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.when + +/** + * Created on 2016-05-19. + * @author pldupont + */ +class SprintTest extends AbstractResourceTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + void "Given a RestClient, when calling getAll(), then receive a list of Sprint."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/" + JSONResources.SPRINT_ORIGIN_BOARD_ID + "/sprint")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_SPRINTS)) + + List sprints = Sprint.getAll(mockRestClient, JSONResources.SPRINT_ORIGIN_BOARD_ID); + + assertThat sprints, new IsNot<>(new IsNull()) + assertThat sprints.size(), new IsEqual(2) + "Assert equals to Sprint"(sprints.get(0)) + } + + @Test + void "Given a RestClient, when calling getAll() and use doesn't have access, then throws an 401 error."() { + RestException unauthorized = new RestException("Do not have access", 401, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "board/" + JSONResources.SPRINT_ORIGIN_BOARD_ID + "/sprint")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve a list of Sprint : /rest/agile/1.0/board/" + JSONResources.SPRINT_ORIGIN_BOARD_ID + "/sprint"); + + Sprint.getAll(mockRestClient, JSONResources.SPRINT_ORIGIN_BOARD_ID); + } + + @Test + void "Given a RestClient, when calling getSprint(sprintId), then receive one Sprint."() { + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "sprint/${JSONResources.SPRINT_ID}")) + .thenReturn(JSONSerializer.toJSON(JSONResources.SPRINT)) + + Sprint sprint = Sprint.get(mockRestClient, JSONResources.SPRINT_ID); + + "Assert equals to Sprint"(sprint) + } + + @Test + void "Given a RestClient, when calling getSprint(666), then throws an 404 error."() { + RestException unauthorized = new RestException("Do not have access", 404, "Unauthorized") + RestClient mockRestClient = "given a REST Client"() + when(mockRestClient.get(AgileResource.RESOURCE_URI + "sprint/666")) + .thenThrow(unauthorized) + expectedException.expect(JiraException.class); + expectedException.expectMessage("Failed to retrieve Sprint : /rest/agile/1.0/sprint/666"); + + Sprint.get(mockRestClient, 666); + } + + @Test + void "Given a valid Sprint, when calling getIssues(), then receive a list of Issues."() { + RestClient mockRestClient = "given a REST Client"() + Sprint mockSprint = new Sprint(mockRestClient, JSONSerializer.toJSON(JSONResources.SPRINT) as JSONObject) + when(mockRestClient.get(AgileResource.RESOURCE_URI + "sprint/${JSONResources.SPRINT_ID}/issue")) + .thenReturn(JSONSerializer.toJSON(JSONResources.LIST_OF_ISSUES)) + + List issues = mockSprint.getIssues(); + + assertThat issues, new IsNot<>(new IsNull()) + assertThat issues.size(), new IsEqual(4) + "Assert equals to Issue"(issues.get(0)) + } +}