diff --git a/src/main/java/com/taskadapter/redmineapi/bean/Issue.java b/src/main/java/com/taskadapter/redmineapi/bean/Issue.java index 693fea68..62a69f0c 100644 --- a/src/main/java/com/taskadapter/redmineapi/bean/Issue.java +++ b/src/main/java/com/taskadapter/redmineapi/bean/Issue.java @@ -29,6 +29,8 @@ public class Issue implements Identifiable, FluentStyle { public final static Property SUBJECT = new Property<>(String.class, "subject"); public final static Property START_DATE = new Property<>(Date.class, "startDate"); public final static Property DUE_DATE = new Property<>(Date.class, "dueDate"); + public final static Property ACTUAL_START_DATE = new Property<>(Date.class, "actualStartDate"); + public final static Property ACTUAL_DUE_DATE = new Property<>(Date.class, "actualDueDate"); public final static Property CREATED_ON = new Property<>(Date.class, "createdOn"); public final static Property UPDATED_ON = new Property<>(Date.class, "updatedOn"); public final static Property DONE_RATIO = new Property<>(Integer.class, "doneRatio"); @@ -249,6 +251,24 @@ public Issue setDueDate(Date dueDate) { return this; } + public Date getActualStartDate() { + return storage.get(ACTUAL_START_DATE); + } + + public Issue setActualStartDate(Date actualStartDate) { + storage.set(ACTUAL_START_DATE, actualStartDate); + return this; + } + + public Date getActualDueDate() { + return storage.get(ACTUAL_DUE_DATE); + } + + public Issue setActualDueDate(Date actualStartDate) { + storage.set(ACTUAL_DUE_DATE, actualStartDate); + return this; + } + public Integer getAuthorId() { return storage.get(AUTHOR_ID); } diff --git a/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilder.java b/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilder.java index 26435c90..f9168e87 100644 --- a/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilder.java +++ b/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilder.java @@ -229,6 +229,10 @@ public static void writeIssue(final JSONWriter writer, Issue issue) throws JSONE addIfSet(writer, "author_id", storage, Issue.AUTHOR_ID); addIfSet(writer, "start_date", storage, Issue.START_DATE, RedmineDateParser.SHORT_DATE_FORMAT_V2.get()); addIfSet(writer, "due_date", storage, Issue.DUE_DATE, RedmineDateParser.SHORT_DATE_FORMAT_V2.get()); + addIfSet(writer, "actual_start_date", storage, Issue.ACTUAL_START_DATE, + RedmineDateParser.SHORT_DATE_FORMAT_V2.get()); + addIfSet(writer, "actual_due_date", storage, Issue.ACTUAL_DUE_DATE, + RedmineDateParser.SHORT_DATE_FORMAT_V2.get()); addIfSetIdentifiable(writer, "tracker_id", storage, Issue.TRACKER); addIfSet(writer, "description", storage, Issue.DESCRIPTION); diff --git a/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONParser.java b/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONParser.java index 5f1f563e..441c906a 100644 --- a/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONParser.java +++ b/src/main/java/com/taskadapter/redmineapi/internal/RedmineJSONParser.java @@ -209,6 +209,8 @@ public static Issue parseIssue(JSONObject content) throws JSONException { } result.setStartDate(getDateOrNull(content, "start_date")); result.setDueDate(getDateOrNull(content, "due_date")); + result.setActualStartDate(getDateOrNull(content, "actual_start_date")); + result.setActualDueDate(getDateOrNull(content, "actual_due_date")); result.setTracker(JsonInput.getObjectOrNull(content, "tracker", RedmineJSONParser::parseTracker)); result.setDescription(JsonInput.getStringOrNull(content, "description")); result.setCreatedOn(getDateOrNull(content, "created_on")); diff --git a/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilderTest.java b/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilderTest.java index 5805bab5..c11ed462 100644 --- a/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilderTest.java +++ b/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONBuilderTest.java @@ -8,7 +8,9 @@ import com.taskadapter.redmineapi.bean.Version; import org.junit.Test; +import java.util.Calendar; import java.util.Collections; +import java.util.GregorianCalendar; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.assertTrue; @@ -60,6 +62,16 @@ public void fieldsExplicitlySetToNullAreAddedToIssueJSonAsNull() { assertThat(generatedJSON).contains("\"notes\":null"); } + @Test + public void actualStartAndDueDatesAreSetAndAreAddedToIssueJson() { + Issue issue = new Issue() + .setActualStartDate(new GregorianCalendar(2021, Calendar.APRIL, 23).getTime()) + .setActualDueDate(new GregorianCalendar(2021, Calendar.APRIL, 25).getTime()); + final String generatedJson = RedmineJSONBuilder.toSimpleJSON("some_project_key", issue, RedmineJSONBuilder::writeIssue); + assertThat(generatedJson).contains("\"actual_start_date\":\"2021-04-23\""); + assertThat(generatedJson).contains("\"actual_due_date\":\"2021-04-25\""); + } + @Test public void onlyExplicitlySetFieldsAreAddedToUserJSon() { User user = new User(null) diff --git a/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONParserTest.java b/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONParserTest.java index aadefe2b..2fabde66 100644 --- a/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONParserTest.java +++ b/src/test/java/com/taskadapter/redmineapi/internal/RedmineJSONParserTest.java @@ -22,10 +22,7 @@ import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.List; +import java.util.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -39,6 +36,8 @@ public class RedmineJSONParserTest { private static final String FILE_EMPTY_ISSUES = "issue/issues_empty_list.json"; private static final String REDMINE_ISSUES = "issue/redmine_issues.json"; + private static final String REDMINE_ISSUES_WITH_ACTUAL_START_AND_DUE_DATES = + "issue/redmine_issues_with_actual_start_and_due_dates.json"; @Test public void testParseProject1() throws ParseException, JSONException { @@ -93,12 +92,38 @@ public void testCountIssues() { } + @Test + public void testParseIssuesWithActualStartAndDates() { + try { + List issues = loadRedmineIssuesWithActualStartAndDueDates(); + + Assert.assertEquals(3, issues.size()); + Assert.assertEquals(new GregorianCalendar(2020, Calendar.DECEMBER, 15).getTime(), + issues.get(0).getActualStartDate()); + Assert.assertNull(issues.get(0).getActualDueDate()); + Assert.assertEquals(new GregorianCalendar(2020, Calendar.DECEMBER, 15).getTime(), + issues.get(1).getActualStartDate()); + Assert.assertEquals(new GregorianCalendar(2021, Calendar.FEBRUARY, 4).getTime(), + issues.get(1).getActualDueDate()); + Assert.assertNull(issues.get(2).getActualStartDate()); + Assert.assertNull(issues.get(2).getActualDueDate()); + } catch (Exception e) { + fail(e.getMessage()); + } + } + private List loadRedmine11Issues() throws IOException, JSONException { String json = MyIOUtils.getResourceAsString(REDMINE_ISSUES); return JsonInput.getListOrEmpty(RedmineJSONParser.getResponse(json), "issues", RedmineJSONParser::parseIssue); } + private List loadRedmineIssuesWithActualStartAndDueDates() throws IOException { + String json = MyIOUtils.getResourceAsString(REDMINE_ISSUES_WITH_ACTUAL_START_AND_DUE_DATES); + return JsonInput.getListOrEmpty(RedmineJSONParser.getResponse(json), + "issues", RedmineJSONParser::parseIssue); + } + /* Gson parser is bad at detecting errors :( */ @Ignore @Test diff --git a/src/test/resources/issue/redmine_issues_with_actual_start_and_due_dates.json b/src/test/resources/issue/redmine_issues_with_actual_start_and_due_dates.json new file mode 100644 index 00000000..4ce0e682 --- /dev/null +++ b/src/test/resources/issue/redmine_issues_with_actual_start_and_due_dates.json @@ -0,0 +1,49 @@ +{"total_count":3, "limit":25, "offset":0, "issues":[ + { + "id":72, + "project":{"name":"test project","id":27}, + "tracker":{"name":"Bug","id":1}, + "status":{"name":"New","id":1}, + "priority":{"name":"Normal","id":4}, + "author":{"name":"Redmine Admin","id":1}, + "subject":"testGetIssues: Thu Dec 10 13:59:26 PST 2020", + "description":"", + "start_date":"2020-12-14", + "due_date":"2020-12-15", + "actual_start_date": "2020-12-15", + "done_ratio":0, + "created_on":"2020-12-10T13:59:25Z", + "updated_on":"2020-12-10T13:59:25Z" + }, + { + "id":71, + "project":{"name":"test project","id":26}, + "tracker":{"name":"Bug","id":1}, + "status":{"name":"New","id":1}, + "priority":{"name":"Normal","id":4}, + "author":{"name":"Redmine Admin","id":1}, + "subject":"testGetIssues: Fri Dec 11 13:57:58 PST 2011", + "description":"", + "start_date": "2020-12-16", + "due_date": "2021-01-12", + "actual_start_date": "2020-12-15", + "actual_due_date": "2021-02-04", + "done_ratio":0, + "created_on":"2020-12-11T14:58:59Z", + "updated_on":"2020-12-11T16:57:59Z" + }, + { + "id":70, + "project":{"name":"test project","id":25}, + "tracker":{"name":"Bug","id":1}, + "status":{"name":"New","id":1}, + "priority":{"name":"Normal","id":4}, + "author":{"name":"Redmine Admin","id":1}, + "subject":"Issue Fri Dec 11 13:57:22 PST 2020", + "description":"", + "start_date":"2020-12-12", + "done_ratio":0, + "created_on":"2020-12-11T13:57:20Z", + "updated_on":"2020-12-11T13:57:20Z" + }] +}