1
0
Fork 0

Merge pull request #10 from rcarz/master

Sync with rcarz/jira-client
master
Kyle Chaplin 2015-03-12 08:21:59 -05:00
commit 51aea783b6
50 changed files with 2442 additions and 175 deletions

2
.gitignore vendored
View File

@ -5,3 +5,5 @@ work/
.classpath
.settings
.project
.idea/
*.iml

View File

@ -1,3 +1,4 @@
Bob Carroll <bob.carroll@alum.rit.edu> @rcarz
Kyle Chaplin <chaplinkyle@gmail.com> @chaplinkyle
Alesandro Lang <info@alesandro-lang.com> @alesandroLang
Javier Molina <javinovich@gmail.com> @javinovich

View File

@ -32,7 +32,7 @@ Point your *settings.xml* at [Maven Central](http://repo1.maven.org/maven2) and
<dependency>
<groupId>net.rcarz</groupId>
<artifactId>jira-client</artifactId>
<version>0.3</version>
<version>0.5</version>
<scope>compile</scope>
</dependency>
```
@ -45,7 +45,6 @@ Patches are welcome and appreciated. Please try to follow existing styles, and s
```java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import net.rcarz.jiraclient.BasicCredentials;
@ -149,10 +148,10 @@ public class Example {
/* Set two new values for customfield_5678. */
issue.update()
.field("customfield_5678", new HashMap() {{
put("value", "foo");
put("value", "bar");
put("id", "1234"); /* you can also update using the value ID */
.field("customfield_5678", new ArrayList() {{
add("foo");
add("bar");
add(Field.valueById("1234")); /* you can also update using the value ID */
}})
.execute();

14
pom.xml
View File

@ -3,7 +3,7 @@
<groupId>net.rcarz</groupId>
<artifactId>jira-client</artifactId>
<version>0.4-SNAPSHOT</version>
<version>0.6-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jira-client</name>
@ -43,7 +43,6 @@
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -57,14 +56,19 @@
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -86,7 +86,7 @@ public class Attachment extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "attachment/" + id);
result = restclient.get(getBaseUri() + "attachment/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve attachment " + id, ex);
}

View File

@ -62,5 +62,14 @@ public class BasicCredentials implements ICredentials {
public String getLogonName() {
return username;
}
@Override
public void initialize(RestClient client) throws JiraException {
}
@Override
public void logout(RestClient client) throws JiraException {
}
}

View File

@ -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;
import java.util.Date;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONObject;
/**
* Issue change log.
*/
public class ChangeLog extends Resource {
/**
* List of change log entries.
*/
private List<ChangeLogEntry> entries = null;
/**
* Creates a change log from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
*/
protected ChangeLog(RestClient restclient, JSONObject json) {
super(restclient);
if (json != null)
deserialise(json);
}
/**
* Deserializes a change log from a json payload.
* @param json the json payload
*/
private void deserialise(JSONObject json) {
Map map = json;
entries = Field.getResourceArray(ChangeLogEntry.class, map.get(
Field.CHANGE_LOG_ENTRIES), restclient);
}
/**
* Returns the list of change log entries in the change log.
* @return the list of entries
*/
public List<ChangeLogEntry> getEntries() {
return entries;
}
}

View File

@ -0,0 +1,100 @@
/**
* 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;
import java.util.Date;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONObject;
/**
* Contains information about an issue change log entry. Change log entries are
* not returned by default when fetching issues. The <code>changelog</code>
* field (expansion) must be explicitly provided in
* {@link JiraClient#getIssue(String, String)}.
*/
public class ChangeLogEntry extends Resource {
/**
* Changelog author.
*/
private User author = null;
/**
* Date when the changelog entry was created.
*/
private Date created = null;
/**
* List of change log items in the change log entry.
*/
private List<ChangeLogItem> items = null;
/**
* Creates a change log from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
*/
protected ChangeLogEntry(RestClient restclient, JSONObject json) {
super(restclient);
if (json != null)
deserialise(json);
}
/**
* Deserializes a change log entry from a json payload.
* @param json the json payload
*/
private void deserialise(JSONObject json) {
Map map = json;
id = Field.getString(map.get("id"));
author = Field.getResource(User.class, map.get("author"), restclient);
created = Field.getDate(map.get("created"));
items = Field.getResourceArray(ChangeLogItem.class, map.get(
Field.CHANGE_LOG_ITEMS), restclient);
}
/**
* Obtains the author of the change log entry.
* @return the author
*/
public User getAuthor() {
return author;
}
/**
* Returns the date when the change log entry was created.
* @return the date
*/
public Date getCreated() {
return created;
}
/**
* Returns the list of items in the change log entry.
* @return the list of items
*/
public List<ChangeLogItem> getItems() {
return items;
}
}

View File

@ -0,0 +1,135 @@
/**
* 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;
import java.util.Map;
import net.sf.json.JSONObject;
/**
* Item in a {@link ChangeLogEntry}.
*/
public class ChangeLogItem extends Resource {
/**
* Field changed.
*/
private String field = null;
/**
* Type of field changed.
*/
private String fieldType = null;
/**
* What the field changed from.
*/
private String from = null;
/**
* What the field changed from in user-readable format.
*/
private String fromString = null;
/**
* What the field changed to.
*/
private String to = null;
/**
* What the field changed to in user-readable format.
*/
private String toString = null;
/**
* Creates a change log item from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
*/
protected ChangeLogItem(RestClient restclient, JSONObject json) {
super(restclient);
if (json != null)
deserialise(json);
}
/**
* Deserializes the json payload.
* @param json the json payload
*/
private void deserialise(JSONObject json) {
Map map = json;
field = Field.getString(map.get("field"));
fieldType = Field.getString(map.get("fieldtype"));
from = Field.getString(map.get("from"));
fromString = Field.getString(map.get("fromString"));
to = Field.getString(map.get("to"));
toString = Field.getString(map.get("toString"));
}
/**
* Obtains the field changed.
* @return the field changed
*/
public String getField() {
return field;
}
/**
* Obtains the type of field changed.
* @return the type of field
*/
public String getFieldType() {
return fieldType;
}
/**
* Obtains the value the field was changed from.
* @return the value
*/
public String getFrom() {
return from;
}
/**
* Obtains the value the field was changed from.
* @return the value in user-readable format
*/
public String getFromString() {
return fromString;
}
/**
* Obtains the value the field was changed to.
* @return the value
*/
public String getTo() {
return to;
}
/**
* Obtains the value the field was changed to.
* @return the value in user-readable format
*/
public String getToString() {
return toString;
}
}

View File

@ -78,7 +78,7 @@ public class Comment extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issue/" + issue + "/comment/" + id);
result = restclient.get(getBaseUri() + "issue/" + issue + "/comment/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve comment " + id + " on issue " + issue, ex);
}

View File

@ -19,8 +19,10 @@
package net.rcarz.jiraclient;
import java.util.HashMap;
import java.util.Map;
import net.rcarz.jiraclient.Issue.FluentCreate;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
@ -28,8 +30,108 @@ import net.sf.json.JSONObject;
* Represents an issue component.
*/
public class Component extends Resource {
/**
* Used to chain fields to a create action.
*/
public static final class FluentCreate {
/**
* The Jira rest client.
*/
RestClient restclient = null;
/**
* The JSON request that will be built incrementally as fluent methods
* are invoked.
*/
JSONObject req = new JSONObject();
/**
* Creates a new fluent.
* @param restclient the REST client
* @param project the project key
*/
private FluentCreate(RestClient restclient, String project) {
this.restclient = restclient;
req.put("project", project);
}
/**
* Sets the name of the component.
* @param name the name
* @return <code>this</code>
*/
public FluentCreate name(String name) {
req.put("name", name);
return this;
}
/**
* Sets the description of the component.
* @param description the description
* @return <code>this</code>
*/
public FluentCreate description(String description) {
req.put("description", description);
return this;
}
/**
* Sets the lead user name.
* @param leadUserName the lead user name
* @return <code>this</code>
*/
public FluentCreate leadUserName(String leadUserName) {
req.put("leadUserName", leadUserName);
return this;
}
/**
* Sets the assignee type.
* @param assigneeType the assignee type
* @return <code>this</code>
*/
public FluentCreate assigneeType(String assigneeType) {
req.put("assigneeType", assigneeType);
return this;
}
/**
* Sets whether the assignee type is valid.
* @param assigneeTypeValid is the assignee type valid?
* @return <code>this</code>
*/
public FluentCreate assigneeTypeValue(boolean assigneeTypeValid) {
req.put("isAssigneeTypeValid", assigneeTypeValid);
return this;
}
/**
* Executes the create action.
* @return the created component
*
* @throws JiraException when the create fails
*/
public Component execute() throws JiraException {
JSON result = null;
try {
result = restclient.post(getRestUri(null), req);
} catch (Exception ex) {
throw new JiraException("Failed to create issue", ex);
}
if (!(result instanceof JSONObject) || !((JSONObject) result).containsKey("id")
|| !(((JSONObject) result).get("id") instanceof String)) {
throw new JiraException("Unexpected result on create component");
}
return new Component(restclient, (JSONObject) result);
}
}
private String name = null;
private String description = null;
private boolean isAssigneeTypeValid = false;
/**
@ -51,6 +153,7 @@ public class Component extends Resource {
self = Field.getString(map.get("self"));
id = Field.getString(map.get("id"));
name = Field.getString(map.get("name"));
description = Field.getString(map.get("description"));
isAssigneeTypeValid = Field.getBoolean(map.get("isAssigneeTypeValid"));
}
@ -70,7 +173,7 @@ public class Component extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "component/" + id);
result = restclient.get(getRestUri(id));
} catch (Exception ex) {
throw new JiraException("Failed to retrieve component " + id, ex);
}
@ -90,8 +193,42 @@ public class Component extends Resource {
return name;
}
public String getDescription() {
return description;
}
public boolean isAssigneeTypeValid() {
return isAssigneeTypeValid;
}
private static String getRestUri(String id) {
return getBaseUri() + "component/" + (id != null ? id : "");
}
/**
* Creates a new JIRA component.
*
* @param restclient REST client instance
* @param project Key of the project to create the component in
*
* @return a fluent create instance
*/
public static FluentCreate create(RestClient restclient, String project) {
FluentCreate fc = new FluentCreate(restclient, project);
return fc;
}
/**
* Deletes a component from a project.
*
* @throws JiraException failed to delete the component
*/
public void delete() throws JiraException {
try {
restclient.delete(getRestUri(id));
} catch (Exception ex) {
throw new JiraException("Failed to delete component " + id, ex);
}
}
}

View File

@ -68,7 +68,7 @@ public class CustomFieldOption extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "customFieldOption/" + id);
result = restclient.get(getBaseUri() + "customFieldOption/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve custom field option " + id, ex);
}

View File

