1
0
Fork 0

Rebase from HEAD project

master
Pierre-Luc Dupont 2016-06-02 10:13:38 -04:00
commit a90809c412
16 changed files with 988 additions and 418 deletions

View File

@ -3,4 +3,7 @@ Kyle Chaplin <chaplinkyle@gmail.com> @chaplinkyle
Alesandro Lang <info@alesandro-lang.com> @alesandroLang
Javier Molina <javinovich@gmail.com> @javinovich
Joseph McCarthy <luckymikuhatsune@gmail.com>
Magnus Lundberg <ludde89l@gmail.com>
Anders Kreinøe <anders@kreinoee.dk> @Kreinoee
Andrey Kuzmin <agkoozmin@gmail.com> @nach-o-man
Pierre-Luc Dupont <pldupont@gmail.com> @pldupont

View File

@ -127,6 +127,11 @@ public class Example {
/* Now let's start progress on this issue. */
issue.transition().execute("Start Progress");
/* Add the first comment and update it */
Comment comment = issue.addComment("I am a comment!");
comment.update("I am the first comment!");
issue.getComments().get(0).update("this works too!");
/* Pretend customfield_1234 is a text field. Get the raw field value... */
Object cfvalue = issue.getField("customfield_1234");
@ -194,6 +199,12 @@ public class Example {
for (Issue i : sr.issues)
System.out.println("Result: " + i);
/* Search with paging (optionally 10 issues at a time). There are optional
arguments for including/expanding fields, and page size/start. */
Issue.SearchResult sr = jira.searchIssues("project IN (GOTHAM) ORDER BY id");
while (sr.iterator().hasNext())
System.out.println("Result: " + sr.iterator().next());
} catch (JiraException ex) {
System.err.println(ex.getMessage());

View File

@ -30,6 +30,7 @@ import java.util.Map;
*/
public class Comment extends Resource {
private String issueKey = null;
private User author = null;
private String body = null;
private Date created = null;
@ -42,9 +43,10 @@ public class Comment extends Resource {
* @param restclient REST client instance
* @param json JSON payload
*/
protected Comment(RestClient restclient, JSONObject json) {
protected Comment(RestClient restclient, JSONObject json, String issueKey) {
super(restclient);
this.issueKey = issueKey;
if (json != null)
deserialise(json);
}
@ -86,7 +88,59 @@ public class Comment extends Resource {
if (!(result instanceof JSONObject))
throw new JiraException("JSON payload is malformed");
return new Comment(restclient, (JSONObject)result);
return new Comment(restclient, (JSONObject)result, issue);
}
/**
* Updates the comment body.
*
* @param issue associated issue record
* @param body Comment text
*
* @throws JiraException when the comment update fails
*/
public void update(String body) throws JiraException {
update(body, null, null);
}
/**
* Updates the comment body with limited visibility.
*
* @param issue associated issue record
* @param body Comment text
* @param visType Target audience type (role or group)
* @param visName Name of the role or group to limit visibility to
*
* @throws JiraException when the comment update fails
*/
public void update(String body, String visType, String visName)
throws JiraException {
JSONObject req = new JSONObject();
req.put("body", body);
if (visType != null && visName != null) {
JSONObject vis = new JSONObject();
vis.put("type", visType);
vis.put("value", visName);
req.put("visibility", vis);
}
JSON result = null;
try {
String issueUri = getBaseUri() + "issue/" + issueKey;
result = restclient.put(issueUri + "/comment/" + id, req);
} catch (Exception ex) {
throw new JiraException("Failed add update comment " + id, ex);
}
if (!(result instanceof JSONObject)) {
throw new JiraException("JSON payload is malformed");
}
deserialise((JSONObject) result);
}
@Override

View File

@ -19,24 +19,107 @@
package net.rcarz.jiraclient;
import net.sf.json.JSONArray;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import java.lang.Iterable;
import java.lang.UnsupportedOperationException;
import java.sql.Timestamp;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONNull;
/**
* Utility functions for translating between JSON and fields.
*/
public final class Field {
/**
* Field metadata structure.
*/
public static final class Meta {
public boolean required;
public String type;
public String items;
public String name;
public String system;
public String custom;
public int customId;
}
/**
* Field update operation.
*/
public static final class Operation {
public String name;
public Object value;
/**
* Initialises a new update operation.
*
* @param name Operation name
* @param value Field value
*/
public Operation(String name, Object value) {
this.name = name;
this.value = value;
}
}
/**
* Allowed value types.
*/
public enum ValueType {
KEY("key"), NAME("name"), ID_NUMBER("id"), VALUE("value");
private String typeName;
private ValueType(String typeName) {
this.typeName = typeName;
}
@Override
public String toString() {
return typeName;
}
};
/**
* Value and value type pair.
*/
public static final class ValueTuple {
public final String type;
public final Object value;
/**
* Initialises the value tuple.
*
* @param type
* @param value
*/
public ValueTuple(String type, Object value) {
this.type = type;
this.value = (value != null ? value : JSONNull.getInstance());
}
/**
* Initialises the value tuple.
*
* @param type
* @param value
*/
public ValueTuple(ValueType type, Object value) {
this(type.toString(), value);
}
}
public static final String ASSIGNEE = "assignee";
public static final String ATTACHMENT = "attachment";
public static final String CHANGE_LOG = "changelog";
;
public static final String CHANGE_LOG_ENTRIES = "histories";
public static final String CHANGE_LOG_ITEMS = "items";
public static final String COMMENT = "comment";
@ -66,8 +149,10 @@ public final class Field {
public static final String CREATED_DATE = "created";
public static final String UPDATED_DATE = "updated";
public static final String TRANSITION_TO_STATUS = "to";
public static final String DATE_FORMAT = "yyyy-MM-dd";
public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
private Field() { }
/**
@ -91,14 +176,22 @@ public final class Field {
*
* @param c a JSONObject instance
* @param restclient REST client instance
* @param issueKey key of the parent issue
*
* @return a list of comments found in c
*/
public static List<Comment> getComments(Object c, RestClient restclient) {
public static List<Comment> getComments(Object c, RestClient restclient,
String issueKey) {
List<Comment> results = new ArrayList<Comment>();
if (c instanceof JSONObject && !((JSONObject)c).isNullObject())
results = getResourceArray(Comment.class, ((Map)c).get("comments"), restclient);
if (c instanceof JSONObject && !((JSONObject)c).isNullObject()) {
results = getResourceArray(
Comment.class,
((Map)c).get("comments"),
restclient,
issueKey
);
}
return results;
}
@ -119,7 +212,7 @@ public final class Field {
return results;
}
/**
* Gets a list of remote links from the given object.
*
@ -206,20 +299,19 @@ public final class Field {
}
/**
* Gets a long from the given object.
*
* @param i a Long or an Integer instance
*
* @return a long primitive or 0 if i isn't a Long or an Integer instance
*/
+ * Gets a long from the given object.
+ *
+ * @param i a Long or an Integer instance
+ *
+ * @return a long primitive or 0 if i isn't a Long or an Integer instance
+ */
public static long getLong(Object i) {
long result = 0;
if (i instanceof Long)
if (i instanceof Long) {
result = ((Long) i).longValue();
if (i instanceof Integer)
} else if (i instanceof Integer) {
result = ((Integer) i).intValue();
}
return result;
}
@ -261,6 +353,22 @@ public final class Field {
public static <T extends Resource> T getResource(
Class<T> type, Object r, RestClient restclient) {
return getResource(type, r, restclient, null);
}
/**
* Gets a JIRA resource from the given object.
*
* @param type Resource data type
* @param r a JSONObject instance
* @param restclient REST client instance
* @param parentId id/key of the parent resource
*
* @return a Resource instance or null if r isn't a JSONObject instance
*/
public static <T extends Resource> T getResource(
Class<T> type, Object r, RestClient restclient, String parentId) {
T result = null;
if (r instanceof JSONObject && !((JSONObject)r).isNullObject()) {
@ -273,7 +381,7 @@ public final class Field {
else if (type == ChangeLogItem.class)
result = (T)new ChangeLogItem(restclient, (JSONObject)r);
else if (type == Comment.class)
result = (T)new Comment(restclient, (JSONObject)r);
result = (T)new Comment(restclient, (JSONObject)r, parentId);
else if (type == Component.class)
result = (T)new Component(restclient, (JSONObject)r);
else if (type == CustomFieldOption.class)
@ -290,6 +398,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 == ProjectCategory.class)
result = (T)new ProjectCategory(restclient, (JSONObject)r);
else if (type == RemoteLink.class)
result = (T)new RemoteLink(restclient, (JSONObject)r);
else if (type == Resolution.class)
@ -361,11 +471,34 @@ public final class Field {
public static <T extends Resource> List<T> getResourceArray(
Class<T> type, Object ra, RestClient restclient) {
return getResourceArray(type, ra, restclient, null);
}
/**
* Gets a list of JIRA resources from the given object.
*
* @param type Resource data type
* @param ra a JSONArray instance
* @param restclient REST client instance
* @param parentId id/key of the parent resource
*
* @return a list of Resources found in ra
*/
public static <T extends Resource> List<T> getResourceArray(
Class<T> type, Object ra, RestClient restclient, String parentId) {
List<T> results = new ArrayList<T>();
if (ra instanceof JSONArray) {
for (Object v : (JSONArray)ra) {
T item = getResource(type, v, restclient);
T item = null;
if (parentId != null) {
item = getResource(type, v, restclient, parentId);
} else {
item = getResource(type, v, restclient);
}
if (item != null)
results.add(item);
}
@ -483,10 +616,12 @@ public final class Field {
itemMap.put(ValueType.NAME.toString(), realValue.toString());
realResult = itemMap;
} else if (type.equals("string") && custom != null
} else if ( type.equals("option") ||
(
type.equals("string") && custom != null
&& (custom.equals("com.atlassian.jira.plugin.system.customfieldtypes:multicheckboxes") ||
custom.equals("com.atlassian.jira.plugin.system.customfieldtypes:multiselect"))) {
custom.equals("com.atlassian.jira.plugin.system.customfieldtypes:multiselect")))) {
realResult = new JSONObject();
((JSONObject)realResult).put(ValueType.VALUE.toString(), realValue.toString());
} else if (type.equals("string"))
@ -591,7 +726,7 @@ public final class Field {
else if (value instanceof TimeTracking)
return ((TimeTracking) value).toJsonObject();
} else if (m.type.equals("number")) {
if (!(value instanceof java.lang.Integer) && !(value instanceof java.lang.Double) && !(value
if(!(value instanceof java.lang.Integer) && !(value instanceof java.lang.Double) && !(value
instanceof java.lang.Float) && !(value instanceof java.lang.Long) )
{
throw new JiraException("Field '" + name + "' expects a Numeric value");
@ -655,83 +790,5 @@ public final class Field {
public static ValueTuple valueById(String id) {
return new ValueTuple(ValueType.ID_NUMBER, id);
}
/**
* Allowed value types.
*/
public enum ValueType {
KEY("key"), NAME("name"), ID_NUMBER("id"), VALUE("value");
private String typeName;
private ValueType(String typeName) {
this.typeName = typeName;
}
@Override
public String toString() {
return typeName;
}
}
/**
* Field metadata structure.
*/
public static final class Meta {
public boolean required;
public String type;
public String items;
public String name;
public String system;
public String custom;
public int customId;
}
/**
* Field update operation.
*/
public static final class Operation {
public String name;
public Object value;
/**
* Initialises a new update operation.
*
* @param name Operation name
* @param value Field value
*/
public Operation(String name, Object value) {
this.name = name;
this.value = value;
}
}
/**
* Value and value type pair.
*/
public static final class ValueTuple {
public final String type;
public final Object value;
/**
* Initialises the value tuple.
*
* @param type
* @param value
*/
public ValueTuple(String type, Object value) {
this.type = type;
this.value = (value != null ? value : JSONNull.getInstance());
}
/**
* Initialises the value tuple.
*
* @param type
* @param value
*/
public ValueTuple(ValueType type, Object value) {
this(type.toString(), value);
}
}
}

View File

@ -0,0 +1,74 @@
package net.rcarz.jiraclient;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import java.net.URI;
import java.util.Map;
/**
* Represens a Jira filter.
*/
public class Filter extends Resource {
private String name;
private String jql;
private boolean favourite;
public Filter(RestClient restclient, JSONObject json) {
super(restclient);
if (json != null)
deserialise(json);
}
private void deserialise(JSONObject json) {
Map map = json;
id = Field.getString(map.get("id"));
self = Field.getString(map.get("self"));
name = Field.getString(map.get("name"));
jql = Field.getString(map.get("jql"));
favourite = Field.getBoolean(map.get("favourite"));
}
public boolean isFavourite() {
return favourite;
}
public String getJql() {
return jql;
}
public String getName() {
return name;
}
public static Filter get(final RestClient restclient, final String id) throws JiraException {
JSON result = null;
try {
URI uri = restclient.buildURI(getBaseUri() + "filter/" + id);
result = restclient.get(uri);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve filter with id " + id, ex);
}
if (!(result instanceof JSONObject)) {
throw new JiraException("JSON payload is malformed");
}
return new Filter(restclient, (JSONObject) result);
}
@Override
public String toString() {
return "Filter{" +
"favourite=" + favourite +
", name='" + name + '\'' +
", jql='" + jql + '\'' +
'}';
}
}

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
@ -528,174 +528,181 @@ public class Issue extends Resource {
}
/**
/**
* Iterates over all issues in the query by getting the next page of
* issues when the iterator reaches the last of the current page.
*/
private static class IssueIterator implements Iterator<Issue> {
private Iterator<Issue> currentPage;
private RestClient restclient;
private Issue nextIssue;
private Iterator<Issue> currentPage;
private RestClient restclient;
private Issue nextIssue;
private Integer maxResults = -1;
private String jql;
private String includedFields;
private String expandFields;
private Integer startAt;
private List<Issue> issues;
private int total;
public IssueIterator(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults, Integer startAt)
throws JiraException {
this.restclient = restclient;
this.jql = jql;
this.includedFields = includedFields;
this.expandFields = expandFields;
this.maxResults = maxResults;
this.startAt = startAt;
private String jql;
private String includedFields;
private String expandFields;
private Integer startAt;
private List<Issue> issues;
private int total;
public IssueIterator(RestClient restclient, String jql, String includedFields,
String expandFields, Integer maxResults, Integer startAt)
throws JiraException {
this.restclient = restclient;
this.jql = jql;
this.includedFields = includedFields;
this.expandFields = expandFields;
this.maxResults = maxResults;
this.startAt = startAt;
}
@Override
public boolean hasNext() {
if (nextIssue != null) {
return true;
}
try {
nextIssue = getNextIssue();
} catch (JiraException e) {
throw new RuntimeException(e);
}
return nextIssue != null;
}
@Override
public boolean hasNext() {
if (nextIssue != null) {
return true;
}
try {
nextIssue = getNextIssue();
} catch (JiraException e) {
throw new RuntimeException(e);
}
return nextIssue != null;
}
@Override
public Issue next() {
if (! hasNext()) {
throw new NoSuchElementException();
}
Issue result = nextIssue;
nextIssue = null;
return result;
}
@Override
public Issue next() {
if (! hasNext()) {
throw new NoSuchElementException();
}
Issue result = nextIssue;
nextIssue = null;
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Method remove() not support for class " +
this.getClass().getName());
}
@Override
public void remove() {
throw new UnsupportedOperationException("Method remove() not support for class " + this.getClass().getName());
}
/**
* Gets the next issue, returning null if none more available
* Will ask the next set of issues from the server if the end
* of the current list of issues is reached.
*
* @return the next issue, null if none more available
* @throws JiraException
*/
private Issue getNextIssue() throws JiraException {
// first call
if (currentPage == null) {
currentPage = getNextIssues().iterator();
if (currentPage == null || !currentPage.hasNext()) {
return null;
} else {
return currentPage.next();
}
}
// check if we need to get the next set of issues
if (! currentPage.hasNext()) {
currentPage = getNextIssues().iterator();
}
/**
* Gets the next issue, returning null if none more available
* Will ask the next set of issues from the server if the end of the current list of issues is reached.
*
* @return the next issue, null if none more available
* @throws JiraException
*/
private Issue getNextIssue() throws JiraException {
// first call
if (currentPage == null) {
currentPage = getNextIssues().iterator();
if (currentPage == null) {
return null;
} else {
return currentPage.next();
}
}
// check if we need to get the next set of issues
if (! currentPage.hasNext()) {
currentPage = getNextIssues().iterator();
}
// return the next item if available
if (currentPage.hasNext()) {
return currentPage.next();
} else {
return null;
}
}
// return the next item if available
if (currentPage.hasNext()) {
return currentPage.next();
} else {
return null;
}
}
/**
* Execute the query to get the next set of issues.
* Also sets the startAt, maxMresults, total and issues fields,
* so that the SearchResult can access them.
*
* @return the next set of issues.
* @throws JiraException
*/
private List<Issue> getNextIssues() throws JiraException {
if (issues == null && startAt == null) {
startAt = Integer.valueOf(0);
} else if (issues != null) {
startAt = startAt + issues.size();
}
/**
* Execute the query to get the next set of issues.
* Also sets the startAt, maxMresults, total and issues fields,
* so that the SearchResult can access them.
*
* @return the next set of issues.
* @throws JiraException
*/
private List<Issue> getNextIssues() throws JiraException {
if (issues == null) {
startAt = Integer.valueOf(0);
} else {
startAt = startAt + issues.size();
}
JSON result = null;
JSON result = null;
try {
URI searchUri = createSearchURI(restclient, jql, includedFields,
expandFields, maxResults, startAt);
result = restclient.get(searchUri);
} catch (Exception ex) {
throw new JiraException("Failed to search issues", ex);
}
try {
URI searchUri = createSearchURI(restclient, jql, includedFields,
expandFields, maxResults, startAt);
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;
this.startAt = Field.getInteger(map.get("startAt"));
this.maxResults = Field.getInteger(map.get("maxResults"));
this.total = Field.getInteger(map.get("total"));
this.issues = Field.getResourceArray(Issue.class, map.get("issues"), restclient);
return issues;
}
if (!(result instanceof JSONObject)) {
throw new JiraException("JSON payload is malformed");
}
Map map = (Map) result;
this.startAt = Field.getInteger(map.get("startAt"));
this.maxResults = Field.getInteger(map.get("maxResults"));
this.total = Field.getInteger(map.get("total"));
this.issues = Field.getResourceArray(Issue.class, map.get("issues"), restclient);
return issues;
}
}
/**
* Issue search results structure.
*
* The issues of the result can be retrived from this class in 2 ways.
*
* The first is to access the issues field directly. This is a list of Issue instances.
* Note however that this will only contain the issues fetched in the initial search,
* so its size will be the same as the max result value or below.
*
* The second way is to use the iterator methods. This will return an Iterator instance,
* that will iterate over every result of the search, even if there is more than the max
* result value. The price for this, is that the call to next has none determistic performence,
* as it sometimes need to fetch a new batch of issues from Jira.
*/
public static class SearchResult {
public int start = 0;
public int max = 0;
public int total = 0;
public List<Issue> issues = null;
private RestClient restclient;
private String jql;
private String includedFields;
private String expandFields;
private Integer startAt;
private IssueIterator issueIterator;
private IssueIterator issueIterator;
public SearchResult(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults, Integer startAt) throws JiraException {
this.restclient = restclient;
this.jql = jql;
this.includedFields = includedFields;
this.expandFields = expandFields;
initSearchResult(maxResults, start);
public SearchResult(RestClient restclient, String jql, String includedFields,
String expandFields, Integer maxResults, Integer startAt)
throws JiraException {
this.issueIterator = new IssueIterator(
restclient,
jql,
includedFields,
expandFields,
maxResults,
startAt
);
/* backwards compatibility shim - first page only */
this.issueIterator.hasNext();
this.max = issueIterator.maxResults;
this.start = issueIterator.startAt;
this.issues = issueIterator.issues;
this.total = issueIterator.total;
}
private void initSearchResult(Integer maxResults, Integer start) throws JiraException {
this.issueIterator = new IssueIterator(restclient, jql, includedFields, expandFields, maxResults, startAt);
this.issueIterator.hasNext();
this.max = issueIterator.maxResults;
this.start = issueIterator.startAt;
this.issues = issueIterator.issues;
this.total = issueIterator.total;
}
/**
/**
* All issues found.
*
* @return All issues found.
*/
public IssueIterator iterator() {
return issueIterator;
public Iterator<Issue> iterator() {
return issueIterator;
}
}
@ -805,11 +812,13 @@ public class Issue extends Resource {
key = Field.getString(map.get("key"));
fields = (Map)map.get("fields");
if (fields == null)
return;
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);
comments = Field.getComments(fields.get(Field.COMMENT), restclient, key);
components = Field.getResourceArray(Component.class, fields.get(Field.COMPONENTS), restclient);
description = Field.getString(fields.get(Field.DESCRIPTION));
dueDate = Field.getDate(fields.get(Field.DUE_DATE));
@ -849,10 +858,10 @@ public class Issue extends Resource {
JSON result = null;
try {
Map<String, String> params = new HashMap<String, String>();
params.put("expand", "projects.issuetypes.fields");
params.put("projectKeys", pval);
params.put("issuetypeNames", itval);
Map<String, String> params = new HashMap<String, String>();
params.put("expand", "projects.issuetypes.fields");
params.put("projectKeys", pval);
params.put("issuetypeNames", itval);
URI createuri = restclient.buildURI(
getBaseUri() + "issue/createmeta",
params);
@ -876,7 +885,7 @@ public class Issue extends Resource {
restclient);
if (projects.isEmpty() || projects.get(0).getIssueTypes().isEmpty())
throw new JiraException("Project '"+ project + "' or issue type '" + issueType +
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();
@ -907,8 +916,8 @@ public class Issue extends Resource {
JSON result = null;
try {
Map<String, String> params = new HashMap<String, String>();
params.put("expand", "transitions.fields");
Map<String, String> params = new HashMap<String, String>();
params.put("expand", "transitions.fields");
URI transuri = restclient.buildURI(
getRestUri(key) + "/transitions",params);
result = restclient.get(transuri);
@ -947,7 +956,7 @@ public class Issue extends Resource {
throw new JiraException("Failed add attachment to issue " + key, ex);
}
}
/**
* Adds a remote link to this issue.
*
@ -998,24 +1007,24 @@ public class Issue extends Resource {
}
/**
* Removes an attachments.
*
* @param attachmentId attachment id to remove
*
* @throws JiraException when the attachment removal fails
*/
public void removeAttachment(String attachmentId) throws JiraException {
if (attachmentId == null) {
throw new NullPointerException("attachmentId may not be null");
}
try {
restclient.delete(getBaseUri() + "attachment/" + attachmentId);
} catch (Exception ex) {
throw new JiraException("Failed remove attachment " + attachmentId, ex);
}
}
* Removes an attachments.
*
* @param attachmentId attachment id to remove
*
* @throws JiraException when the attachment removal fails
*/
public void removeAttachment(String attachmentId) throws JiraException {
if (attachmentId == null) {
throw new NullPointerException("attachmentId may not be null");
}
try {
restclient.delete(getBaseUri() + "attachment/" + attachmentId);
} catch (Exception ex) {
throw new JiraException("Failed remove attachment " + attachmentId, ex);
}
}
/**
* Adds a comment to this issue.
@ -1024,8 +1033,8 @@ public class Issue extends Resource {
*
* @throws JiraException when the comment creation fails
*/
public void addComment(String body) throws JiraException {
addComment(body, null, null);
public Comment addComment(String body) throws JiraException {
return addComment(body, null, null);
}
/**
@ -1037,7 +1046,7 @@ public class Issue extends Resource {
*
* @throws JiraException when the comment creation fails
*/
public void addComment(String body, String visType, String visName)
public Comment addComment(String body, String visType, String visName)
throws JiraException {
JSONObject req = new JSONObject();
@ -1051,11 +1060,19 @@ public class Issue extends Resource {
req.put("visibility", vis);
}
JSON result = null;
try {
restclient.post(getRestUri(key) + "/comment", req);
result = restclient.post(getRestUri(key) + "/comment", req);
} catch (Exception ex) {
throw new JiraException("Failed add comment to issue " + key, ex);
}
if (!(result instanceof JSONObject)) {
throw new JiraException("JSON payload is malformed");
}
return new Comment(restclient, (JSONObject) result, key);
}
/**
@ -1248,7 +1265,7 @@ public class Issue extends Resource {
* <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
@ -1260,56 +1277,12 @@ public class Issue extends Resource {
Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put("fields", includedFields);
if (expand != null) {
queryParams.put("expand", expand);
}
if (expand != null) {
queryParams.put("expand", expand);
}
return new Issue(restclient, realGet(restclient, key, queryParams));
}
/**
* Search for issues with the given query.
*
* @param restclient REST client instance
*
* @param jql JQL statement
*
* @return a search result structure with results (issues include all
* navigable fields)
*
* @throws JiraException when the search fails
*/
public static SearchResult search(RestClient restclient, String jql)
throws JiraException {
return search(restclient, jql, null, null);
}
/**
* Search for issues with the given query and specify which fields to
* retrieve.
*
* @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>
*
* @return a search result structure with results
*
* @throws JiraException when the search fails
*/
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
@ -1329,10 +1302,10 @@ public class Issue extends Resource {
* <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
*
@ -1343,66 +1316,52 @@ public class Issue extends Resource {
* @throws JiraException when the search fails
*/
public static SearchResult search(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults, Integer startAt)
throws JiraException {
String includedFields, String expandFields, Integer maxResults,
Integer startAt) throws JiraException {
SearchResult sr = new SearchResult(restclient, jql, includedFields, expandFields, maxResults, startAt);
return sr;
return new SearchResult(
restclient,
jql,
includedFields,
expandFields,
maxResults,
startAt
);
}
private static JSON executeSearch(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults,
Integer startAt) throws JiraException {
JSON result = null;
try {
URI searchUri = createSearchURI(restclient, jql, includedFields,
expandFields, maxResults, startAt);
result = restclient.get(searchUri);
} catch (Exception ex) {
throw new JiraException("Failed to search issues", ex);
/**
* Creates the URI to execute a jql search.
*
* @param restclient
* @param jql
* @param includedFields
* @param expandFields
* @param maxResults
* @param startAt
* @return the URI to execute a jql search.
* @throws URISyntaxException
*/
private static URI createSearchURI(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults,
Integer startAt) throws URISyntaxException {
Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put("jql", jql);
if(maxResults != null){
queryParams.put("maxResults", String.valueOf(maxResults));
}
if (includedFields != null) {
queryParams.put("fields", includedFields);
}
if (expandFields != null) {
queryParams.put("expand", expandFields);
}
if (startAt != null) {
queryParams.put("startAt", String.valueOf(startAt));
}
if (!(result instanceof JSONObject)) {
throw new JiraException("JSON payload is malformed");
}
return result;
}
/**
* Creates the URI to execute a jql search.
*
* @param restclient
* @param jql
* @param includedFields
* @param expandFields
* @param maxResults
* @param startAt
* @return the URI to execute a jql search.
* @throws URISyntaxException
*/
private static URI createSearchURI(RestClient restclient, String jql,
String includedFields, String expandFields, Integer maxResults,
Integer startAt) throws URISyntaxException {
Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put("jql", jql);
if(maxResults != null){
queryParams.put("maxResults", String.valueOf(maxResults));
}
if (includedFields != null) {
queryParams.put("fields", includedFields);
}
if (expandFields != null) {
queryParams.put("expand", expandFields);
}
if (startAt != null) {
queryParams.put("startAt", String.valueOf(startAt));
}
URI searchUri = restclient.buildURI(getBaseUri() + "search", queryParams);
return searchUri;
}
URI searchUri = restclient.buildURI(getBaseUri() + "search", queryParams);
return searchUri;
}
/**
* Reloads issue data from the JIRA server (issue includes all navigable
@ -1611,7 +1570,7 @@ public class Issue extends Resource {
public User getReporter() {
return reporter;
}
public List<RemoteLink> getRemoteLinks() throws JiraException {
JSONArray obj;
try {
@ -1696,5 +1655,17 @@ public class Issue extends Resource {
return updatedDate;
}
public boolean delete(final boolean deleteSubtasks) throws JiraException {
boolean result;
try {
URI uri = restclient.buildURI(getBaseUri() + "issue/" + this.key, new HashMap<String, String>() {{
put("deleteSubtasks", String.valueOf(deleteSubtasks));
}});
result = (restclient.delete(uri) == null);
} catch (Exception ex) {
throw new JiraException("Failed to delete issue " + key, ex);
}
return result;
}
}

View File

@ -50,7 +50,7 @@ public class JiraClient {
* @throws JiraException
*/
public JiraClient(String uri) throws JiraException {
this(uri, null);
this(null, uri, null);
}
/**
@ -61,17 +61,31 @@ public class JiraClient {
* @throws JiraException
*/
public JiraClient(String uri, ICredentials creds) throws JiraException {
PoolingClientConnectionManager connManager = new PoolingClientConnectionManager();
connManager.setDefaultMaxPerRoute(20);
connManager.setMaxTotal(40);
HttpClient httpclient = new DefaultHttpClient(connManager);
this(null, uri, creds);
}
/**
* Creates an authenticated JIRA client with custom HttpClient.
*
* @param httpClient Custom HttpClient to be used
* @param uri Base URI of the JIRA server
* @param creds Credentials to authenticate with
* @throws JiraException
*/
public JiraClient(HttpClient httpClient, String uri, ICredentials creds) throws JiraException {
if (httpClient == null) {
PoolingClientConnectionManager connManager = new PoolingClientConnectionManager();
connManager.setDefaultMaxPerRoute(20);
connManager.setMaxTotal(40);
httpClient = new DefaultHttpClient(connManager);
}
restclient = new RestClient(httpclient, creds, URI.create(uri));
restclient = new RestClient(httpClient, creds, URI.create(uri));
if (creds != null) {
username = creds.getLogonName();
//intialize connection if required
creds.initialize(restclient);
//intialize connection if required
creds.initialize(restclient);
}
}
@ -179,7 +193,7 @@ public class JiraClient {
public Issue.SearchResult searchIssues(String jql)
throws JiraException {
return Issue.search(restclient, jql, null, null);
return searchIssues(jql, null, null, null, null);
}
/**
@ -196,7 +210,7 @@ public class JiraClient {
public Issue.SearchResult searchIssues(String jql, Integer maxResults)
throws JiraException {
return Issue.search(restclient, jql, null, maxResults);
return searchIssues(jql, null, null, maxResults, null);
}
/**
@ -223,7 +237,7 @@ public class JiraClient {
public Issue.SearchResult searchIssues(String jql, String includedFields)
throws JiraException {
return Issue.search(restclient, jql, includedFields, null);
return searchIssues(jql, includedFields, null, null, null);
}
/**
@ -248,10 +262,10 @@ public class JiraClient {
*
* @throws JiraException when the search fails
*/
public Issue.SearchResult searchIssues(String jql, String includedFields, String expandFields)
throws JiraException {
public Issue.SearchResult searchIssues(String jql, String includedFields,
String expandFields) throws JiraException {
return Issue.search(restclient, jql, includedFields, expandFields, null, null);
return searchIssues(jql, includedFields, expandFields, null, null);
}
/**
@ -279,7 +293,7 @@ public class JiraClient {
public Issue.SearchResult searchIssues(String jql, String includedFields, Integer maxResults)
throws JiraException {
return Issue.search(restclient, jql, includedFields, maxResults);
return searchIssues(jql, includedFields, null, maxResults, null);
}
/**
@ -313,8 +327,7 @@ public class JiraClient {
public Issue.SearchResult searchIssues(String jql, String includedFields,
Integer maxResults, Integer startAt) throws JiraException {
return Issue.search(restclient, jql, includedFields, null, maxResults,
startAt);
return searchIssues(jql, includedFields, null, maxResults, startAt);
}
/**
@ -347,11 +360,28 @@ public class JiraClient {
*
* @throws JiraException when the search fails
*/
public Issue.SearchResult searchIssues(String jql, String includedFields, String expandFields,
Integer maxResults, Integer startAt) throws JiraException {
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 Issue.search(
restclient,
jql,
includedFields,
expandFields,
maxResults,
startAt
);
}
/**
* Retrieve the jira filter with the supplied id.
* @param id id of the filter to retreive.
* @return The Jira filter with the supplied id
* @throws JiraException
*/
public Filter getFilter(final String id) throws JiraException {
return Filter.get(restclient, id);
}
/**

View File

@ -41,6 +41,8 @@ public class Project extends Resource {
private List<IssueType> issueTypes = null;
private List<Version> versions = null;
private Map<String, String> roles = null;
private ProjectCategory category = null;
private String email = null;
/**
* Creates a project from a JSON payload.
@ -73,6 +75,8 @@ public class Project extends Resource {
restclient);
versions = Field.getResourceArray(Version.class, map.get("versions"), restclient);
roles = Field.getMap(String.class, String.class, map.get("roles"));
category = Field.getResource(ProjectCategory.class, map.get( "projectCategory" ), restclient);
email = Field.getString( map.get("email"));
}
/**
@ -170,5 +174,13 @@ public class Project extends Resource {
public Map<String, String> getRoles() {
return roles;
}
public ProjectCategory getCategory() {
return category;
}
public String getEmail() {
return email;
}
}

View File

@ -0,0 +1,97 @@
/**
* jira-client - a simple JIRA REST client
* Copyright (c) 2013 Bob Carroll (bob.carroll@alum.rit.edu)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.rcarz.jiraclient;
import java.util.Map;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
/**
* Represents a project category.
*/
public class ProjectCategory extends Resource {
private String name = null;
private String description = null;
/**
* Creates a category from a JSON payload.
*
* @param restclient REST client instance
* @param json JSON payload
*/
protected ProjectCategory(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"));
description = Field.getString(map.get("description"));
name = Field.getString(map.get("name"));
}
/**
* Retrieves the given status record.
*
* @param restclient REST client instance
* @param id Internal JIRA ID of the status
*
* @return a status instance
*
* @throws JiraException when the retrieval fails
*/
public static ProjectCategory get(RestClient restclient, String id)
throws JiraException {
JSON result = null;
try {
result = restclient.get(getBaseUri() + "projectCategory/" + id);
} catch (Exception ex) {
throw new JiraException("Failed to retrieve status " + id, ex);
}
if (!(result instanceof JSONObject))
throw new JiraException("JSON payload is malformed");
return new ProjectCategory(restclient, (JSONObject)result);
}
@Override
public String toString() {
return getName();
}
public String getDescription() {
return description;
}
public String getName() {
return name;
}
}

View File

@ -60,10 +60,10 @@ public class WorkLog extends Resource {
id = Field.getString(map.get("id"));
author = Field.getResource(User.class, map.get("author"), restclient);
comment = Field.getString(map.get("comment"));
created = Field.getDate(map.get("created"));
updated = Field.getDate(map.get("updated"));
created = Field.getDateTime(map.get("created"));
updated = Field.getDateTime(map.get("updated"));
updateAuthor = Field.getResource(User.class, map.get("updateAuthor"), restclient);
started = Field.getDate(map.get("started"));
started = Field.getDateTime(map.get("started"));
timeSpent = Field.getString(map.get("timeSpent"));
timeSpentSeconds = Field.getInteger(map.get("timeSpentSeconds"));
}

View File

@ -0,0 +1,29 @@
package net.rcarz.jiraclient;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Test cases for stuff relating to filters.
*/
public class FilterTest {
@Test
public void testGetFilter() throws JiraException {
JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);
String id = "12844";
Filter filter = jira.getFilter(id);
assertNotNull(filter);
assertEquals("with id " + id, id, filter.getId());
final String expectedName = "All JIRA Bugs";
assertEquals("with name " + expectedName, expectedName, filter.getName());
final String expectedJql = "project = 10240 AND issuetype = 1 ORDER BY key DESC";
assertEquals("with jql: " + expectedJql, expectedJql, filter.getJql());
assertEquals("None favourite", false, filter.isFavourite());
}
}

View File

@ -5,6 +5,7 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.net.URI;
import java.util.List;
import java.util.Map;
@ -16,6 +17,9 @@ import org.joda.time.DateTimeZone;
import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
public class IssueTest {
/**
@ -199,4 +203,16 @@ public class IssueTest {
}
/**
* false is bu default so we test positive case only
*/
@Test
public void testDelete() throws Exception {
RestClient restClient = mock(RestClient.class);
URI uri = new URI("DUMMY");
when(restClient.buildURI(anyString(), any(Map.class))).thenReturn(uri);
when(restClient.delete(eq(uri))).thenReturn(null);
Issue issue = new Issue(restClient, Utils.getTestIssue());
Assert.assertTrue(issue.delete(true));
}
}

View File

@ -0,0 +1,60 @@
package net.rcarz.jiraclient;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
public class ProjectTest {
@Test
public void testCreateProject() {
new Project(null, Utils.getTestProject());
}
@Test
public void projectDataTest()
{
String email = "from-jira@example.com";
String assigneeType = "PROJECT_LEAD";
String name = "Example";
String self = "http://www.example.com/jira/rest/api/2/project/EX";
String id = "10000";
String key = "EX";
String description = "This project was created as an example for REST.";
Project project = new Project(null, Utils.getTestProject());
assertEquals(description, project.getDescription());
assertEquals(name, project.getName());
assertEquals(id, project.getId());
assertEquals(email, project.getEmail());
assertEquals(assigneeType, project.getAssigneeType());
assertTrue(project.getVersions().isEmpty());
assertEquals(name, project.getName());
assertEquals(self, project.getSelf());
assertEquals( key, project.getKey());
}
@Test
public void projectIssueTypesTest()
{
Project project = new Project(null, Utils.getTestProject());
assertEquals(2, project.getIssueTypes().size());
assertEquals("Task", project.getIssueTypes().get(0).getName());
assertEquals("Bug", project.getIssueTypes().get(1).getName());
}
@Test
public void projectCategoryTest()
{
String name = "FIRST";
String id = "10000";
String description = "First Project Category";
Project project = new Project(null, Utils.getTestProject());
assertNotNull(project.getCategory());
assertEquals(description, project.getCategory().getDescription());
assertEquals(name, project.getCategory().getName());
assertEquals(id, project.getCategory().getId());
}
}

View File

@ -2,9 +2,13 @@ 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;
import java.nio.channels.Pipe;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import static org.junit.Assert.*;
public class SearchTest {
@ -21,6 +25,51 @@ public class SearchTest {
assertEquals("and resolution Fixed", "Fixed", searchResult.issues.get(0).getResolution().getName());
}
@Test
public void testEmptySearchGivesEmptyResult() throws JiraException {
final JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);
//Valid jql query that will always yield no results.
final String q = "key = NotExisting-9999999 AND key = blah-8833772";
Issue.SearchResult searchResult = jira.searchIssues(q);
final String assertMsg = "Searches that yield no results, should return an empty "
+ Issue.SearchResult.class.getSimpleName() + " instance";
assertTrue(assertMsg, searchResult.issues.isEmpty());
assertFalse(assertMsg, searchResult.issues.iterator().hasNext());
assertEquals(assertMsg, 0, searchResult.total);
assertEquals(assertMsg, 0, searchResult.start);
}
@Test
public void testSearchResultIteratorWithinMaxResultLimit() throws JiraException {
final JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);
final int usedMax = 2;
//Will return everything from the public Jira for Jira
final Issue.SearchResult searchResult = jira.searchIssues("", usedMax);
final List<Issue> iterResults = new ArrayList<Issue>(usedMax);
final Iterator<Issue> iterator = searchResult.iterator();
for (int i = 0 ; i < usedMax ; i++) {
iterResults.add(iterator.next());
}
assertEquals(searchResult.issues, iterResults);
}
@Test
public void testIterateBeyondMaxResult() throws JiraException {
final JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);
//Will return everything from the public Jira for Jira (at the time of writing 163697 issues).
final int usedMax = 2;
Issue.SearchResult searchResult = jira.searchIssues("", usedMax);
final Iterator<Issue> iterator = searchResult.iterator();
System.out.println(searchResult.issues);
for (int i = 0 ; i < 3 ; i++) {
//Running this 3 times without failing, ensures the it can fetch issues beyond the first fetch batch size, as the used max result is only 2.
iterator.next();
}
}
@Test
public void testExpandingChangeLogInSearch() throws JiraException {
JiraClient jira = new JiraClient("https://jira.atlassian.com/", null);

View File

@ -281,4 +281,116 @@ public class Utils {
return jsonObject;
}
public static JSONObject getTestProject() {
JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON("{" +
" \"expand\": \"description,lead,url,projectKeys\"," +
" \"self\": \"http://www.example.com/jira/rest/api/2/project/EX\"," +
" \"id\": \"10000\"," +
" \"key\": \"EX\"," +
" \"description\": \"This project was created as an example for REST.\"," +
" \"lead\": {" +
" \"self\": \"http://www.example.com/jira/rest/api/2/user?username=fred\"," +
" \"name\": \"fred\"," +
" \"avatarUrls\": {" +
" \"48x48\": \"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred\"," +
" \"24x24\": \"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred\"," +
" \"16x16\": \"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred\"," +
" \"32x32\": \"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred\"" +
" }," +
" \"displayName\": \"Fred F. User\"," +
" \"active\": false" +
" }," +
" \"components\": [" +
" {" +
" \"self\": \"http://www.example.com/jira/rest/api/2/component/10000\"," +
" \"id\": \"10000\"," +
" \"name\": \"Component 1\"," +
" \"description\": \"This is a JIRA component\"," +
" \"lead\": {" +
" \"self\": \"http://www.example.com/jira/rest/api/2/user?username=fred\"," +
" \"name\": \"fred\"," +
" \"avatarUrls\": {" +
" \"48x48\": \"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred\"," +
" \"24x24\": \"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred\"," +
" \"16x16\": \"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred\"," +
" \"32x32\": \"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred\"" +
" }," +
" \"displayName\": \"Fred F. User\"," +
" \"active\": false" +
" }," +
" \"assigneeType\": \"PROJECT_LEAD\"," +
" \"assignee\": {" +
" \"self\": \"http://www.example.com/jira/rest/api/2/user?username=fred\"," +
" \"name\": \"fred\"," +
" \"avatarUrls\": {" +
" \"48x48\": \"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred\"," +
" \"24x24\": \"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred\"," +
" \"16x16\": \"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred\"," +
" \"32x32\": \"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred\"" +
" }," +
" \"displayName\": \"Fred F. User\"," +
" \"active\": false" +
" }," +
" \"realAssigneeType\": \"PROJECT_LEAD\"," +
" \"realAssignee\": {" +
" \"self\": \"http://www.example.com/jira/rest/api/2/user?username=fred\"," +
" \"name\": \"fred\"," +
" \"avatarUrls\": {" +
" \"48x48\": \"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred\"," +
" \"24x24\": \"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred\"," +
" \"16x16\": \"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred\"," +
" \"32x32\": \"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred\"" +
" }," +
" \"displayName\": \"Fred F. User\"," +
" \"active\": false" +
" }," +
" \"isAssigneeTypeValid\": false," +
" \"project\": \"HSP\"," +
" \"projectId\": 10000" +
" }" +
" ]," +
" \"issueTypes\": [" +
" {" +
" \"self\": \"http://localhost:8090/jira/rest/api/2.0/issueType/3\"," +
" \"id\": \"3\"," +
" \"description\": \"A task that needs to be done.\"," +
" \"iconUrl\": \"http://localhost:8090/jira/images/icons/issuetypes/task.png\"," +
" \"name\": \"Task\"," +
" \"subtask\": false," +
" \"avatarId\": 1" +
" }," +
" {" +
" \"self\": \"http://localhost:8090/jira/rest/api/2.0/issueType/1\"," +
" \"id\": \"1\"," +
" \"description\": \"A problem with the software.\"," +
" \"iconUrl\": \"http://localhost:8090/jira/images/icons/issuetypes/bug.png\"," +
" \"name\": \"Bug\"," +
" \"subtask\": false," +
" \"avatarId\": 10002" +
" }" +
" ]," +
" \"url\": \"http://www.example.com/jira/browse/EX\"," +
" \"email\": \"from-jira@example.com\"," +
" \"assigneeType\": \"PROJECT_LEAD\"," +
" \"versions\": []," +
" \"name\": \"Example\"," +
" \"roles\": {" +
" \"Developers\": \"http://www.example.com/jira/rest/api/2/project/EX/role/10000\"" +
" }," +
" \"avatarUrls\": {" +
" \"48x48\": \"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000\"," +
" \"24x24\": \"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000\"," +
" \"16x16\": \"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000\"," +
" \"32x32\": \"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000\"" +
" }," +
" \"projectCategory\": {" +
" \"self\": \"http://www.example.com/jira/rest/api/2/projectCategory/10000\"," +
" \"id\": \"10000\"," +
" \"name\": \"FIRST\"," +
" \"description\": \"First Project Category\"" +
" }" +
"}");
return jsonObject;
}
}

View File

@ -43,12 +43,8 @@ public class WorklogTest {
userJSON.put("name","Joseph McCarthy");
mockJSONObject.put("author", userJSON);
String DATE_FORMAT = "yyyy-MM-dd";
SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
final Date parse = df.parse(dateString, new ParsePosition(0));
WorkLog workLog = new WorkLog(mockRestClient,mockJSONObject);
assertEquals(parse.toString() + " by Joseph McCarthy",workLog.toString());
assertEquals(workLog.getCreatedDate() + " by Joseph McCarthy",workLog.toString());
}
@Test
@ -63,14 +59,13 @@ public class WorklogTest {
assertEquals("45517", workLog.getId());
String author = "joseph";
assertEquals(author, workLog.getAuthor().getName());
String started = "2015-08-17T00:00:00.000";
assertEquals(started, simpleDateFormat.format(workLog.getStarted()));
String created = "2015-08-20T00:00:00.000";
assertEquals(created, simpleDateFormat.format(workLog.getCreatedDate()));
final long expectedStartedUnixTimeStamp = 1439803140000L; //unix timestamp in millis of 2015-08-17T13:19:00.000+0400
assertEquals(expectedStartedUnixTimeStamp, workLog.getStarted().getTime());
final long expectedCreatedAndUpdatedUnitTimeStamp = 1440062384000L; //unix timestamp in millis of 2015-08-20T13:19:44.000+0400
assertEquals(expectedCreatedAndUpdatedUnitTimeStamp, workLog.getCreatedDate().getTime());
assertEquals(expectedCreatedAndUpdatedUnitTimeStamp, workLog.getUpdatedDate().getTime());
assertEquals(21600, workLog.getTimeSpentSeconds());
assertEquals(author, workLog.getUpdateAuthor().getName());
assertEquals(created, simpleDateFormat.format(workLog.getUpdatedDate()));
}
}