diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java index af0f03eff..afba2a9fb 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java @@ -1,5 +1,6 @@ package pro.taskana.rest; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; @@ -27,6 +28,7 @@ import pro.taskana.TaskQuery; import pro.taskana.TaskService; import pro.taskana.TaskState; import pro.taskana.TaskSummary; +import pro.taskana.TimeInterval; import pro.taskana.exceptions.AttachmentPersistenceException; import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.ConcurrencyException; @@ -70,11 +72,17 @@ public class TaskController extends AbstractPagingController { private static final String POR_SYSTEM = "por.system"; private static final String POR_COMPANY = "por.company"; private static final String DUE = "due"; + private static final String DUE_TO = "due-until"; + private static final String DUE_FROM = "due-from"; private static final String PLANNED = "planned"; + private static final String PLANNED_TO = "planned-until"; + private static final String PLANNED_FROM = "planned-from"; private static final String SORT_BY = "sort-by"; private static final String SORT_DIRECTION = "order"; + private static final String INDEFINITE = ""; + private TaskService taskService; private TaskResourceAssembler taskResourceAssembler; @@ -248,6 +256,8 @@ public class TaskController extends AbstractPagingController { LOGGER.debug("Entry to applyFilterParams(taskQuery= {}, params= {})", taskQuery, params); } + checkForIllegalParamCombinations(params); + // apply filters if (params.containsKey(NAME)) { String[] names = extractCommaSeparatedFields(params.get(NAME)); @@ -280,22 +290,7 @@ public class TaskController extends AbstractPagingController { params.remove(WORKBASKET_ID); } if (params.containsKey(WORKBASKET_KEY)) { - String[] domains = null; - if (params.get(DOMAIN) != null) { - domains = extractCommaSeparatedFields(params.get(DOMAIN)); - } - if (domains == null || domains.length != 1) { - throw new InvalidArgumentException( - "workbasket-key requires excactly one domain as second parameter."); - } - String[] workbasketKeys = extractCommaSeparatedFields(params.get(WORKBASKET_KEY)); - KeyDomain[] keyDomains = new KeyDomain[workbasketKeys.length]; - for (int i = 0; i < workbasketKeys.length; i++) { - keyDomains[i] = new KeyDomain(workbasketKeys[i], domains[0]); - } - taskQuery.workbasketKeyDomainIn(keyDomains); - params.remove(WORKBASKET_KEY); - params.remove(DOMAIN); + updateTaskQueryWithWorkbasketKey(taskQuery,params); } if (params.containsKey(OWNER)) { String[] owners = extractCommaSeparatedFields(params.get(OWNER)); @@ -330,6 +325,43 @@ public class TaskController extends AbstractPagingController { params.remove(POR_VALUE); } + if (params.containsKey(PLANNED)) { + updateTaskQueryWithPlannedOrDueTimeIntervals(taskQuery, params, PLANNED); + } + + if (params.containsKey(DUE)) { + updateTaskQueryWithPlannedOrDueTimeIntervals(taskQuery, params, DUE); + } + + if (params.containsKey(PLANNED_FROM) && params.containsKey(PLANNED_TO)) { + updateTaskQueryWithPlannedOrDueTimeInterval(taskQuery, params, PLANNED_FROM, PLANNED_TO); + + } else if (params.containsKey(PLANNED_FROM) && !params.containsKey(PLANNED_TO)) { + + TimeInterval timeInterval = createIndefiniteTimeIntervalFromParam(params, PLANNED_FROM); + updateTaskQueryWithIndefiniteTimeInterval(taskQuery, params, PLANNED_FROM, timeInterval); + + } else if (!params.containsKey(PLANNED_FROM) && params.containsKey(PLANNED_TO)) { + + TimeInterval timeInterval = createIndefiniteTimeIntervalFromParam(params, PLANNED_TO); + updateTaskQueryWithIndefiniteTimeInterval(taskQuery, params, PLANNED_TO, timeInterval); + } + + if (params.containsKey(DUE_FROM) && params.containsKey(DUE_TO)) { + updateTaskQueryWithPlannedOrDueTimeInterval(taskQuery, params, DUE_FROM, DUE_TO); + + } else if (params.containsKey(DUE_FROM) && !params.containsKey(DUE_TO)) { + + TimeInterval indefiniteTimeInterval = createIndefiniteTimeIntervalFromParam(params, DUE_FROM); + updateTaskQueryWithIndefiniteTimeInterval( + taskQuery, params, DUE_FROM, indefiniteTimeInterval); + + } else if (!params.containsKey(DUE_FROM) && params.containsKey(DUE_TO)) { + + TimeInterval timeInterval = createIndefiniteTimeIntervalFromParam(params, DUE_TO); + updateTaskQueryWithIndefiniteTimeInterval(taskQuery, params, DUE_TO, timeInterval); + } + if (LOGGER.isDebugEnabled()) { LOGGER.debug("Exit from applyFilterParams(), returning {}", taskQuery); } @@ -337,6 +369,151 @@ public class TaskController extends AbstractPagingController { return taskQuery; } + private void updateTaskQueryWithWorkbasketKey(TaskQuery taskQuery, + MultiValueMap params) + throws InvalidArgumentException { + + String[] domains = null; + if (params.get(DOMAIN) != null) { + domains = extractCommaSeparatedFields(params.get(DOMAIN)); + } + if (domains == null || domains.length != 1) { + throw new InvalidArgumentException( + "workbasket-key requires excactly one domain as second parameter."); + } + String[] workbasketKeys = extractCommaSeparatedFields(params.get(WORKBASKET_KEY)); + KeyDomain[] keyDomains = new KeyDomain[workbasketKeys.length]; + for (int i = 0; i < workbasketKeys.length; i++) { + keyDomains[i] = new KeyDomain(workbasketKeys[i], domains[0]); + } + taskQuery.workbasketKeyDomainIn(keyDomains); + params.remove(WORKBASKET_KEY); + params.remove(DOMAIN); + + } + + private void checkForIllegalParamCombinations(MultiValueMap params) { + + if (params.containsKey(PLANNED) + && (params.containsKey(PLANNED_FROM) || params.containsKey(PLANNED_TO))) { + + throw new IllegalArgumentException( + "It is prohibited to use the param \"" + + PLANNED + + "\" in combination with the params \"" + + PLANNED_FROM + + "\" and / or \"" + + PLANNED_TO + + "\""); + } + + if (params.containsKey(DUE) && (params.containsKey(DUE_FROM) || params.containsKey(DUE_TO))) { + + throw new IllegalArgumentException( + "It is prohibited to use the param \"" + + DUE + + "\" in combination with the params \"" + + PLANNED_FROM + + "\" and / or \"" + + PLANNED_TO + + "\""); + } + } + + private void updateTaskQueryWithIndefiniteTimeInterval( + TaskQuery taskQuery, + MultiValueMap params, + String param, + TimeInterval timeInterval) { + + if (param.equals(PLANNED_FROM) || param.equals(PLANNED_TO)) { + taskQuery.plannedWithin(timeInterval); + + } else { + taskQuery.dueWithin(timeInterval); + } + params.remove(param); + } + + private TimeInterval createIndefiniteTimeIntervalFromParam( + MultiValueMap params, String param) { + + if (param.equals(PLANNED_FROM) || param.equals(DUE_FROM)) { + + return new TimeInterval(Instant.parse(params.get(param).get(0)), null); + + } else { + + return new TimeInterval(null, Instant.parse(params.get(param).get(0))); + } + } + + private void updateTaskQueryWithPlannedOrDueTimeInterval( + TaskQuery taskQuery, + MultiValueMap params, + String plannedFromOrDueFrom, + String plannedToOrDueTo) { + + TimeInterval timeInterval = + new TimeInterval( + Instant.parse(params.get(plannedFromOrDueFrom).get(0)), + Instant.parse(params.get(plannedToOrDueTo).get(0))); + + taskQuery.plannedWithin(timeInterval); + + params.remove(plannedToOrDueTo); + params.remove(plannedFromOrDueFrom); + } + + private void updateTaskQueryWithPlannedOrDueTimeIntervals( + TaskQuery taskQuery, MultiValueMap params, String plannedOrDue) { + + String[] instants = extractCommaSeparatedFields(params.get(plannedOrDue)); + + TimeInterval[] timeIntervals = extractTimeIntervals(instants); + + taskQuery.plannedWithin(timeIntervals); + + params.remove(plannedOrDue); + } + + private TimeInterval[] extractTimeIntervals(String[] instants) { + + List timeIntervalsList = new ArrayList<>(); + + for (int i = 0; i < instants.length - 1; i += 2) { + + TimeInterval timeInterval = determineTimeInterval(instants, i); + + if (timeInterval != null) { + + timeIntervalsList.add(timeInterval); + } + } + + TimeInterval[] timeIntervalArray = new TimeInterval[timeIntervalsList.size()]; + + return timeIntervalsList.toArray(timeIntervalArray); + } + + private TimeInterval determineTimeInterval(String[] instants, int i) { + + if (!instants[i].equals(INDEFINITE) && !instants[i + 1].equals(INDEFINITE)) { + + return new TimeInterval(Instant.parse(instants[i]), Instant.parse(instants[i + 1])); + + } else if (instants[i].equals(INDEFINITE) && !instants[i + 1].equals(INDEFINITE)) { + + return new TimeInterval(null, Instant.parse(instants[i + 1])); + + } else if (!instants[i].equals(INDEFINITE) && instants[i + 1].equals(INDEFINITE)) { + + return new TimeInterval(Instant.parse(instants[i]), null); + } + + return null; + } + private TaskQuery applySortingParams(TaskQuery taskQuery, MultiValueMap params) throws InvalidArgumentException { if (LOGGER.isDebugEnabled()) { diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java index 0686dad34..0c61e5835 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java @@ -1,12 +1,7 @@ package pro.taskana.rest; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; @@ -19,7 +14,9 @@ import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.time.Instant; +import java.time.temporal.ChronoUnit; import javax.sql.DataSource; +import org.assertj.core.api.Fail; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -54,6 +51,7 @@ class TaskControllerIntTest { public String schemaName; @Autowired RestHelper restHelper; + @Autowired private DataSource dataSource; @BeforeAll @@ -74,8 +72,8 @@ class TaskControllerIntTest { HttpMethod.GET, restHelper.defaultRequest(), ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertEquals(25, response.getBody().getContent().size()); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(25); } @Test @@ -87,8 +85,188 @@ class TaskControllerIntTest { HttpMethod.GET, restHelper.defaultRequest(), ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertEquals(22, response.getBody().getContent().size()); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(22); + } + + @Test + void testGetAllTasksByWorkbasketIdWithinMultiplePlannedTimeIntervals() { + + Instant firstInstant = Instant.now().minus(7, ChronoUnit.DAYS); + Instant secondInstant = Instant.now().minus(10, ChronoUnit.DAYS); + Instant thirdInstant = Instant.now().minus(10, ChronoUnit.DAYS); + Instant fourthInstant = Instant.now().minus(11, ChronoUnit.DAYS); + + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&planned=" + + firstInstant + + ",," + + secondInstant + + "," + + thirdInstant + + "," + + "," + + fourthInstant + + "&sort-by=planned", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(6); + } + + @Test + void testGetAllTasksByWorkbasketIdWithinSinglePlannedTimeInterval() { + + Instant plannedFromInstant = Instant.now().minus(6, ChronoUnit.DAYS); + Instant plannedToInstant = Instant.now().minus(3, ChronoUnit.DAYS); + + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&planned-from=" + + plannedFromInstant + + "&planned-until=" + + plannedToInstant + + "&sort-by=planned", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(3); + } + + @Test + void testGetAllTasksByWorkbasketIdWithinSingleIndefinitePlannedTimeInterval() { + + Instant plannedFromInstant = Instant.now().minus(6, ChronoUnit.DAYS); + + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&planned-from=" + + plannedFromInstant + + "&sort-by=planned", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(4); + } + + @Test + void testGetAllTasksByWorkbasketIdWithInvalidPlannedParamsCombination() { + HttpClientErrorException e = + Assertions.assertThrows( + HttpClientErrorException.class, + () -> + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&planned=2020-01-22T09:44:47.453Z,," + + "2020-01-19T07:44:47.453Z,2020-01-19T19:44:47.453Z," + + ",2020-01-18T09:44:47.453Z" + + "&planned-from=2020-01-19T07:44:47.453Z" + + "&sort-by=planned", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class))); + assertThat(HttpStatus.BAD_REQUEST).isEqualTo(e.getStatusCode()); + } + + @Test + void testGetAllTasksByWorkbasketIdWithinMultipleDueTimeIntervals() { + + Instant firstInstant = Instant.now().minus(7, ChronoUnit.DAYS); + Instant secondInstant = Instant.now().minus(10, ChronoUnit.DAYS); + Instant thirdInstant = Instant.now().minus(10, ChronoUnit.DAYS); + Instant fourthInstant = Instant.now().minus(11, ChronoUnit.DAYS); + + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&due=" + + firstInstant + + ",," + + secondInstant + + "," + + thirdInstant + + "," + + "," + + fourthInstant + + "&sort-by=due", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(6); + } + + @Test + void testGetAllTasksByWorkbasketIdWithinSingleDueTimeInterval() { + + Instant dueFromInstant = Instant.now().minus(8, ChronoUnit.DAYS); + Instant dueToInstant = Instant.now().minus(3, ChronoUnit.DAYS); + + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&due-from=" + + dueFromInstant + + "&due-until=" + + dueToInstant + + "&sort-by=due", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(9); + } + + @Test + void testGetAllTasksByWorkbasketIdWithinSingleIndefiniteDueTimeInterval() { + + Instant dueToInstant = Instant.now().minus(1, ChronoUnit.DAYS); + + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&due-until=" + + dueToInstant + + "&sort-by=due", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(6); + } + + @Test + void testGetAllTasksByWorkbasketIdWithInvalidDueParamsCombination() { + HttpClientErrorException e = + Assertions.assertThrows( + HttpClientErrorException.class, + () -> + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?workbasket-id=WBI:100000000000000000000000000000000001" + + "&due=2020-01-22T09:44:47.453Z,," + + "2020-01-19T07:44:47.453Z,2020-01-19T19:44:47.453Z," + + ",2020-01-18T09:44:47.453Z" + + "&due-from=2020-01-19T07:44:47.453Z" + + "&sort-by=planned", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class))); + assertThat(HttpStatus.BAD_REQUEST).isEqualTo(e.getStatusCode()); } @Test @@ -102,8 +280,8 @@ class TaskControllerIntTest { HttpMethod.GET, request, ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertEquals(20, response.getBody().getContent().size()); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(20); } @Test @@ -124,7 +302,7 @@ class TaskControllerIntTest { request, ParameterizedTypeReference.forType(TaskSummaryListResource.class)); }); - assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode()); + assertThat(HttpStatus.BAD_REQUEST).isEqualTo(e.getStatusCode()); } @Test @@ -135,8 +313,8 @@ class TaskControllerIntTest { HttpMethod.GET, new HttpEntity<>(restHelper.getHeadersAdmin()), ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertEquals(73, response.getBody().getContent().size()); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(73); } @Test @@ -148,14 +326,15 @@ class TaskControllerIntTest { HttpMethod.GET, restHelper.defaultRequest(), ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertTrue( - response - .getBody() - .getLink(Link.REL_SELF) - .getHref() - .endsWith( - "/api/v1/tasks?por.type=VNR&por.value=22334455&sort-by=por.value&order=desc")); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat( + response + .getBody() + .getLink(Link.REL_SELF) + .getHref() + .endsWith( + "/api/v1/tasks?por.type=VNR&por.value=22334455&sort-by=por.value&order=desc")) + .isTrue(); } @Test @@ -166,10 +345,10 @@ class TaskControllerIntTest { HttpMethod.GET, restHelper.defaultRequest(), ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - fail(); + Fail.fail(""); } catch (HttpClientErrorException e) { - assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode()); - assertTrue(e.getResponseBodyAsString().contains("[invalid]")); + assertThat(e.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(e.getResponseBodyAsString().contains("[invalid]")).isTrue(); } } @@ -184,23 +363,25 @@ class TaskControllerIntTest { HttpMethod.GET, request, ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertEquals(1, response.getBody().getContent().size()); - assertTrue(response.getBody().getLink(Link.REL_LAST).getHref().contains("page=14")); - assertEquals( - "TKI:100000000000000000000000000000000000", - response.getBody().getContent().iterator().next().getTaskId()); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertTrue( - response - .getBody() - .getLink(Link.REL_SELF) - .getHref() - .endsWith( - "/api/v1/tasks?" - + "state=READY,CLAIMED&sort-by=por.value&order=desc&page=15&page-size=5")); - assertNotNull(response.getBody().getLink(Link.REL_FIRST)); - assertNotNull(response.getBody().getLink(Link.REL_LAST)); - assertNotNull(response.getBody().getLink(Link.REL_PREVIOUS)); + assertThat(response.getBody().getContent()).hasSize(1); + assertThat(response.getBody().getLink(Link.REL_LAST).getHref().contains("page=14")).isTrue(); + assertThat("TKI:100000000000000000000000000000000000") + .isEqualTo(response.getBody().getContent().iterator().next().getTaskId()); + + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat( + response + .getBody() + .getLink(Link.REL_SELF) + .getHref() + .endsWith( + "/api/v1/tasks?" + + "state=READY,CLAIMED&sort-by=por.value&order=desc&page=15&page-size=5")) + .isTrue(); + + assertThat(response.getBody().getLink(Link.REL_FIRST)).isNotNull(); + assertThat(response.getBody().getLink(Link.REL_LAST)).isNotNull(); + assertThat(response.getBody().getLink(Link.REL_PREVIOUS)).isNotNull(); } @Test @@ -220,7 +401,7 @@ class TaskControllerIntTest { HttpMethod.GET, request, ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertEquals(25, response.getBody().getContent().size()); + assertThat(response.getBody().getContent()).hasSize(25); response = template.exchange( @@ -228,21 +409,23 @@ class TaskControllerIntTest { HttpMethod.GET, request, ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertEquals(5, response.getBody().getContent().size()); - assertTrue(response.getBody().getLink(Link.REL_LAST).getHref().contains("page=5")); - assertEquals( - "TKI:000000000000000000000000000000000023", - response.getBody().getContent().iterator().next().getTaskId()); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertTrue( - response - .getBody() - .getLink(Link.REL_SELF) - .getHref() - .endsWith("/api/v1/tasks?sort-by=due&order=desc&page=5&page-size=5")); - assertNotNull(response.getBody().getLink(Link.REL_FIRST)); - assertNotNull(response.getBody().getLink(Link.REL_LAST)); - assertNotNull(response.getBody().getLink(Link.REL_PREVIOUS)); + assertThat(response.getBody().getContent()).hasSize(5); + assertThat(response.getBody().getLink(Link.REL_LAST).getHref().contains("page=5")).isTrue(); + assertThat("TKI:000000000000000000000000000000000023") + .isEqualTo(response.getBody().getContent().iterator().next().getTaskId()); + + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat( + response + .getBody() + .getLink(Link.REL_SELF) + .getHref() + .endsWith("/api/v1/tasks?sort-by=due&order=desc&page=5&page-size=5")) + .isTrue(); + + assertThat(response.getBody().getLink(Link.REL_FIRST)).isNotNull(); + assertThat(response.getBody().getLink(Link.REL_LAST)).isNotNull(); + assertThat(response.getBody().getLink(Link.REL_PREVIOUS)).isNotNull(); } @Test @@ -263,23 +446,26 @@ class TaskControllerIntTest { HttpMethod.GET, request, ParameterizedTypeReference.forType(TaskSummaryListResource.class)); - assertEquals(1, response.getBody().getContent().size()); - assertEquals( - "TKI:000000000000000000000000000000000013", - response.getBody().getContent().iterator().next().getTaskId()); - assertNotNull(response.getBody().getLink(Link.REL_SELF)); - assertTrue( - response - .getBody() - .getLink(Link.REL_SELF) - .getHref() - .endsWith( - "/api/v1/tasks?por.company=00&por.system=PASystem&por.instance=00&" - + "por.type=VNR&por.value=22334455&sort-by=por.type&order=asc&" - + "page=2&page-size=5")); - assertNotNull(response.getBody().getLink(Link.REL_FIRST)); - assertNotNull(response.getBody().getLink(Link.REL_LAST)); - assertNotNull(response.getBody().getLink(Link.REL_PREVIOUS)); + assertThat(response.getBody().getContent()).hasSize(1); + assertThat("TKI:000000000000000000000000000000000013") + .isEqualTo(response.getBody().getContent().iterator().next().getTaskId()); + + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + + assertThat( + response + .getBody() + .getLink(Link.REL_SELF) + .getHref() + .endsWith( + "/api/v1/tasks?por.company=00&por.system=PASystem&por.instance=00&" + + "por.type=VNR&por.value=22334455&sort-by=por.type&order=asc&" + + "page=2&page-size=5")) + .isTrue(); + + assertThat(response.getBody().getLink(Link.REL_FIRST)).isNotNull(); + assertThat(response.getBody().getLink(Link.REL_LAST)).isNotNull(); + assertThat(response.getBody().getLink(Link.REL_PREVIOUS)).isNotNull(); } @Test @@ -289,7 +475,7 @@ class TaskControllerIntTest { HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setRequestProperty("Authorization", "Basic YWRtaW46YWRtaW4="); - assertEquals(200, con.getResponseCode()); + assertThat(con.getResponseCode()).isEqualTo(200); final ObjectMapper objectMapper = new ObjectMapper(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), UTF_8)); @@ -303,8 +489,8 @@ class TaskControllerIntTest { String response = content.toString(); JsonNode jsonNode = objectMapper.readTree(response); String created = jsonNode.get("created").asText(); - assertFalse(response.contains("\"attachments\":[]")); - assertTrue(created.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z")); + assertThat(response.contains("\"attachments\":[]")).isFalse(); + assertThat(created.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z")).isTrue(); } @Test @@ -313,7 +499,7 @@ class TaskControllerIntTest { HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setRequestProperty("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); - assertEquals(200, con.getResponseCode()); + assertThat(con.getResponseCode()).isEqualTo(200); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), UTF_8)); String inputLine; @@ -334,14 +520,15 @@ class TaskControllerIntTest { out.write(content.toString()); out.flush(); out.close(); - assertEquals(200, con.getResponseCode()); + assertThat(con.getResponseCode()).isEqualTo(200); + con.disconnect(); url = new URL(restHelper.toUrl("/api/v1/tasks/TKI:100000000000000000000000000000000000")); con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setRequestProperty("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); - assertEquals(200, con.getResponseCode()); + assertThat(con.getResponseCode()).isEqualTo(200); in = new BufferedReader(new InputStreamReader(con.getInputStream(), UTF_8)); content = new StringBuffer(); @@ -356,7 +543,7 @@ class TaskControllerIntTest { TaskResource originalTaskObject = mapper.readValue(originalTask, TaskResource.class); TaskResource updatedTaskObject = mapper.readValue(updatedTask, TaskResource.class); - assertNotEquals(originalTaskObject.getModified(), updatedTaskObject.getModified()); + assertThat(updatedTaskObject.getModified()).isNotEqualTo(originalTaskObject.getModified()); } @Test @@ -369,12 +556,13 @@ class TaskControllerIntTest { HttpMethod.POST, new HttpEntity<>(taskResource, restHelper.getHeaders()), ParameterizedTypeReference.forType(TaskResource.class)); - assertEquals(responseCreate.getStatusCode(), HttpStatus.CREATED); - assertNotNull(responseCreate.getBody()); + assertThat(HttpStatus.CREATED).isEqualTo(responseCreate.getStatusCode()); + assertThat(responseCreate.getBody()).isNotNull(); String taskIdOfCreatedTask = responseCreate.getBody().getTaskId(); - assertNotNull(taskIdOfCreatedTask); - assertTrue(taskIdOfCreatedTask.startsWith("TKI:")); + + assertThat(taskIdOfCreatedTask).isNotNull(); + assertThat(taskIdOfCreatedTask.startsWith("TKI:")).isTrue(); ResponseEntity responseDeleted = template.exchange( @@ -383,7 +571,7 @@ class TaskControllerIntTest { new HttpEntity<>(restHelper.getHeadersAdmin()), ParameterizedTypeReference.forType(Void.class)); - assertEquals(HttpStatus.NO_CONTENT, responseDeleted.getStatusCode()); + assertThat(responseDeleted.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); } /** @@ -426,7 +614,8 @@ class TaskControllerIntTest { out.write(taskToCreateJson); out.flush(); out.close(); - assertEquals(400, con.getResponseCode()); + assertThat(con.getResponseCode()).isEqualTo(400); + con.disconnect(); final String taskToCreateJson2 = @@ -446,7 +635,8 @@ class TaskControllerIntTest { out.write(taskToCreateJson2); out.flush(); out.close(); - assertEquals(400, con.getResponseCode()); + assertThat(con.getResponseCode()).isEqualTo(400); + con.disconnect(); } diff --git a/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc b/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc index caed02234..d89bbfb69 100644 --- a/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc +++ b/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc @@ -115,6 +115,26 @@ include::{snippets}/GetAllTasksDocTest/response-fields.adoc[] The list generated in the response can be filtered using following parameters in the uri: + name | priority | state | classification.key | workbasket-id | {workbasket-key , domain} | + owner | por.company | por.system | por.instance | por.type | por.value + +planned | planned-from | planned-until | due | due-from | due-until + + +If it is sufficient to filter the list with a single time interval, use the parameters + +planned-from/due-from and planned-until/due-until. + +Leave one of them out of the request, if you want to have an open interval. + + +If it is required to filter the list with multiple planned or due time intervals, the parameters + + planned / due can be used as follows instead: + + + - provide a string with an even number of instants separated by a ",". Time intervals will be + + determined in pairs of those instants. + + + - in case of a required open interval, just give one of the arguments of the pair an empty string + + like this: + + + planned = {Instant1},{Instant2},,{Instant3} this will create two intervals, one between Instant1 + + and Instant2 as well as an open interval between the beginning of time and Instant3 + + + Note that it is prohibited to use the planned / due parameter in combination with the + + planned-from/due-from and/or planned-until/due-until parameters + It can also be sorted by using this set of parameters: + sortBy = { classification.key | por.type | por.value | state | name| due | planned | priority } | order={ desc | asc }