@ -21,6 +21,7 @@ package net.rcarz.jiraclient;
import java.lang.Iterable;
import java.lang.UnsupportedOperationException;
import java.sql.Timestamp;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -31,6 +32,7 @@ import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONNull;
/**
* Utility functions for translating between JSON and fields.
@ -73,11 +75,16 @@ public final class Field {
* Allowed value types.
*/
public enum ValueType {
KEY("key"), NAME("name"), ID_NUMBER("id");
private String value;
KEY("key"), NAME("name"), ID_NUMBER("id"), VALUE("value");
private String typeName;
private ValueType(String value) {
this.value = value;
private ValueType(String typeName) {
this.typeName = typeName;
}
@Override
public String toString() {
return typeName;
}
};
@ -96,7 +103,7 @@ public final class Field {
*/
public ValueTuple(String type, Object value) {
this.type = type;
this.value = value;
this.value = (value != null ? value : JSONNull.getInstance());
}
/**
@ -106,12 +113,15 @@ public final class Field {
* @param value
*/
public ValueTuple(ValueType type, Object value) {
this(type.value, 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";
public static final String COMPONENTS = "components";
public static final String DESCRIPTION = "description";
@ -120,6 +130,7 @@ public final class Field {
public static final String ISSUE_LINKS = "issuelinks";
public static final String ISSUE_TYPE = "issuetype";
public static final String LABELS = "labels";
public static final String PARENT = "parent";
public static final String PRIORITY = "priority";
public static final String PROJECT = "project";
public static final String REPORTER = "reporter";
@ -133,8 +144,13 @@ public final class Field {
public static final String VOTES = "votes";
public static final String WATCHES = "watches";
public static final String WORKLOG = "worklog";
public static final String TIME_ESTIMATE = "timeestimate";
public static final String TIME_SPENT = "timespent";
public static final String CREATED_DATE = "created";
public static final String UPDATED_DATE = "updated";
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() { }
@ -187,6 +203,23 @@ public final class Field {
return results;
}
/**
* Gets a list of remote links from the given object.
*
* @param c a JSONObject instance
* @param restclient REST client instance
*
* @return a list of remote links found in c
*/
public static List<RemoteLink> getRemoteLinks(Object c, RestClient restclient) {
List<RemoteLink> results = new ArrayList<RemoteLink>();
if (c instanceof JSONArray)
results = getResourceArray(RemoteLink.class, c, restclient);
return results;
}
/**
* Gets a date from the given object.
@ -206,6 +239,24 @@ public final class Field {
return result;
}
/**
* Gets a date with a time from the given object.
*
* @param d a string representation of a date
*
* @return a Date instance or null if d isn't a string
*/
public static Date getDateTime(Object d) {
Date result = null;
if (d instanceof String) {
SimpleDateFormat df = new SimpleDateFormat(DATETIME_FORMAT);
result = df.parse((String)d, new ParsePosition(0));
}
return result;
}
/**
* Gets an floating-point number from the given object.
*
@ -281,6 +332,12 @@ public final class Field {
if (r instanceof JSONObject && !((JSONObject)r).isNullObject()) {
if (type == Attachment.class)
result = (T)new Attachment(restclient, (JSONObject)r);
else if (type == ChangeLog.class)
result = (T)new ChangeLog(restclient, (JSONObject)r);
else if (type == ChangeLogEntry.class)
result = (T)new ChangeLogEntry(restclient, (JSONObject)r);
else if (type == ChangeLogItem.class)
result = (T)new ChangeLogItem(restclient, (JSONObject)r);
else if (type == Comment.class)
result = (T)new Comment(restclient, (JSONObject)r);
else if (type == Component.class)
@ -299,6 +356,8 @@ public final class Field {
result = (T)new Priority(restclient, (JSONObject)r);
else if (type == Project.class)
result = (T)new Project(restclient, (JSONObject)r);
else if (type == RemoteLink.class)
result = (T)new RemoteLink(restclient, (JSONObject)r);
else if (type == Resolution.class)
result = (T)new Resolution(restclient, (JSONObject)r);
else if (type == Status.class)
@ -455,29 +514,46 @@ public final class Field {
* @return a JSON-encoded array of items
*/
public static JSONArray toArray(Iterable iter, String type) throws JiraException {
JSONArray result = new JSONArray();
JSONArray results = new JSONArray();
if (type == null)
throw new JiraException("Array field metadata is missing item type");
for (Object val : iter) {
Operation oper = null;
Object realValue = null;
Object realResult = null;
if (val instanceof Operation) {
oper = (Operation)val;
realValue = oper.value;
} else
realValue = val;
if (type.equals("component") || type.equals("group") ||
type.equals("user") || type.equals("version")) {
JSONObject json = new JSONObject();
JSONObject itemMap = new JSONObject();
if (val instanceof ValueTuple) {
ValueTuple tuple = (ValueTuple)val;
json.put(tuple.type, tuple.value.toString());
if (realValue instanceof ValueTuple) {
ValueTuple tuple = (ValueTuple)realValue;
itemMap.put(tuple.type, tuple.value.toString());
} else
json.put(ValueType.NAME.value, val.toString());
itemMap.put(ValueType.NAME.toString(), realValue.toString());
result.add(json.toString());
realResult = itemMap;
} else if (type.equals("string"))
result.add(val.toString());
realResult = realValue.toString();
if (oper != null) {
JSONObject operMap = new JSONObject();
operMap.put(oper.name, realResult);
results.add(operMap);
} else
results.add(realResult);
}
return result;
return results;
}
/**
@ -495,71 +571,85 @@ public final class Field {
public static Object toJson(String name, Object value, JSONObject editmeta)
throws JiraException, UnsupportedOperationException {
if (value == null)
return null;
Meta m = getFieldMetadata(name, editmeta);
if (m.type == null)
throw new JiraException("Field metadata is missing a type");
if (m.type.equals("array")) {
if (!(value instanceof Iterable))
if (value == null)
value = new ArrayList();
else if (!(value instanceof Iterable))
throw new JiraException("Field expects an Iterable value");
boolean isOper = false;
for (Object v : (Iterable)value) {
isOper = v instanceof Operation;
break;
}
if (isOper) {
List results = new ArrayList();
for (Object v : (Iterable)value) {
Operation oper = (Operation)v;
JSONObject json = new JSONObject();
json.put(oper.name, oper.value.toString());
results.add(json.toString());
}
return toArray(results, m.items);
} else
return toArray((Iterable)value, m.items);
return toArray((Iterable)value, m.items);
} else if (m.type.equals("date")) {
Date d = toDate(value);
if (value == null)
return JSONNull.getInstance();
Date d = toDate(value);
if (d == null)
throw new JiraException("Field expects a date value or format is invalid");
SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
return df.format(d);
} else if (m.type.equals("datetime")) {
if (value == null)
return JSONNull.getInstance();
else if (!(value instanceof Timestamp))
throw new JiraException("Field expects a Timestamp value");
SimpleDateFormat df = new SimpleDateFormat(DATETIME_FORMAT);
return df.format(value);
} else if (m.type.equals("issuetype") || m.type.equals("priority") ||
m.type.equals("user") || m.type.equals("resolution")) {
JSONObject json = new JSONObject();
if (value instanceof ValueTuple) {
if (value == null)
return JSONNull.getInstance();
else if (value instanceof ValueTuple) {
ValueTuple tuple = (ValueTuple)value;
json.put(tuple.type, tuple.value.toString());
} else
json.put(ValueType.NAME.value, value.toString());
json.put(ValueType.NAME.toString(), value.toString());
return json.toString();
} else if (m.type.equals("project") || m.type.equals("issuelink")) {
JSONObject json = new JSONObject();
if (value instanceof ValueTuple) {
if (value == null)
return JSONNull.getInstance();
else if (value instanceof ValueTuple) {
ValueTuple tuple = (ValueTuple)value;
json.put(tuple.type, tuple.value.toString());
} else
json.put(ValueType.KEY.value, value.toString());
json.put(ValueType.KEY.toString(), value.toString());
return json.toString();
} else if (m.type.equals("string")) {
if (value instanceof Map)
return toJsonMap((Map)value);
} else if (m.type.equals("string") || (m.type.equals("securitylevel"))) {
if (value == null)
return "";
else if (value instanceof List)
return toJsonMap((List)value);
else if (value instanceof ValueTuple) {
JSONObject json = new JSONObject();
ValueTuple tuple = (ValueTuple)value;
json.put(tuple.type, tuple.value.toString());
return json.toString();
}
return value.toString();
} else if (m.type.equals("timetracking")) {
if (value == null)
return JSONNull.getInstance();
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
instanceof java.lang.Float) && !(value instanceof java.lang.Long) )
{
throw new JiraException("Field expects a Numeric value");
}
return value;
}
throw new UnsupportedOperationException(m.type + " is not a supported field type");
@ -568,15 +658,20 @@ public final class Field {
/**
* Converts the given map to a JSON object.
*
* @param map Map to be converted
* @param list List of values to be converted
*
* @return a JSON-encoded map
*/
public static Object toJsonMap(Map map) {
public static Object toJsonMap(List list) {
JSONObject json = new JSONObject();
for (Object k : map.keySet())
json.put(k, map.get(k));
for (Object item : list) {
if (item instanceof ValueTuple) {
ValueTuple vt = (ValueTuple)item;
json.put(vt.type, vt.value.toString());
} else
json.put(ValueType.VALUE.toString(), item.toString());
}
return json.toString();
}
@ -595,7 +690,7 @@ public final class Field {
/**
* Create a value tuple with value type of name.
*
* @param key The name value
* @param name The name value
*
* @return a value tuple
*/
@ -606,7 +701,7 @@ public final class Field {
/**
* Create a value tuple with value type of ID number.
*
* @param key The ID number value
* @param id The ID number value
*
* @return a value tuple
*/

View File

@ -23,6 +23,7 @@ import org.apache.http.HttpRequest;
public interface ICredentials {
void initialize(RestClient client) throws JiraException;
/**
* Sets the Authorization header for the given request.
*
@ -36,5 +37,7 @@ public interface ICredentials {
* @return logon name as a string
*/
String getLogonName();
void logout(RestClient client) throws JiraException;
}

View File

@ -142,6 +142,40 @@ public class Issue extends Resource {
}
}
/**
* count issues with the given query.
*
* @param restclient REST client instance
*
* @param jql JQL statement
*
* @return the count
*
* @throws JiraException when the search fails
*/
public static int count(RestClient restclient, String jql) throws JiraException {
final String j = jql;
JSON result = null;
try {
Map<String, String> queryParams = new HashMap<String, String>() {
{
put("jql", j);
}
};
queryParams.put("maxResults", "1");
URI searchUri = restclient.buildURI(getBaseUri() + "search", queryParams);
result = restclient.get(searchUri);
} catch (Exception ex) {
throw new JiraException("Failed to search issues", ex);
}
if (!(result instanceof JSONObject)) {
throw new JiraException("JSON payload is malformed");
}
Map map = (Map) result;
return Field.getInteger(map.get("total"));
}
/**
* Used to chain fields to an update action.
*/
@ -356,6 +390,7 @@ public class Issue extends Resource {
/* system fields */
private User assignee = null;
private List<Attachment> attachments = null;
private ChangeLog changeLog = null;
private List<Comment> comments = null;
private List<Component> components = null;
private String description = null;
@ -364,6 +399,7 @@ public class Issue extends Resource {
private List<IssueLink> issueLinks = null;
private IssueType issueType = null;
private List<String> labels = null;
private Issue parent = null;
private Priority priority = null;
private Project project = null;
private User reporter = null;
@ -377,6 +413,10 @@ public class Issue extends Resource {
private Votes votes = null;
private Watches watches = null;
private List<WorkLog> workLogs = null;
private Integer timeEstimate = null;
private Integer timeSpent = null;
private Date createdDate = null;
private Date updatedDate = null;
/**
* Creates an issue from a JSON payload.
@ -402,6 +442,7 @@ public class Issue extends Resource {
assignee = Field.getResource(User.class, fields.get(Field.ASSIGNEE), restclient);
attachments = Field.getResourceArray(Attachment.class, fields.get(Field.ATTACHMENT), restclient);
changeLog = Field.getResource(ChangeLog.class, map.get(Field.CHANGE_LOG), restclient);
comments = Field.getComments(fields.get(Field.COMMENT), restclient);
components = Field.getResourceArray(Component.class, fields.get(Field.COMPONENTS), restclient);
description = Field.getString(fields.get(Field.DESCRIPTION));
@ -410,6 +451,7 @@ public class Issue extends Resource {
issueLinks = Field.getResourceArray(IssueLink.class, fields.get(Field.ISSUE_LINKS), restclient);
issueType = Field.getResource(IssueType.class, fields.get(Field.ISSUE_TYPE), restclient);
labels = Field.getStringArray(fields.get(Field.LABELS));
parent = Field.getResource(Issue.class, fields.get(Field.PARENT), restclient);
priority = Field.getResource(Priority.class, fields.get(Field.PRIORITY), restclient);
project = Field.getResource(Project.class, fields.get(Field.PROJECT), restclient);
reporter = Field.getResource(User.class, fields.get(Field.REPORTER), restclient);
@ -423,10 +465,14 @@ public class Issue extends Resource {
votes = Field.getResource(Votes.class, fields.get(Field.VOTES), restclient);
watches = Field.getResource(Watches.class, fields.get(Field.WATCHES), restclient);
workLogs = Field.getWorkLogs(fields.get(Field.WORKLOG), restclient);
timeEstimate = Field.getInteger(fields.get(Field.TIME_ESTIMATE));
timeSpent = Field.getInteger(fields.get(Field.TIME_SPENT));
createdDate = Field.getDateTime(fields.get(Field.CREATED_DATE));
updatedDate = Field.getDateTime(fields.get(Field.UPDATED_DATE));
}
private static String getRestUri(String key) {
return RESOURCE_URI + "issue/" + (key != null ? key : "");
return getBaseUri() + "issue/" + (key != null ? key : "");
}
public static JSONObject getCreateMetadata(
@ -438,7 +484,7 @@ public class Issue extends Resource {
try {
URI createuri = restclient.buildURI(
RESOURCE_URI + "issue/createmeta",
getBaseUri() + "issue/createmeta",
new HashMap<String, String>() {{
put("expand", "projects.issuetypes.fields");
put("projectKeys", pval);
@ -464,7 +510,8 @@ public class Issue extends Resource {
restclient);
if (projects.isEmpty() || projects.get(0).getIssueTypes().isEmpty())
throw new JiraException("Project or issue type missing from create metadata");
throw new JiraException("Project '"+ project + "' or issue type '" + issueType +
"' missing from create metadata. Do you have enough permissions?");
return projects.get(0).getIssueTypes().get(0).getFields();
}
@ -512,7 +559,7 @@ public class Issue extends Resource {
return (JSONArray)jo.get("transitions");
}
/**
* Adds an attachment to this issue.
*
@ -638,7 +685,7 @@ public class Issue extends Resource {
}
try {
restclient.post(RESOURCE_URI + "issueLink", req);
restclient.post(getBaseUri() + "issueLink", req);
} catch (Exception ex) {
throw new JiraException("Failed to link issue " + key + " with issue " + issue, ex);
}
@ -675,9 +722,8 @@ public class Issue extends Resource {
* @throws JiraException when the client fails to retrieve issue metadata
*/
public FluentCreate createSubtask() throws JiraException {
return Issue.create(restclient, getProject().getKey(), "Sub-task")
.field("parent", getKey());
.field(Field.PARENT, getKey());
}
private static JSONObject realGet(RestClient restclient, String key, Map<String, String> queryParams)
@ -686,7 +732,7 @@ public class Issue extends Resource {
JSON result = null;
try {
URI uri = restclient.buildURI(RESOURCE_URI + "issue/" + key, queryParams);
URI uri = restclient.buildURI(getBaseUri() + "issue/" + key, queryParams);
result = restclient.get(uri);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve issue " + key, ex);
@ -747,6 +793,43 @@ public class Issue extends Resource {
return new Issue(restclient, realGet(restclient, key, queryParams));
}
/**
* Retrieves the given issue record.
*
* @param restclient REST client instance
*
* @param key Issue key (PROJECT-123)
*
* @param includedFields Specifies which issue fields will be included in
* the result.
* <br>Some examples how this parameter works:
* <ul>
* <li>*all - include all fields</li>
* <li>*navigable - include just navigable fields</li>
* <li>summary,comment - include just the summary and comments</li>
* <li>*all,-comment - include all fields</li>
* </ul>
*
* @param expand fields to expand when obtaining the issue
*
* @return an issue instance
*
* @throws JiraException when the retrieval fails
*/
public static Issue get(RestClient restclient, String key, final String includedFields,
final String expand) throws JiraException {
Map<String, String> queryParams = new HashMap<String, String>() {
{
put("fields", includedFields);
if (expand != null) {
put("expand", expand);
}
}
};
return new Issue(restclient, realGet(restclient, key, queryParams));
}
/**
* Search for issues with the given query.
*
@ -788,6 +871,44 @@ public class Issue extends Resource {
*/
public static SearchResult search(RestClient restclient, String jql, String includedFields, Integer maxResults)
throws JiraException {
return search(restclient, jql, includedFields, null, maxResults, null);
}
/**
* Search for issues with the given query and specify which fields to
* retrieve. If the total results is bigger than the maximum returned
* results, then further calls can be made using different values for
* the <code>startAt</code> field to obtain all the results.
*
* @param restclient REST client instance
*
* @param jql JQL statement
*
* @param includedFields Specifies which issue fields will be included in
* the result.
* <br>Some examples how this parameter works:
* <ul>
* <li>*all - include all fields</li>
* <li>*navigable - include just navigable fields</li>
* <li>summary,comment - include just the summary and comments</li>
* <li>*all,-comment - include all fields</li>
* </ul>
*
* @param maxResults if non-<code>null</code>, defines the maximum number of
* results that can be returned
*
* @param startAt if non-<code>null</code>, defines the first issue to
* return
*
* @param expandFields fields to expand when obtaining the issue
*
* @return a search result structure with results
*
* @throws JiraException when the search fails
*/
public static SearchResult search(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults, Integer startAt)
throws JiraException {
final String j = jql;
JSON result = null;
@ -799,12 +920,19 @@ public class Issue extends Resource {
}
};
if(maxResults != null){
queryParams.put("maxResults", String.valueOf(maxResults));
queryParams.put("maxResults", String.valueOf(maxResults));
}
if (includedFields != null) {
queryParams.put("fields", includedFields);
}
URI searchUri = restclient.buildURI(RESOURCE_URI + "search", queryParams);
if (expandFields != null) {
queryParams.put("expand", expandFields);
}
if (startAt != null) {
queryParams.put("startAt", String.valueOf(startAt));
}
URI searchUri = restclient.buildURI(getBaseUri() + "search", queryParams);
result = restclient.get(searchUri);
} catch (Exception ex) {
throw new JiraException("Failed to search issues", ex);
@ -973,6 +1101,10 @@ public class Issue extends Resource {
return getKey();
}
public ChangeLog getChangeLog() {
return changeLog;
}
public String getKey() {
return key;
}
@ -1017,6 +1149,10 @@ public class Issue extends Resource {
return labels;
}
public Issue getParent() {
return parent;
}
public Priority getPriority() {
return priority;
}
@ -1028,6 +1164,20 @@ public class Issue extends Resource {
public User getReporter() {
return reporter;
}
public List<RemoteLink> getRemoteLinks() throws JiraException {
JSONArray obj;
try {
URI uri = restclient.buildURI(getRestUri(key) + "/remotelink");
JSON json = restclient.get(uri);
obj = (JSONArray) json;
} catch (Exception ex) {
throw new JiraException("Failed to get remote links for issue "
+ key, ex);
}
return Field.getRemoteLinks(obj, restclient);
}
public Resolution getResolution() {
return resolution;
@ -1068,5 +1218,36 @@ public class Issue extends Resource {
public List<WorkLog> getWorkLogs() {
return workLogs;
}
public List<WorkLog> getAllWorkLogs() throws JiraException {
JSONObject obj;
try {
URI uri = restclient.buildURI(getRestUri(key) + "/worklog");
JSON json = restclient.get(uri);
obj = (JSONObject) json;
} catch (Exception ex) {
throw new JiraException("Failed to get worklog for issue "
+ key, ex);
}
return Field.getWorkLogs(obj, restclient);
}
public Integer getTimeSpent() {
return timeSpent;
}
public Integer getTimeEstimate() {
return timeEstimate;
}
public Date getCreatedDate() {
return createdDate;
}
public Date getUpdatedDate() {
return updatedDate;
}
}

View File

@ -0,0 +1,68 @@
package net.rcarz.jiraclient;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
public class IssueHistory extends Resource {
private static final long serialVersionUID = 1L;
private User user;
private ArrayList<IssueHistoryItem> changes;
private Date created;
/**
* Creates an issue history record from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
*/
protected IssueHistory(RestClient restclient, JSONObject json) {
super(restclient);
if (json != null) {
deserialise(restclient,json);
}
}
public IssueHistory(IssueHistory record, ArrayList<IssueHistoryItem> changes) {
super(record.restclient);
user = record.user;
id = record.id;
self = record.self;
created = record.created;
this.changes = changes;
}
private void deserialise(RestClient restclient, JSONObject json) {
Map map = json;
self = Field.getString(map.get("self"));
id = Field.getString(map.get("id"));
user = new User(restclient,(JSONObject)map.get("author"));
created = Field.getDateTime(map.get("created"));
JSONArray items = JSONArray.fromObject(map.get("items"));
changes = new ArrayList<IssueHistoryItem>(items.size());
for (int i = 0; i < items.size(); i++) {
JSONObject p = items.getJSONObject(i);
changes.add(new IssueHistoryItem(restclient, p));
}
}
public User getUser() {
return user;
}
public ArrayList<IssueHistoryItem> getChanges() {
return changes;
}
public Date getCreated() {
return created;
}
}

View File

@ -0,0 +1,57 @@
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSONObject;
public class IssueHistoryItem extends Resource {
private String field;
private String from;
private String to;
private String fromStr;
private String toStr;
public IssueHistoryItem(RestClient restclient) {
super(restclient);
}
public IssueHistoryItem(RestClient restclient, JSONObject json) {
this(restclient);
if (json != null) {
deserialise(restclient,json);
}
}
private void deserialise(RestClient restclient, JSONObject json) {
Map map = json;
self = Field.getString(map.get("self"));
id = Field.getString(map.get("id"));
field = Field.getString(map.get("field"));
from = Field.getString(map.get("from"));
to = Field.getString(map.get("to"));
fromStr = Field.getString(map.get("fromString"));
toStr = Field.getString(map.get("toString"));
}
public String getField() {
return field;
}
public String getFrom() {
return from;
}
public String getTo() {
return to;
}
public String getFromStr() {
return fromStr;
}
public String getToStr() {
return toStr;
}
}

View File

@ -64,7 +64,7 @@ public class IssueLink extends Resource {
public void delete() throws JiraException {
try {
restclient.delete(RESOURCE_URI + "issueLink/" + id);
restclient.delete(getBaseUri() + "issueLink/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to delete issue link " + id, ex);
}
@ -86,7 +86,7 @@ public class IssueLink extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issueLink/" + id);
result = restclient.get(getBaseUri() + "issueLink/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve issue link " + id, ex);
}

View File

@ -78,7 +78,7 @@ public class IssueType extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issuetype/" + id);
result = restclient.get(getBaseUri() + "issuetype/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve issue type " + id, ex);
}

View File

@ -19,9 +19,13 @@
package net.rcarz.jiraclient;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
@ -113,6 +117,45 @@ public class JiraClient {
return Issue.get(restclient, key, includedFields);
}
/**
* Retreives the issue with the given key.
*
* @param key Issue key (PROJECT-123)
*
* @param includedFields Specifies which issue fields will be included in
* the result.
* <br>Some examples how this parameter works:
* <ul>
* <li>*all - include all fields</li>
* <li>*navigable - include just navigable fields</li>
* <li>summary,comment - include just the summary and comments</li>
* <li>*all,-comment - include all fields</li>
* </ul>
*
* @param expand issue fields to expand when getting issue data
*
* @return an issue instance
*
* @throws JiraException when something goes wrong
*/
public Issue getIssue(String key, String includedFields,
String expand) throws JiraException {
return Issue.get(restclient, key, includedFields, expand);
}
/**
* count issues with the given query.
*
* @param jql JQL statement
*
* @return the count
*
* @throws JiraException when the search fails
*/
public int countIssues(String jql) throws JiraException {
return Issue.count(restclient, jql);
}
/**
* Search for issues with the given query.
*
@ -128,7 +171,7 @@ public class JiraClient {
return Issue.search(restclient, jql, null, null);
}
/**
* Search for issues with the given query and max results.
*
@ -172,6 +215,34 @@ public class JiraClient {
return Issue.search(restclient, jql, includedFields, null);
}
/**
* Search for issues with the given query and specify which fields to
* retrieve and expand.
*
* @param jql JQL statement
*
* @param includedFields Specifies which issue fields will be included in
* the result.
* <br>Some examples how this parameter works:
* <ul>
* <li>*all - include all fields</li>
* <li>*navigable - include just navigable fields</li>
* <li>summary,comment - include just the summary and comments</li>
* <li>*all,-comment - include all fields</li>
* </ul>
*
* @param expandFields Specifies with issue fields should be expanded
*
* @return a search result structure with results
*
* @throws JiraException when the search fails
*/
public Issue.SearchResult searchIssues(String jql, String includedFields, String expandFields)
throws JiraException {
return Issue.search(restclient, jql, includedFields, expandFields, null, null);
}
/**
* Search for issues with the given query and specify which fields to
@ -201,6 +272,78 @@ public class JiraClient {
return Issue.search(restclient, jql, includedFields, maxResults);
}
/**
* Search for issues with the given query and specify which fields to
* retrieve. If the total results is bigger than the maximum returned
* results, then further calls can be made using different values for
* the <code>startAt</code> field to obtain all the results.
*
* @param jql JQL statement
*
* @param includedFields Specifies which issue fields will be included in
* the result.
* <br>Some examples how this parameter works:
* <ul>
* <li>*all - include all fields</li>
* <li>*navigable - include just navigable fields</li>
* <li>summary,comment - include just the summary and comments</li>
* <li>*all,-comment - include all fields</li>
* </ul>
*
* @param maxResults if non-<code>null</code>, defines the maximum number of
* results that can be returned
*
* @param startAt if non-<code>null</code>, defines the first issue to
* return
*
* @return a search result structure with results
*
* @throws JiraException when the search fails
*/
public Issue.SearchResult searchIssues(String jql, String includedFields,
Integer maxResults, Integer startAt) throws JiraException {
return Issue.search(restclient, jql, includedFields, null, maxResults,
startAt);
}
/**
* Search for issues with the given query and specify which fields to
* retrieve. If the total results is bigger than the maximum returned
* results, then further calls can be made using different values for
* the <code>startAt</code> field to obtain all the results.
*
* @param jql JQL statement
*
* @param includedFields Specifies which issue fields will be included in
* the result.
* <br>Some examples how this parameter works:
* <ul>
* <li>*all - include all fields</li>
* <li>*navigable - include just navigable fields</li>
* <li>summary,comment - include just the summary and comments</li>
* <li>*all,-comment - include all fields</li>
* </ul>
*
* @param expandFields Specifies with issue fields should be expanded
*
* @param maxResults if non-<code>null</code>, defines the maximum number of
* results that can be returned
*
* @param startAt if non-<code>null</code>, defines the first issue to
* return
*
* @return a search result structure with results
*
* @throws JiraException when the search fails
*/
public Issue.SearchResult searchIssues(String jql, String includedFields, String expandFields,
Integer maxResults, Integer startAt) throws JiraException {
return Issue.search(restclient, jql, includedFields, expandFields, maxResults,
startAt);
}
/**
*
* @return a list of all priorities available in the Jira installation
@ -208,7 +351,7 @@ public class JiraClient {
*/
public List<Priority> getPriorities() throws JiraException {
try {
URI uri = restclient.buildURI(Resource.RESOURCE_URI + "priority");
URI uri = restclient.buildURI(Resource.getBaseUri() + "priority");
JSON response = restclient.get(uri);
JSONArray prioritiesArray = JSONArray.fromObject(response);
@ -275,6 +418,156 @@ public class JiraClient {
return username;
}
/**
* Obtains the list of all projects in Jira.
* @return all projects; not all data is returned for each project; to get
* the extra data use {@link #getProject(String)}
* @throws JiraException failed to obtain the project list.
*/
public List<Project> getProjects() throws JiraException {
try {
URI uri = restclient.buildURI(Resource.getBaseUri() + "project");
JSON response = restclient.get(uri);
JSONArray projectsArray = JSONArray.fromObject(response);
List<Project> projects = new ArrayList<Project>(projectsArray.size());
for (int i = 0; i < projectsArray.size(); i++) {
JSONObject p = projectsArray.getJSONObject(i);
projects.add(new Project(restclient, p));
}
return projects;
} catch (Exception ex) {
throw new JiraException(ex.getMessage(), ex);
}
}
/**
* Obtains information about a project, given its project key.
* @param key the project key
* @return the project
* @throws JiraException failed to obtain the project
*/
public Project getProject(String key) throws JiraException {
try {
URI uri = restclient.buildURI(Resource.getBaseUri() + "project/" + key);
JSON response = restclient.get(uri);
return new Project(restclient, (JSONObject) response);
} catch (Exception ex) {
throw new JiraException(ex.getMessage(), ex);
}
}
/**
* Obtains the list of all issue types in Jira.
* @return all issue types
* @throws JiraException failed to obtain the issue type list.
*/
public List<IssueType> getIssueTypes() throws JiraException {
try {
URI uri = restclient.buildURI(Resource.getBaseUri() + "issuetype");
JSON response = restclient.get(uri);
JSONArray issueTypeArray = JSONArray.fromObject(response);
List<IssueType> issueTypes = new ArrayList<IssueType>(issueTypeArray.size());
for (int i = 0; i < issueTypeArray.size(); i++) {
JSONObject it = issueTypeArray.getJSONObject(i);
issueTypes.add(new IssueType(restclient, it));
}
return issueTypes;
} catch (Exception ex) {
throw new JiraException(ex.getMessage(), ex);
}
}
/**
* Creates a new component in the given project.
*
* @param project Key of the project to create in
*
* @return a fluent create instance
*/
public Component.FluentCreate createComponent(String project) {
return Component.create(restclient, project);
}
/**
* Obtains a component given its ID.
*
* @param id the component ID
*
* @return the component
*
* @throws JiraException failed to obtain the component
*/
public Component getComponent(String id) throws JiraException {
return Component.get(restclient, id);
}
public ArrayList<IssueHistory> filterChangeLog(List<IssueHistory> histoy,String fields) {
ArrayList<IssueHistory> result = new ArrayList<IssueHistory>(histoy.size());
fields = "," + fields + ",";
for (IssueHistory record : histoy) {
ArrayList<IssueHistoryItem> list = new ArrayList<IssueHistoryItem>(record.getChanges().size());
for (IssueHistoryItem item : record.getChanges()) {
if (fields.contains(item.getField())) {
list.add(item);
}
}
if (list.size() > 0) {
result.add(new IssueHistory(record,list));
}
}
return result;
}
public ArrayList<IssueHistory> getIssueChangeLog(Issue issue) throws JiraException {
try {
ArrayList<IssueHistory> changes = null;
JSON response = getNextPortion(issue, 0);
while (true) {
JSONObject object = JSONObject.fromObject(response);
Object opers = object.get("changelog");
object = JSONObject.fromObject(opers);
Integer totalObj = (Integer)object.get("total");
JSONArray histories = JSONArray.fromObject(object.get("histories"));
if (changes == null) {
changes = new ArrayList<IssueHistory>(totalObj);
}
for (int i = 0; i < histories.size(); i++) {
JSONObject p = histories.getJSONObject(i);
changes.add(new IssueHistory(restclient, p));
}
if (changes.size() >= totalObj) {
break;
} else {
response = getNextPortion(issue,changes.size());
}
}
return changes;
} catch (Exception ex) {
throw new JiraException(ex.getMessage(), ex);
}
}
private JSON getNextPortion(Issue issue, Integer startAt)
throws URISyntaxException, RestException, IOException {
Map<String, String> params = new HashMap<String, String>();
if (startAt != null) {
params.put("startAt", String.valueOf(startAt));
}
params.put("expand","changelog.fields");
URI uri = restclient.buildURI(Issue.getBaseUri() + "issue/" + issue.id, params);
return restclient.get(uri);
}
}

View File

@ -72,7 +72,7 @@ public class LinkType extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issueLinkType/" + id);
result = restclient.get(getBaseUri() + "issueLinkType/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve issue link type " + id, ex);
}

View File

@ -70,7 +70,7 @@ public class Priority extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "priority/" + id);
result = restclient.get(getBaseUri() + "priority/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve priority " + id, ex);
}

View File

@ -91,7 +91,7 @@ public class Project extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "project/" + key);
result = restclient.get(getBaseUri() + "project/" + key);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve project " + key, ex);
}
@ -115,7 +115,7 @@ public class Project extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "project");
result = restclient.get(getBaseUri() + "project");
} catch (Exception ex) {
throw new JiraException("Failed to retrieve projects", ex);
}

View File

@ -0,0 +1,43 @@
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSONObject;
public class RemoteLink extends Resource {
private String remoteUrl;
private String title;
public RemoteLink(RestClient restclient, JSONObject json) {
super(restclient);
if (json != null)
deserialise(json);
}
private void deserialise(JSONObject json) {
Map map = json;
self = Field.getString(map.get("self"));
id = Field.getString(map.get("id"));
Map object = (Map)map.get("object");
remoteUrl = Field.getString(object.get("url"));
title = Field.getString(object.get("title"));
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getRemoteUrl() {
return remoteUrl;
}
public void setRemoteUrl(String remoteUrl) {
this.remoteUrl = remoteUrl;
}
}

View File

@ -70,7 +70,7 @@ public class Resolution extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "resolution/" + id);
result = restclient.get(getBaseUri() + "resolution/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve resolution " + id, ex);
}

View File

@ -24,7 +24,8 @@ package net.rcarz.jiraclient;
*/
public abstract class Resource {
protected static final String RESOURCE_URI = "/rest/api/2/";
public static final String DEFAULT_API_REV = "latest";
public static String apirev = DEFAULT_API_REV;
protected RestClient restclient = null;
protected String id = null;
@ -39,6 +40,34 @@ public abstract class Resource {
this.restclient = restclient;
}
/**
* Gets the JIRA REST API revision number.
*/
public static String getApiRev() {
return apirev;
}
/**
* Sets the JIRA REST API revision number.
*/
public static void setApiRev(String apirev) {
Resource.apirev = apirev;
}
/**
* Resource base URI with API revision number.
*/
public static String getBaseUri() {
return String.format("/rest/api/%s/", apirev);
}
/**
* Resource base URI with API revision number.
*/
public static String getAuthUri() {
return String.format("/rest/auth/%s/", apirev);
}
/**
* Internal JIRA ID.
*/
@ -52,5 +81,12 @@ public abstract class Resource {
public String getUrl() {
return self;
}
/**
* Resource URL.
*/
public String getSelf() {
return self;
}
}

View File

@ -32,8 +32,11 @@ import net.sf.json.JSON;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
@ -125,7 +128,26 @@ public class RestClient {
StringBuilder result = new StringBuilder();
if (ent != null) {
BufferedReader br = new BufferedReader(new InputStreamReader(ent.getContent()));
String encoding = null;
if (ent.getContentEncoding() != null) {
encoding = ent.getContentEncoding().getValue();
}
if (encoding == null) {
Header contentTypeHeader = resp.getFirstHeader("Content-Type");
HeaderElement[] contentTypeElements = contentTypeHeader.getElements();
for (HeaderElement he : contentTypeElements) {
NameValuePair nvp = he.getParameterByName("charset");
if (nvp != null) {
encoding = nvp.getValue();
}
}
}
InputStreamReader isr = encoding != null ?
new InputStreamReader(ent.getContent(), encoding) :
new InputStreamReader(ent.getContent());
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null)
@ -221,6 +243,22 @@ public class RestClient {
return request(new HttpGet(uri));
}
/**
* Executes an HTTP GET with the given path.
*
* @param path Path to be appended to the URI supplied in the construtor
* @param params Map of key value pairs
*
* @return JSON-encoded result or null when there's no content returned
*
* @throws RestException when an HTTP-level error occurs
* @throws IOException when an error reading the response occurs
* @throws URISyntaxException when an error occurred appending the path to the URI
*/
public JSON get(String path, Map<String, String> params) throws RestException, IOException, URISyntaxException {
return get(buildURI(path, params));
}
/**
* Executes an HTTP GET with the given path.
*
@ -233,9 +271,10 @@ public class RestClient {
* @throws URISyntaxException when an error occurred appending the path to the URI
*/
public JSON get(String path) throws RestException, IOException, URISyntaxException {
return get(buildURI(path));
return get(path, null);
}
/**
* Executes an HTTP POST with the given URI and payload.
*

View File

@ -72,7 +72,7 @@ public class Status extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "status/" + id);
result = restclient.get(getBaseUri() + "status/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve status " + id, ex);
}

View File

@ -30,8 +30,10 @@ public class TimeTracking {
private String originalEstimate = null;
private String remainingEstimate = null;
private int originalEstimateSeconds = 0;
private int remainingEstimateSeconds = 0;
private String timeSpent = null;
private Integer originalEstimateSeconds = null;
private Integer remainingEstimateSeconds = null;
private Integer timeSpentSeconds = null;
/**
* Creates a time tracking structure from a JSON payload.
@ -39,12 +41,43 @@ public class TimeTracking {
* @param json JSON payload
*/
protected TimeTracking(JSONObject json) {
Map map = json;
Map<?, ?> map = json;
originalEstimate = Field.getString(map.get("originalEstimate"));
remainingEstimate = Field.getString(map.get("remainingEstimate"));
timeSpent = Field.getString(map.get("timeSpent"));
originalEstimateSeconds = Field.getInteger(map.get("originalEstimateSeconds"));
remainingEstimateSeconds = Field.getInteger(map.get("remainingEstimateSeconds"));
timeSpentSeconds = Field.getInteger(map.get("timeSpentSeconds"));
}
public TimeTracking() {
}
public TimeTracking(TimeTracking tt) {
this.originalEstimate = tt.originalEstimate;
this.remainingEstimate = tt.remainingEstimate;
this.originalEstimateSeconds = tt.originalEstimateSeconds;
this.remainingEstimateSeconds = tt.remainingEstimateSeconds;
this.timeSpent = tt.timeSpent;
this.timeSpentSeconds =tt.timeSpentSeconds;
}
protected JSONObject toJsonObject() {
JSONObject object = new JSONObject();
if (originalEstimate != null)
object.put("originalEstimate", originalEstimate);
if (remainingEstimate != null)
object.put("remainingEstimate", remainingEstimate);
if (originalEstimateSeconds >= 0)
object.put("originalEstimateSeconds", originalEstimateSeconds);
if (remainingEstimateSeconds >= 0)
object.put("remainingEstimateSeconds", remainingEstimateSeconds);
return object;
}
public String getOriginalEstimate() {
@ -55,6 +88,10 @@ public class TimeTracking {
return remainingEstimate;
}
public String getTimeSpent() {
return timeSpent;
}
public int getOriginalEstimateSeconds() {
return originalEstimateSeconds;
}
@ -62,5 +99,24 @@ public class TimeTracking {
public int getRemainingEstimateSeconds() {
return remainingEstimateSeconds;
}
}
public void setOriginalEstimate(String originalEstimate) {
this.originalEstimate = originalEstimate;
}
public void setRemainingEstimate(String remainingEstimate) {
this.remainingEstimate = remainingEstimate;
}
public void setOrignalEstimateSeconds(int originalEstimateSeconds) {
this.originalEstimateSeconds = originalEstimateSeconds;
}
public void setRemainingEstimateSeconds(int remainingEstimateSeconds) {
this.remainingEstimateSeconds = remainingEstimateSeconds;
}
public int getTimeSpentSeconds() {
return timeSpentSeconds;
}
}

View File

@ -0,0 +1,101 @@
/**
* 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;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import org.apache.http.HttpRequest;
/**
* Basic HTTP authentication credentials.
*/
public class TokenCredentials implements ICredentials {
private String username;
private String password;
private String token;
/**
* Creates new basic HTTP credentials.
*
* @param username
* @param password
*/
public TokenCredentials(String username, String password) {
this.username = username;
this.password = password;
}
public TokenCredentials(String jsessionId) {
token = jsessionId;
}
/**
* Sets the Authorization header for the given request.
*
* @param req HTTP request to authenticate
*/
public void authenticate(HttpRequest req) {
if (token != null) {
req.addHeader("Cookie","JSESSIONID="+token+";");
}
}
/**
* Gets the logon name representing these credentials.
*
* @return logon name as a string
*/
public String getLogonName() {
return username;
}
@Override
public void initialize(RestClient client) throws JiraException {
if (token==null) {
try {
JSONObject req = new JSONObject();
req.put("username", username);
req.put("password", password);
JSON json = client.post(Resource.getAuthUri() + "session", req);
System.out.println(json.toString());
} catch (Exception ex) {
throw new JiraException("Failed to login", ex);
}
}
}
@Override
public void logout(RestClient client) throws JiraException {
if (token != null) {
try {
client.delete(Resource.getAuthUri() + "session");
} catch (Exception e) {
throw new JiraException("Failed to logout", e);
}
}
}
public String getToken() {
return token;
}
}

View File

@ -1,7 +1,7 @@
/**
* 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
@ -19,11 +19,12 @@
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a JIRA user.
*/
@ -39,7 +40,7 @@ public class User extends Resource {
* Creates a user from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
* @param json JSON payload
*/
protected User(RestClient restclient, JSONObject json) {
super(restclient);
@ -48,6 +49,34 @@ public class User extends Resource {
deserialise(json);
}
/**
* Retrieves the given user record.
*
* @param restclient REST client instance
* @param username User logon name
* @return a user instance
* @throws JiraException when the retrieval fails
*/
public static User get(RestClient restclient, String username)
throws JiraException {
JSON result = null;
Map<String, String> params = new HashMap<String, String>();
params.put("username", username);
try {
result = restclient.get(getBaseUri() + "user", params);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve user " + username, ex);
}
if (!(result instanceof JSONObject))
throw new JiraException("JSON payload is malformed");
return new User(restclient, (JSONObject) result);
}
private void deserialise(JSONObject json) {
Map map = json;
@ -56,35 +85,22 @@ public class User extends Resource {
active = Field.getBoolean(map.get("active"));
avatarUrls = Field.getMap(String.class, String.class, map.get("avatarUrls"));
displayName = Field.getString(map.get("displayName"));
email = Field.getString(map.get("email"));
email = getEmailFromMap(map);
name = Field.getString(map.get("name"));
}
/**
* Retrieves the given user record.
* API changes email address might be represented as either "email" or "emailAddress"
*
* @param restclient REST client instance
* @param username User logon name
*
* @return a user instance
*
* @throws JiraException when the retrieval fails
* @param map JSON object for the User
* @return String email address of the JIRA user.
*/
public static User get(RestClient restclient, String username)
throws JiraException {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "user?username=" + username);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve user " + username, ex);
private String getEmailFromMap(Map map) {
if (map.containsKey("email")) {
return Field.getString(map.get("email"));
} else {
return Field.getString(map.get("emailAddress"));
}
if (!(result instanceof JSONObject))
throw new JiraException("JSON payload is malformed");
return new User(restclient, (JSONObject)result);
}
@Override

View File

@ -1,7 +1,7 @@
/**
* 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
@ -19,11 +19,11 @@
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import java.util.Map;
/**
* Represents a product version.
*/
@ -32,12 +32,14 @@ public class Version extends Resource {
private String name = null;
private boolean archived = false;
private boolean released = false;
private String releaseDate;
private String description = null;
/**
* Creates a version from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
* @param json JSON payload
*/
protected Version(RestClient restclient, JSONObject json) {
super(restclient);
@ -45,6 +47,77 @@ public class Version extends Resource {
if (json != null)
deserialise(json);
}
/**
* Merges the given version with current version
*
* @param version
* The version to merge
*/
public void mergeWith(Version version) {
JSONObject req = new JSONObject();
req.put("description", version.getDescription());
req.put("name", version.getName());
req.put("archived", version.isArchived());
req.put("released", version.isReleased());
req.put("releaseDate", version.getReleaseDate());
try {
restclient.put(Resource.getBaseUri() + "version/" + id, req);
} catch (Exception ex) {
throw new RuntimeException("Failed to merge", ex);
}
}
/**
* Copies the version to the given project
*
* @param project
* The project the version will be copied to
*/
public void copyTo(Project project) {
JSONObject req = new JSONObject();
req.put("description", getDescription());
req.put("name", getName());
req.put("archived", isArchived());
req.put("released", isReleased());
req.put("releaseDate", getReleaseDate());
req.put("project", project.getKey());
req.put("projectId", project.getId());
try {
restclient.post(Resource.getBaseUri() + "version/", req);
} catch (Exception ex) {
throw new RuntimeException("Failed to copy to project '" + project.getKey() + "'", ex);
}
}
/**
* Retrieves the given version record.
*
* @param restclient REST client instance
* @param id Internal JIRA ID of the version
* @return a version instance
* @throws JiraException when the retrieval fails
*/
public static Version get(RestClient restclient, String id)
throws JiraException {
JSON result = null;
try {
result = restclient.get(getBaseUri() + "version/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve version " + id, ex);
}
if (!(result instanceof JSONObject))
throw new JiraException("JSON payload is malformed");
return new Version(restclient, (JSONObject) result);
}
private void deserialise(JSONObject json) {
Map map = json;
@ -54,33 +127,8 @@ public class Version extends Resource {
name = Field.getString(map.get("name"));
archived = Field.getBoolean(map.get("archived"));
released = Field.getBoolean(map.get("released"));
}
/**
* Retrieves the given version record.
*
* @param restclient REST client instance
* @param id Internal JIRA ID of the version
*
* @return a version instance
*
* @throws JiraException when the retrieval fails
*/
public static Version get(RestClient restclient, String id)
throws JiraException {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "version/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve version " + id, ex);
}
if (!(result instanceof JSONObject))
throw new JiraException("JSON payload is malformed");
return new Version(restclient, (JSONObject)result);
releaseDate = Field.getString(map.get("releaseDate"));
description = Field.getString(map.get("description"));
}
@Override
@ -99,5 +147,14 @@ public class Version extends Resource {
public boolean isReleased() {
return released;
}
public String getReleaseDate() {
return releaseDate;
}
public String getDescription() {
return description;
}
}

View File

@ -19,11 +19,11 @@
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import java.util.Map;
/**
* Represents issue votes.
*/
@ -71,7 +71,7 @@ public class Votes extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issue/" + issue + "/votes");
result = restclient.get(getBaseUri() + "issue/" + issue + "/votes");
} catch (Exception ex) {
throw new JiraException("Failed to retrieve votes for issue " + issue, ex);
}

View File

@ -19,11 +19,11 @@
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import java.util.Map;
/**
* Represents issue watches.
*/
@ -71,7 +71,7 @@ public class Watches extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issue/" + issue + "/watches");
result = restclient.get(getBaseUri() + "issue/" + issue + "/watches");
} catch (Exception ex) {
throw new JiraException("Failed to retrieve watches for issue " + issue, ex);
}

View File

@ -84,7 +84,7 @@ public class WorkLog extends Resource {
JSON result = null;
try {
result = restclient.get(RESOURCE_URI + "issue/" + issue + "/worklog/" + id);
result = restclient.get(getBaseUri() + "issue/" + issue + "/worklog/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve work log " + id + " on issue " + issue, ex);
}
@ -119,5 +119,9 @@ public class WorkLog extends Resource {
public Date getUpdatedDate() {
return updated;
}
public int getTimeSpentSeconds() {
return timeSpentSeconds;
}
}

View File

@ -40,8 +40,9 @@ public class Backlog {
private RestClient restclient = null;
private List<SprintIssue> issues = null;
private List<SprintIssue> backlogIssues = null;
private int rankCustomFieldId = 0;
private List<Sprint> openSprints = null;
private List<Sprint> sprints = null;
private List<RapidViewProject> projects = null;
private List<Marker> markers = null;
private List<Epic> epics = null;
@ -72,9 +73,9 @@ public class Backlog {
map.get("issues"),
restclient);
rankCustomFieldId = Field.getInteger(map.get("rankCustomFieldId"));
openSprints = GreenHopperField.getResourceArray(
sprints = GreenHopperField.getResourceArray(
Sprint.class,
map.get("openSprints"),
map.get("sprints"),
restclient);
projects = GreenHopperField.getResourceArray(
RapidViewProject.class,
@ -124,6 +125,25 @@ public class Backlog {
}
}
}
//determining which issues are actually in the backlog vs the sprints
//fill in the issues into the single sprints and the backlog issue list respectively
for(SprintIssue issue : issues){
boolean addedToSprint = false;
for(Sprint sprint : sprints){
if(sprint.getIssuesIds().contains(issue.getId())){
sprint.getIssues().add(issue);
addedToSprint = true;
}
}
if(!addedToSprint){
if(backlogIssues == null){
backlogIssues = new ArrayList<SprintIssue>();
}
backlogIssues.add(issue);
}
}
}
/**
@ -163,12 +183,16 @@ public class Backlog {
return issues;
}
public List<SprintIssue> getBacklogIssues() {
return backlogIssues;
}
public int getRankCustomFieldId() {
return rankCustomFieldId;
}
public List<Sprint> getOpenSprints() {
return openSprints;
public List<Sprint> getSprints() {
return sprints;
}
public List<RapidViewProject> getProjects() {

View File

@ -31,7 +31,7 @@ import net.sf.json.JSONObject;
public class EstimateStatistic {
private String statFieldId = null;
private int statFieldValue = 0;
private Double statFieldValue = 0.0;
private String statFieldText = null;
/**
@ -49,8 +49,8 @@ public class EstimateStatistic {
Map val = (Map)json.get("statFieldValue");
statFieldValue = Field.getInteger(map.get("value"));
statFieldText = Field.getString(map.get("text"));
statFieldValue = Field.getDouble(val.get("value"));
statFieldText = Field.getString(val.get("text"));
}
}
@ -58,7 +58,7 @@ public class EstimateStatistic {
return statFieldId;
}
public int getFieldValue() {
public Double getFieldValue() {
return statFieldValue;
}

View File

@ -37,6 +37,8 @@ public final class GreenHopperField {
public static final String DATE_TIME_FORMAT = "d/MMM/yy h:m a";
public static final String NO_DATE = "None";
private GreenHopperField() { }
/**
@ -47,9 +49,10 @@ public final class GreenHopperField {
* @return the date-time or null
*/
public static DateTime getDateTime(Object dt) {
return dt != null ?
DateTime.parse((String)dt, DateTimeFormat.forPattern(DATE_TIME_FORMAT)) :
null;
if(dt == null || ((String)dt).equals(NO_DATE)){
return null;
}
return DateTime.parse((String)dt, DateTimeFormat.forPattern(DATE_TIME_FORMAT));
}
/**

View File

@ -134,8 +134,7 @@ public class RapidView extends GreenHopperResource {
JSON result = null;
try {
System.out.println(getId());
result = restclient.get(RESOURCE_URI + "sprints/" + id);
result = restclient.get(RESOURCE_URI + "sprintquery/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve sprints", ex);
}

View File

@ -22,6 +22,8 @@ package net.rcarz.jiraclient.greenhopper;
import net.rcarz.jiraclient.Field;
import net.rcarz.jiraclient.RestClient;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONObject;
@ -38,6 +40,8 @@ public class Sprint extends GreenHopperResource {
private DateTime startDate = null;
private DateTime endDate = null;
private DateTime completeDate = null;
private List<Integer> issuesIds = null;
private List<SprintIssue> issues = null;
/**
* Creates a sprint from a JSON payload.
@ -61,6 +65,7 @@ public class Sprint extends GreenHopperResource {
startDate = GreenHopperField.getDateTime(map.get("startDate"));
endDate = GreenHopperField.getDateTime(map.get("endDate"));
completeDate = GreenHopperField.getDateTime(map.get("completeDate"));
issuesIds = GreenHopperField.getIntegerArray(map.get("issuesIds"));
}
@Override
@ -87,5 +92,16 @@ public class Sprint extends GreenHopperResource {
public DateTime getCompleteDate() {
return completeDate;
}
public List<SprintIssue> getIssues(){
if(issues == null){
issues = new ArrayList<SprintIssue>();
}
return issues;
}
public List<Integer> getIssuesIds() {
return issuesIds;
}
}

View File

@ -0,0 +1,139 @@
package net.rcarz.jiraclient;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.util.List;
import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Assert;
import org.junit.Test;
public class IssueTest {
/**
* If no exception thrown the test is passed.
*/
@Test
public void testCreateIssue() {
new Issue(null, Utils.getTestIssue());
}
@Test
public void testGetIssueStatus() {
String statusName = "To Do";
String statusID = "10004";
String description = "Issue is currently in progress.";
String iconURL = "https://brainbubble.atlassian.net/images/icons/statuses/open.png";
Issue issue = new Issue(null, Utils.getTestIssue());
assertNotNull(issue.getStatus());
assertEquals(description, issue.getStatus().getDescription());
assertEquals(iconURL, issue.getStatus().getIconUrl());
assertEquals(statusName, issue.getStatus().getName());
assertEquals(statusID, issue.getStatus().getId());
}
@Test
public void getReporter() {
Issue issue = new Issue(null, Utils.getTestIssue());
assertNotNull(issue.getReporter());
User reporter = issue.getReporter();
assertEquals(reporter.getDisplayName(), "Joseph McCarthy");
assertEquals(reporter.getName(), "joseph");
assertTrue(reporter.isActive());
assertEquals(reporter.getEmail(), "joseph.b.mccarthy2012@googlemail.com");
Map<String, String> avatars = reporter.getAvatarUrls();
assertNotNull(avatars);
assertEquals(avatars.size(), 4);
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=16", avatars.get("16x16"));
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=24", avatars.get("24x24"));
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=32", avatars.get("32x32"));
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=48", avatars.get("48x48"));
}
@Test
public void testGetIssueType(){
Issue issue = new Issue(null, Utils.getTestIssue());
IssueType issueType = issue.getIssueType();
assertNotNull(issueType);
assertFalse(issueType.isSubtask());
assertEquals(issueType.getName(), "Story");
assertEquals(issueType.getId(), "7");
assertEquals(issueType.getIconUrl(), "https://brainbubble.atlassian.net/images/icons/issuetypes/story.png");
assertEquals(issueType.getDescription(), "This is a test issue type.");
}
@Test
public void testGetVotes(){
Issue issue = new Issue(null, Utils.getTestIssue());
Votes votes = issue.getVotes();
assertNotNull(votes);
assertFalse(votes.hasVoted());
assertEquals(votes.getVotes(),0);
}
@Test
public void testGetWatchers(){
Issue issue = new Issue(null, Utils.getTestIssue());
Watches watches = issue.getWatches();
assertNotNull(watches);
assertFalse(watches.isWatching());
assertEquals(watches.getWatchCount(), 0);
assertEquals(watches.getSelf(), "https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/watchers");
}
@Test
public void testGetVersion(){
Issue issue = new Issue(null,Utils.getTestIssue());
List<Version> versions = issue.getFixVersions();
assertNotNull(versions);
assertEquals(versions.size(),1);
Version version = versions.get(0);
Assert.assertEquals(version.getId(), "10200");
Assert.assertEquals(version.getName(), "1.0");
assertFalse(version.isArchived());
assertFalse(version.isReleased());
Assert.assertEquals(version.getReleaseDate(), "2013-12-01");
Assert.assertEquals(version.getDescription(), "First Full Functional Build");
}
@Test
public void testPlainTimeTracking() {
Issue issue = new Issue(null,Utils.getTestIssue());
assertEquals(new Integer(144000), issue.getTimeEstimate());
assertEquals(new Integer(86400), issue.getTimeSpent());
}
@Test
public void testCreatedDate(){
Issue issue = new Issue(null,Utils.getTestIssue());
assertEquals(new DateTime(2013, 9, 29, 20, 16, 19, 854, DateTimeZone.forOffsetHours(1)).toDate(), issue.getCreatedDate());
}
@Test
public void testUpdatedDate(){
Issue issue = new Issue(null,Utils.getTestIssue());
assertEquals(new DateTime(2013, 10, 9, 22, 24, 55, 961, DateTimeZone.forOffsetHours(1)).toDate(), issue.getUpdatedDate());
}
}

View File

@ -0,0 +1,47 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
public class IssueTypeTest {
@Test
public void testIssueTypeInit() {
IssueType issueType = new IssueType(null, null);
}
@Test
public void testGetIssueType() {
IssueType issueType = new IssueType(null, getTestJSON());
assertFalse(issueType.isSubtask());
assertEquals(issueType.getName(), "Story");
assertEquals(issueType.getId(), "7");
assertEquals(issueType.getIconUrl(), "https://brainbubble.atlassian.net/images/icons/issuetypes/story.png");
assertEquals(issueType.getDescription(), "This is a test issue type.");
}
@Test
public void testIssueTypeToString(){
IssueType issueType = new IssueType(null, getTestJSON());
assertEquals(issueType.toString(),"Story");
}
private JSONObject getTestJSON() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("self", "https://brainbubble.atlassian.net/rest/api/2/issuetype/7");
jsonObject.put("id", "7");
jsonObject.put("description", "This is a test issue type.");
jsonObject.put("iconUrl", "https://brainbubble.atlassian.net/images/icons/issuetypes/story.png");
jsonObject.put("name", "Story");
jsonObject.put("subtask", false);
return jsonObject;
}
}

View File

@ -0,0 +1,45 @@
package net.rcarz.jiraclient;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class SearchTest {
@Test
public void testSimpleSearch() throws JiraException {
JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);
String key = "JRA-1";
Issue.SearchResult searchResult = jira.searchIssues("key = " + key);
assertNotNull(searchResult);
assertEquals("should return exactly 1 issue", 1, searchResult.issues.size());
assertEquals("with key " + key, key, searchResult.issues.get(0).getKey());
assertEquals("and resolution Fixed", "Fixed", searchResult.issues.get(0).getResolution().getName());
}
@Test
public void testExpandingChangeLogInSearch() throws JiraException {
JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);
String key = "JRA-2048";
Issue.SearchResult searchResult = jira.searchIssues("key = " + key, null, "changelog");
assertEquals("should return exactly 1 issue", 1, searchResult.issues.size());
ChangeLog changeLog = searchResult.issues.get(0).getChangeLog();
assertNotNull("returned issue should have a changeLog", changeLog);
boolean closedStatusFound = false;
for (ChangeLogEntry entry : changeLog.getEntries()) {
for (ChangeLogItem item : entry.getItems()) {
closedStatusFound |= "Closed".equals(item.getToString());
}
}
assertTrue("ChangeLog should contain Closed entry", closedStatusFound);
}
}

View File

@ -0,0 +1,44 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import static junit.framework.Assert.assertEquals;
public class StatusTest {
private String statusID = "10004";
private String description = "Issue is currently in progress.";
private String iconURL = "https://site/images/icons/statuses/open.png";
@Test
public void testJSONDeserializer() throws IOException, URISyntaxException {
Status status = new Status(new RestClient(null, new URI("/123/asd")), getTestJSON());
assertEquals(status.getDescription(), description);
assertEquals(status.getIconUrl(), iconURL);
assertEquals(status.getName(), "Open");
assertEquals(status.getId(), statusID);
}
private JSONObject getTestJSON() {
JSONObject json = new JSONObject();
json.put("description", description);
json.put("name", "Open");
json.put("iconUrl", iconURL);
json.put("id", statusID);
return json;
}
@Test
public void testStatusToString() throws URISyntaxException {
Status status = new Status(new RestClient(null, new URI("/123/asd")), getTestJSON());
assertEquals("Open",status.toString());
}
}

View File

@ -0,0 +1,24 @@
package net.rcarz.jiraclient;
import static junit.framework.Assert.assertEquals;
import org.junit.Test;
public class TimeTrackingTest {
private Issue issue = new Issue(null, Utils.getTestIssue());
private TimeTracking time = issue.getTimeTracking();
@Test
public void testAttributeMappings() {
assertEquals("1w", time.getOriginalEstimate());
assertEquals(144000, time.getOriginalEstimateSeconds());
assertEquals("2d", time.getRemainingEstimate());
assertEquals(57600, time.getRemainingEstimateSeconds());
assertEquals("3d", time.getTimeSpent());
assertEquals(86400, time.getTimeSpentSeconds());
}
}

View File

@ -0,0 +1,67 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
public class UserTest {
private java.lang.String username = "joseph";
private java.lang.String displayName = "Joseph McCarthy";
private java.lang.String email = "joseph.b.mccarthy2012@googlemail.com";
private java.lang.String userID = "10";
private boolean isActive = true;
private String self = "https://brainbubble.atlassian.net/rest/api/2/user?username=joseph";
@Test
public void testJSONDeserializer() throws IOException, URISyntaxException {
User user = new User(new RestClient(null, new URI("/123/asd")), getTestJSON());
assertEquals(user.getName(), username);
assertEquals(user.getDisplayName(), displayName);
assertEquals(user.getEmail(), email);
assertEquals(user.getId(), userID);
Map<String, String> avatars = user.getAvatarUrls();
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=16", avatars.get("16x16"));
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=24", avatars.get("24x24"));
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=32", avatars.get("32x32"));
assertEquals("https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=48", avatars.get("48x48"));
assertTrue(user.isActive());
}
private JSONObject getTestJSON() {
JSONObject json = new JSONObject();
json.put("name", username);
json.put("email", email);
json.put("active", isActive);
json.put("displayName", displayName);
json.put("self", self);
JSONObject images = new JSONObject();
images.put("16x16", "https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=16");
images.put("24x24", "https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=24");
images.put("32x32", "https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=32");
images.put("48x48", "https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=48");
json.put("avatarUrls", images);
json.put("id", "10");
return json;
}
@Test
public void testStatusToString() throws URISyntaxException {
User user = new User(new RestClient(null, new URI("/123/asd")), getTestJSON());
assertEquals(username, user.toString());
}
}

View File

@ -0,0 +1,204 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
public class Utils {
public static JSONObject getTestIssue() {
JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON("{\n" +
" \"expand\": \"renderedFields,names,schema,transitions,operations,editmeta,changelog\",\n" +
" \"id\": \"10742\",\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/latest/issue/10742\",\n" +
" \"key\": \"FILTA-43\",\n" +
" \"fields\": {\n" +
" \"progress\": {\n" +
" \"progress\": 0,\n" +
" \"total\": 0\n" +
" },\n" +
" \"summary\": \"Maintain Company Details\",\n" +
" \"timetracking\": {\n" +
" \"originalEstimate\": \"1w\",\n" +
" \"remainingEstimate\": \"2d\",\n" +
" \"timeSpent\": \"3d\",\n" +
" \"originalEstimateSeconds\": 144000,\n" +
" \"remainingEstimateSeconds\": 57600,\n" +
" \"timeSpentSeconds\": 86400\n" +
" },\n" +
" \"issuetype\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/issuetype/7\",\n" +
" \"id\": \"7\",\n" +
" \"description\": \"This is a test issue type.\",\n" +
" \"iconUrl\": \"https://brainbubble.atlassian.net/images/icons/issuetypes/story.png\",\n" +
" \"name\": \"Story\",\n" +
" \"subtask\": false\n" +
" },\n" +
" \"votes\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/votes\",\n" +
" \"votes\": 0,\n" +
" \"hasVoted\": false\n" +
" },\n" +
" \"resolution\": null,\n" +
" \"fixVersions\": [\n" +
" {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/version/10200\",\n" +
" \"id\": \"10200\",\n" +
" \"description\": \"First Full Functional Build\",\n" +
" \"name\": \"1.0\",\n" +
" \"archived\": false,\n" +
" \"released\": false,\n" +
" \"releaseDate\": \"2013-12-01\"\n" +
" }\n" +
" ],\n" +
" \"resolutiondate\": null,\n" +
" \"timespent\": 86400,\n" +
" \"reporter\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/user?username=joseph\",\n" +
" \"name\": \"joseph\",\n" +
" \"emailAddress\": \"joseph.b.mccarthy2012@googlemail.com\",\n" +
" \"avatarUrls\": {\n" +
" \"16x16\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=16\",\n" +
" \"24x24\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=24\",\n" +
" \"32x32\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=32\",\n" +
" \"48x48\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=48\"\n" +
" },\n" +
" \"displayName\": \"Joseph McCarthy\",\n" +
" \"active\": true\n" +
" },\n" +
" \"aggregatetimeoriginalestimate\": null,\n" +
" \"created\": \"2013-09-29T20:16:19.854+0100\",\n" +
" \"updated\": \"2013-10-09T22:24:55.961+0100\",\n" +
" \"description\": \"{panel:title=Description|borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1|bgColor=#FFFFCE}\\r\\nAs a company / admin\\r\\n\\r\\nI want to update the company details like contact details / name and so on\\r\\n\\r\\nSo that their details are up to date\\r\\n{panel}\\r\\n\\r\\n{panel:title=Acceptance Criteria|borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1|bgColor=#FFFFCE}\\r\\nCan I change the company name?\\r\\nCan I change our emails, addresses and phone number etc?\\r\\nCan I change my invoicing details?\\r\\nCan I change our application service agreement?\\r\\n{panel}\",\n" +
" \"priority\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/priority/3\",\n" +
" \"iconUrl\": \"https://brainbubble.atlassian.net/images/icons/priorities/major.png\",\n" +
" \"name\": \"Major\",\n" +
" \"id\": \"3\"\n" +
" },\n" +
" \"duedate\": null,\n" +
" \"customfield_10001\": null,\n" +
" \"customfield_10002\": null,\n" +
" \"customfield_10003\": null,\n" +
" \"issuelinks\": [\n" +
" \n" +
" ],\n" +
" \"customfield_10004\": null,\n" +
" \"watches\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/watchers\",\n" +
" \"watchCount\": 0,\n" +
" \"isWatching\": false\n" +
" },\n" +
" \"worklog\": {\n" +
" \"startAt\": 0,\n" +
" \"maxResults\": 20,\n" +
" \"total\": 0,\n" +
" \"worklogs\": [\n" +
" \n" +
" ]\n" +
" },\n" +
" \"customfield_10000\": null,\n" +
" \"subtasks\": [\n" +
" \n" +
" ],\n" +
" \"status\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/status/10004\",\n" +
" \"description\": \"Issue is currently in progress.\",\n" +
" \"iconUrl\": \"https://brainbubble.atlassian.net/images/icons/statuses/open.png\",\n" +
" \"name\": \"To Do\",\n" +
" \"id\": \"10004\"\n" +
" },\n" +
" \"customfield_10007\": null,\n" +
" \"customfield_10006\": \"90\",\n" +
" \"labels\": [\n" +
" \n" +
" ],\n" +
" \"customfield_10005\": null,\n" +
" \"workratio\": -1,\n" +
" \"assignee\": null,\n" +
" \"attachment\": [\n" +
" \n" +
" ],\n" +
" \"customfield_10200\": null,\n" +
" \"aggregatetimeestimate\": null,\n" +
" \"project\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/project/10501\",\n" +
" \"id\": \"10501\",\n" +
" \"key\": \"FILTA\",\n" +
" \"name\": \"Filta\",\n" +
" \"avatarUrls\": {\n" +
" \"16x16\": \"https://brainbubble.atlassian.net/secure/projectavatar?size=xsmall&pid=10501&avatarId=10307\",\n" +
" \"24x24\": \"https://brainbubble.atlassian.net/secure/projectavatar?size=small&pid=10501&avatarId=10307\",\n" +
" \"32x32\": \"https://brainbubble.atlassian.net/secure/projectavatar?size=medium&pid=10501&avatarId=10307\",\n" +
" \"48x48\": \"https://brainbubble.atlassian.net/secure/projectavatar?pid=10501&avatarId=10307\"\n" +
" }\n" +
" },\n" +
" \"versions\": [\n" +
" \n" +
" ],\n" +
" \"environment\": null,\n" +
" \"timeestimate\": 144000,\n" +
" \"lastViewed\": \"2013-11-24T16:37:50.358+0000\",\n" +
" \"aggregateprogress\": {\n" +
" \"progress\": 0,\n" +
" \"total\": 0\n" +
" },\n" +
" \"components\": [\n" +
" {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/component/10303\",\n" +
" \"id\": \"10303\",\n" +
" \"name\": \"Account Management\"\n" +
" },\n" +
" {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/component/10301\",\n" +
" \"id\": \"10301\",\n" +
" \"name\": \"User Management\"\n" +
" }\n" +
" ],\n" +
" \"comment\": {\n" +
" \"startAt\": 0,\n" +
" \"maxResults\": 1,\n" +
" \"total\": 1,\n" +
" \"comments\": [\n" +
" {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/issue/10742/comment/10500\",\n" +
" \"id\": \"10500\",\n" +
" \"author\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/user?username=joseph\",\n" +
" \"name\": \"joseph\",\n" +
" \"emailAddress\": \"joseph.b.mccarthy2012@googlemail.com\",\n" +
" \"avatarUrls\": {\n" +
" \"16x16\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=16\",\n" +
" \"24x24\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=24\",\n" +
" \"32x32\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=32\",\n" +
" \"48x48\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=48\"\n" +
" },\n" +
" \"displayName\": \"Joseph McCarthy\",\n" +
" \"active\": true\n" +
" },\n" +
" \"body\": \"&#116;&#104;&#105;&#115;&#32;&#105;&#115;&#32;&#110;&#111;&#116;&#32;&#114;&#101;&#97;&#108;&#108;&#121;&#32;&#97;&#115;&#115;&#105;&#103;&#110;&#101;&#100;&#32;&#116;&#111;&#32;&#109;&#101;&#44;&#32;&#106;&#117;&#115;&#116;&#32;&#116;&#101;&#115;&#116;&#105;&#110;&#103;&#32;&#111;&#117;&#116;&#32;&#116;&#104;&#101;&#32;&#105;&#110;&#116;&#101;&#108;&#108;&#105;&#106;&#32;&#45;&#32;&#106;&#105;&#114;&#97;&#32;&#112;&#108;&#117;&#103;&#105;&#110;&#32;&#58;&#41;\",\n" +
" \"updateAuthor\": {\n" +
" \"self\": \"https://brainbubble.atlassian.net/rest/api/2/user?username=joseph\",\n" +
" \"name\": \"joseph\",\n" +
" \"emailAddress\": \"joseph.b.mccarthy2012@googlemail.com\",\n" +
" \"avatarUrls\": {\n" +
" \"16x16\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=16\",\n" +
" \"24x24\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=24\",\n" +
" \"32x32\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=32\",\n" +
" \"48x48\": \"https://secure.gravatar.com/avatar/a5a271f9eee8bbb3795f41f290274f8c?d=mm&s=48\"\n" +
" },\n" +
" \"displayName\": \"Joseph McCarthy\",\n" +
" \"active\": true\n" +
" },\n" +
" \"created\": \"2013-10-09T22:14:54.979+0100\",\n" +
" \"updated\": \"2013-10-09T22:24:55.956+0100\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"timeoriginalestimate\": null,\n" +
" \"aggregatetimespent\": null\n" +
" }\n" +
"}");
return jsonObject;
}
}

View File

@ -0,0 +1,59 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class VersionTest {
@Test
public void testVersionInit(){
Version version = new Version(null,null);
}
@Test
public void testVersionJSON(){
Version version = new Version(null,getTestJSON());
assertEquals(version.getId(),"10200");
assertEquals(version.getName(),"1.0");
assertFalse(version.isArchived());
assertFalse(version.isReleased());
assertEquals(version.getReleaseDate(),"2013-12-01");
assertEquals(version.getDescription(),"First Full Functional Build");
}
@Test
public void testVersionToString(){
Version version = new Version(null,getTestJSON());
assertEquals(version.toString(),"1.0");
}
private JSONObject getTestJSON() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id","10200");
jsonObject.put("description","First Full Functional Build");
jsonObject.put("name","1.0");
jsonObject.put("archived",false);
jsonObject.put("released",false);
jsonObject.put("releaseDate","2013-12-01");
return jsonObject;
}
}
/**
"fixVersions": [
{
"self": "https://brainbubble.atlassian.net/rest/api/2/version/10200",
"id": "10200",
"description": "First Full Functional Build",
"name": "1.0",
"archived": false,
"released": false,
"releaseDate": "2013-12-01"
}
],
**/

View File

@ -0,0 +1,51 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
public class VotesTest {
@Test
public void testVotesInit(){
Votes votes = new Votes(null,null);
}
@Test
public void testVotesJSON(){
Votes votes = new Votes(null,getTestJSON());
assertFalse(votes.hasVoted());
assertEquals(votes.getId(),"10");
assertEquals(votes.getVotes(),0);
assertEquals(votes.getSelf(),"https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/votes");
}
@Test
public void testGetToString(){
Votes votes = new Votes(null,getTestJSON());
assertEquals(votes.toString(),"0");
}
private JSONObject getTestJSON() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("self","https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/votes");
jsonObject.put("votes",0);
jsonObject.put("hasVoted",false);
jsonObject.put("id","10");
return jsonObject;
}
}
/**
"votes": {
"self": "https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/votes",
"votes": 0,
"hasVoted": false
},
**/

View File

@ -0,0 +1,41 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONObject;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
public class WatchesTest {
@Test
public void testWatchesInit() {
Watches watches = new Watches(null, null);
}
@Test
public void testWatchesJSON() {
Watches watches = new Watches(null, getTestJSON());
assertFalse(watches.isWatching());
assertEquals(watches.getWatchCount(), 0);
assertEquals(watches.getId(), "10");
assertEquals(watches.getSelf(), "https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/watchers");
}
@Test
public void testWatchesToString() {
Watches watches = new Watches(null, getTestJSON());
assertEquals(watches.toString(), "0");
}
private JSONObject getTestJSON() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", "10");
jsonObject.put("self", "https://brainbubble.atlassian.net/rest/api/2/issue/FILTA-43/watchers");
jsonObject.put("watchCount", 0);
jsonObject.put("isWatching", false);
return jsonObject;
}
}