1
0
Fork 0

Add worklog to issues

master
Marius Merkevicius 2016-10-17 00:57:23 +03:00
parent c9220cb17e
commit c0b9f1ae11
5 changed files with 279 additions and 5 deletions

View File

@ -23,11 +23,16 @@ import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.*;
import net.rcarz.utils.WorklogUtils;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* Represents a JIRA issue.
@ -1075,6 +1080,36 @@ public class Issue extends Resource {
return new Comment(restclient, (JSONObject) result, key);
}
/**
* Adds {@link WorkLog} to this issue
* @param comment provided comment
* @param startDate provided start date
* @param timeSpentSeconds provided time spent. This cannot be lower than 1m inute
* @return
* @throws JiraException when worklog creation fails
*/
public WorkLog addWorkLog(String comment, DateTime startDate, long timeSpentSeconds) throws JiraException {
try {
if (comment == null)
throw new IllegalArgumentException("Invalid comment.");
if (startDate == null)
throw new IllegalArgumentException("Invalid start time.");
if (timeSpentSeconds < 60) // We do not add a worklog that duration is below a minute
throw new IllegalArgumentException("Time spent cannot be lower than 1 minute.");
JSONObject req = new JSONObject();
req.put("comment", comment);
req.put("started", DateTimeFormat.forPattern(Field.DATETIME_FORMAT).print(startDate.getMillis()));
req.put("timeSpent", WorklogUtils.formatDurationFromSeconds(timeSpentSeconds));
JSON result = restclient.post(getRestUri(key) + "/worklog", req);
JSONObject jo = (JSONObject) result;
return new WorkLog(restclient, jo);
} catch (Exception ex) {
throw new JiraException("Failed add worklog to issue " + key, ex);
}
}
/**
* Links this issue with another issue.
*

View File

@ -0,0 +1,39 @@
package net.rcarz.utils;
import net.rcarz.jiraclient.WorkLog;
import org.joda.time.DurationFieldType;
import org.joda.time.Period;
import org.joda.time.PeriodType;
/**
* Created by mariusmerkevicius on 1/30/16.
* A set of utils static methods help set {@link WorkLog}
*/
public class WorklogUtils {
/**
* Formats duration time into pretty string format
* Does not output seconds
* @param durationInSeconds provided duration to format
* @return formatted duration
*/
public static String formatDurationFromSeconds(long durationInSeconds) {
if (durationInSeconds < 60)
return "0m";
StringBuilder builder = new StringBuilder();
PeriodType type = PeriodType.forFields(new DurationFieldType[]{
DurationFieldType.hours(),
DurationFieldType.minutes()
});
Period period = new Period(1000 * durationInSeconds, type);
if (period.getHours() != 0)
builder.append(period.getHours()).append("h").append(" ");
if (period.getMinutes() != 0)
builder.append(period.getMinutes()).append("m").append(" ");
if ((builder.length() > 0) && builder.charAt(builder.length()-1) == " ".charAt(0))
builder.deleteCharAt(builder.length()-1);
return builder.toString();
}
}

View File

@ -0,0 +1,130 @@
package net.rcarz.jiraclient;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import org.joda.time.DateTime;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Created by mariusmerkevicius on 1/30/16.
*/
public class IssueWorklogTest {
@Test
public void testParsing_inputValid_shouldCreateJsonObject() throws Exception {
// Arrange
// Act
JSONObject worklogObject = (JSONObject) JSONSerializer.toJSON(RESPONSE_WORKLOG_BODY);
// Assert
assertNotNull(worklogObject);
}
@Test
public void testParsing_inputValidJson_shouldCreateWorklog() throws Exception {
// Arrange
// Act
WorkLog workLog = new WorkLog(mock(RestClient.class), (JSONObject) JSONSerializer.toJSON(RESPONSE_WORKLOG_BODY));
// Assert
assertThat(workLog).isNotNull();
assertThat(workLog.getAuthor()).isNotNull();
assertThat(workLog.getSelf()).isEqualTo("https://jira.test.lt/rest/api/2/issue/32374/worklog/80720");
assertThat(workLog.getId()).isEqualTo("80720");
assertThat(workLog.getComment()).isEqualTo("Test");
assertThat(workLog.getCreatedDate().getTime()).isEqualTo(1454104800000L);
assertThat(workLog.getUpdatedDate().getTime()).isEqualTo(1454104800000L);
assertThat(workLog.getStarted().getTime()).isEqualTo(1453879853201L);
assertThat(workLog.getTimeSpent()).isEqualTo("5m");
assertThat(workLog.getTimeSpentSeconds()).isEqualTo(300);
}
@Test
public void testAdding_inputValid_shouldInvokeAdding() throws Exception {
// Arrange
Issue issue = mock(Issue.class);
issue.restclient = mock(RestClient.class);
doCallRealMethod().when(issue).addWorkLog(anyString(), any(DateTime.class), anyLong());
// Act
issue.addWorkLog("test", DateTime.now(), 60);
// Assert
verify(issue.restclient).post(anyString(), any(JSON.class));
}
@Test
public void testAdding_inputNullComment_shouldNotAdd() throws Exception {
// Arrange
Issue issue = mock(Issue.class);
issue.restclient = mock(RestClient.class);
doCallRealMethod().when(issue).addWorkLog(anyString(), any(DateTime.class), anyLong());
// Act
try {
issue.addWorkLog(null, DateTime.now(), 120);
} catch (JiraException e) {
assertThat(e).hasMessageContaining("Failed add worklog to issue");
}
// Assert
verify(issue.restclient, never()).post(anyString(), any(JSON.class));
}
@Test
public void testAdding_inputNullDateTime_shouldNotAdd() throws Exception {
// Arrange
Issue issue = mock(Issue.class);
issue.restclient = mock(RestClient.class);
doCallRealMethod().when(issue).addWorkLog(anyString(), any(DateTime.class), anyLong());
// Act
try {
issue.addWorkLog("asdf", null, 120);
} catch (JiraException e) {
assertThat(e).hasMessageContaining("Failed add worklog to issue");
}
// Assert
verify(issue.restclient, never()).post(anyString(), any(JSON.class));
}
@Test
public void testAdding_inputDurationTooLow_shouldNotAdd() throws Exception {
// Arrange
Issue issue = mock(Issue.class);
issue.restclient = mock(RestClient.class);
doCallRealMethod().when(issue).addWorkLog(anyString(), any(DateTime.class), anyLong());
// Act
try {
issue.addWorkLog("asdf", DateTime.now(), 30);
} catch (JiraException e) {
assertThat(e).hasMessageContaining("Failed add worklog to issue");
}
// Assert
verify(issue.restclient, never()).post(anyString(), any(JSON.class));
}
//region Constants
// Mock response from jira
public static final String RESPONSE_WORKLOG_BODY = "{\"self\":\"https://jira.test.lt/rest/api/2/issue/32374/worklog/80720\",\"author\":{\"self\":\"https://jira.test.lt/rest/api/2/user?username=test%40test.lt\",\"name\":\"test@test.lt\",\"key\":\"test@test.lt\",\"emailAddress\":\"test@test.lt\",\"avatarUrls\":{\"48x48\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=48\",\"24x24\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=24\",\"16x16\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=16\",\"32x32\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=32\"},\"displayName\":\"Marius Merkevicius\",\"active\":true,\"timeZone\":\"Europe/Vilnius\"},\"updateAuthor\":{\"self\":\"https://jira.test.lt/rest/api/2/user?username=test%40test.lt\",\"name\":\"test@test.lt\",\"key\":\"test@test.lt\",\"emailAddress\":\"test@test.lt\",\"avatarUrls\":{\"48x48\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=48\",\"24x24\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=24\",\"16x16\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=16\",\"32x32\":\"https://secure.gravatar.com/avatar/e4dacfe8f27cb89341bf990e556a4be0?d=mm&s=32\"},\"displayName\":\"Marius Merkevicius\",\"active\":true,\"timeZone\":\"Europe/Vilnius\"},\"comment\":\"Test\",\"created\":\"2016-01-30T20:46:16.583+0200\",\"updated\":\"2016-01-30T20:46:16.583+0200\",\"started\":\"2016-01-27T09:30:53.201+0200\",\"timeSpent\":\"5m\",\"timeSpentSeconds\":300,\"id\":\"80720\"}";
//endregion
}

View File

@ -59,11 +59,10 @@ public class WorklogTest {
assertEquals("45517", workLog.getId());
String author = "joseph";
assertEquals(author, workLog.getAuthor().getName());
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());
String started = "2015-08-17T12:19:00.000";
assertEquals(started, simpleDateFormat.format(workLog.getStarted()));
String created = "2015-08-20T00:00:00.000";
assertEquals(created, simpleDateFormat.format(workLog.getCreatedDate()));
assertEquals(21600, workLog.getTimeSpentSeconds());
assertEquals(author, workLog.getUpdateAuthor().getName());
}

View File

@ -0,0 +1,71 @@
package net.rcarz.utils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Created by mariusmerkevicius on 1/30/16.
*/
public class WorklogUtilsFormatDurationTest {
@Test
public void testEmpty() throws Exception {
assertEquals("0m", WorklogUtils.formatDurationFromSeconds(0));
}
@Test
public void testNegative() throws Exception {
assertEquals("0m", WorklogUtils.formatDurationFromSeconds(-200));
}
@Test public void testLowSecond() throws Exception {
assertEquals("0m", WorklogUtils.formatDurationFromSeconds(1));
}
@Test public void testSeconds() throws Exception {
assertEquals("0m", WorklogUtils.formatDurationFromSeconds(59));
}
@Test public void testMinutes() throws Exception {
assertEquals("1m", WorklogUtils.formatDurationFromSeconds(60));
}
@Test public void testMinutesAndSeconds() throws Exception {
assertEquals("1m", WorklogUtils.formatDurationFromSeconds(
60 // 1 minute
+ 2) // 2 seconds
);
}
@Test public void testMinutesAndSeconds2() throws Exception {
assertEquals("2m", WorklogUtils.formatDurationFromSeconds(
60 // 1 minute
+ 72) // 72 seconds
);
}
@Test public void testHours() throws Exception {
assertEquals("1h 10m", WorklogUtils.formatDurationFromSeconds(
(60 * 60) // 1 hour
+ (10 * 60) // 10 minutes
+ 3) // 3 seconds
);
}
@Test public void testDays() throws Exception {
assertEquals("50h 20m", WorklogUtils.formatDurationFromSeconds(
(60 * 60 * 50) // 50 hours
+ (60 * 20) // 20 minutes
+ (3) // s seconds
));
}
@Test public void testDays2() throws Exception {
assertEquals("50h 22m", WorklogUtils.formatDurationFromSeconds(
(60 * 60 * 50) // 50 hours
+ (60 * 20) // 20 minutes
+ (125) // 125 seconds
));
}
}