JAMES-2272 Webadmin should expose a task management API
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/d9d73f80 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/d9d73f80 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/d9d73f80 Branch: refs/heads/master Commit: d9d73f801fa61dc311fce2a3566994bc32068a22 Parents: 1b7e8d7 Author: benwa <[email protected]> Authored: Wed Dec 27 12:16:17 2017 +0700 Committer: benwa <[email protected]> Committed: Thu Jan 4 15:03:36 2018 +0700 ---------------------------------------------------------------------- .../james/modules/server/TaskRoutesModule.java | 37 +++ .../modules/server/WebAdminServerModule.java | 2 + server/protocols/webadmin/webadmin-core/pom.xml | 9 + .../james/webadmin/dto/ExecutionDetailsDto.java | 95 +++++++ .../apache/james/webadmin/dto/TaskIdDto.java | 40 +++ .../james/webadmin/routes/TasksRoutes.java | 127 +++++++++ .../james/webadmin/utils/ErrorResponder.java | 1 + .../james/webadmin/utils/JsonExtractor.java | 6 +- .../james/webadmin/routes/TasksRoutesTest.java | 277 +++++++++++++++++++ 9 files changed, 592 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java new file mode 100644 index 0000000..390bb17 --- /dev/null +++ b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java @@ -0,0 +1,37 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.modules.server; + +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.routes.TasksRoutes; + +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; +import com.google.inject.multibindings.Multibinder; + +public class TaskRoutesModule extends AbstractModule { + @Override + protected void configure() { + bind(TasksRoutes.class).in(Scopes.SINGLETON); + + Multibinder<Routes> routesMultibinder = Multibinder.newSetBinder(binder(), Routes.class); + routesMultibinder.addBinding().to(TasksRoutes.class); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java index 1a140ee..6142be1 100644 --- a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java +++ b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java @@ -69,6 +69,8 @@ public class WebAdminServerModule extends AbstractModule { @Override protected void configure() { + install(new TaskRoutesModule()); + bind(JsonTransformer.class).in(Scopes.SINGLETON); bind(WebAdminServer.class).in(Scopes.SINGLETON); http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/pom.xml b/server/protocols/webadmin/webadmin-core/pom.xml index 56bcda1..e86562d 100644 --- a/server/protocols/webadmin/webadmin-core/pom.xml +++ b/server/protocols/webadmin/webadmin-core/pom.xml @@ -55,6 +55,10 @@ <scope>test</scope> </dependency> <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>james-server-task</artifactId> + </dependency> + <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> @@ -75,6 +79,11 @@ <artifactId>guava</artifactId> </dependency> <dependency> + <groupId>com.jayway.restassured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java new file mode 100644 index 0000000..7069e8b --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java @@ -0,0 +1,95 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.webadmin.dto; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.apache.james.task.TaskExecutionDetails; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.github.steveash.guavate.Guavate; + +public class ExecutionDetailsDto { + public static List<ExecutionDetailsDto> from(List<TaskExecutionDetails> tasksDetails) { + return tasksDetails.stream() + .map(ExecutionDetailsDto::new) + .collect(Guavate.toImmutableList()); + } + + public static ExecutionDetailsDto from(TaskExecutionDetails taskDetails) { + return new ExecutionDetailsDto(taskDetails); + } + + private final TaskExecutionDetails executionDetails; + + private ExecutionDetailsDto(TaskExecutionDetails executionDetails) { + this.executionDetails = executionDetails; + } + + public UUID getTaskId() { + return executionDetails.getTaskId().getValue(); + } + + public String getType() { + return executionDetails.getType(); + } + + public String getStatus() { + return executionDetails.getStatus().getValue(); + } + + public Optional<TaskExecutionDetails.AdditionalInformation> getAdditionalInformation() { + return executionDetails.getAdditionalInformation(); + } + + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + public Optional<ZonedDateTime> getSubmitDate() { + return executionDetails.getSubmitDate(); + } + + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + public Optional<ZonedDateTime> getStartedDate() { + return executionDetails.getStartedDate(); + } + + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + public Optional<ZonedDateTime> getCompletedDate() { + return executionDetails.getCompletedDate(); + } + + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + public Optional<ZonedDateTime> getCanceledDate() { + return executionDetails.getCanceledDate(); + } + + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + public Optional<ZonedDateTime> getFailedDate() { + return executionDetails.getFailedDate(); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java new file mode 100644 index 0000000..231d051 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java @@ -0,0 +1,40 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.webadmin.dto; + +import java.util.UUID; + +import org.apache.james.task.TaskId; + +public class TaskIdDto { + public static TaskIdDto from(TaskId id) { + return new TaskIdDto(id.getValue()); + } + + private final UUID uuid; + + public TaskIdDto(UUID uuid) { + this.uuid = uuid; + } + + public UUID getTaskId() { + return uuid; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java new file mode 100644 index 0000000..99bd2df --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java @@ -0,0 +1,127 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.webadmin.routes; + +import java.util.Optional; +import java.util.UUID; +import java.util.function.Supplier; + +import javax.inject.Inject; + +import org.apache.james.task.TaskExecutionDetails; +import org.apache.james.task.TaskId; +import org.apache.james.task.TaskManager; +import org.apache.james.task.TaskNotFoundException; +import org.apache.james.webadmin.Constants; +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.dto.ExecutionDetailsDto; +import org.apache.james.webadmin.utils.ErrorResponder; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.eclipse.jetty.http.HttpStatus; + +import spark.Request; +import spark.Response; +import spark.Service; + +public class TasksRoutes implements Routes { + public static final String BASE = "/tasks"; + private final TaskManager taskManager; + private final JsonTransformer jsonTransformer; + + @Inject + public TasksRoutes(TaskManager taskManager, JsonTransformer jsonTransformer) { + this.taskManager = taskManager; + this.jsonTransformer = jsonTransformer; + } + + @Override + public void define(Service service) { + service.get(BASE + "/:id", this::getStatus, jsonTransformer); + + service.get(BASE + "/:id/await", this::await, jsonTransformer); + + service.delete(BASE + "/:id", this::cancel, jsonTransformer); + + service.get(BASE, this::list, jsonTransformer); + } + + private Object list(Request req, Response response) { + try { + return ExecutionDetailsDto.from( + Optional.ofNullable(req.queryParams("status")) + .map(TaskManager.Status::fromString) + .map(taskManager::list) + .orElse(taskManager.list())); + } catch (IllegalArgumentException e) { + throw ErrorResponder.builder() + .statusCode(HttpStatus.BAD_REQUEST_400) + .type(ErrorResponder.ErrorType.INVALID_ARGUMENT) + .cause(e) + .message("Invalid status query parameter") + .haltError(); + } + } + + private Object getStatus(Request req, Response response) { + TaskId taskId = getTaskId(req); + return respondStatus(taskId, + () -> taskManager.getExecutionDetails(getTaskId(req))); + } + + private Object await(Request req, Response response) { + TaskId taskId = getTaskId(req); + return respondStatus(taskId, + () -> taskManager.await(getTaskId(req))); + } + + private Object respondStatus(TaskId taskId, Supplier<TaskExecutionDetails> executionDetailsSupplier) { + try { + TaskExecutionDetails executionDetails = executionDetailsSupplier.get(); + return ExecutionDetailsDto.from(executionDetails); + } catch (TaskNotFoundException e) { + throw ErrorResponder.builder() + .message(String.format("%s can not be found", taskId.getValue())) + .statusCode(HttpStatus.NOT_FOUND_404) + .type(ErrorResponder.ErrorType.NOT_FOUND) + .haltError(); + } + } + + private Object cancel(Request req, Response response) { + TaskId taskId = getTaskId(req); + taskManager.cancel(taskId); + response.status(HttpStatus.NO_CONTENT_204); + return Constants.EMPTY_BODY; + } + + private TaskId getTaskId(Request req) { + try { + String id = req.params("id"); + return new TaskId(UUID.fromString(id)); + } catch (Exception e) { + throw ErrorResponder.builder() + .statusCode(HttpStatus.BAD_REQUEST_400) + .cause(e) + .type(ErrorResponder.ErrorType.INVALID_ARGUMENT) + .message("Invalid task id") + .haltError(); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java index c87f0f9..b02883d 100644 --- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java @@ -33,6 +33,7 @@ import spark.HaltException; public class ErrorResponder { public enum ErrorType { INVALID_ARGUMENT("InvalidArgument"), + NOT_FOUND("notFound"), WRONG_STATE("WrongState"), SERVER_ERROR("ServerError"); http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java index f4eb420..02ebe50 100644 --- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java @@ -22,6 +22,7 @@ package org.apache.james.webadmin.utils; import java.io.IOException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; public class JsonExtractor<Request> { @@ -29,14 +30,15 @@ public class JsonExtractor<Request> { private final Class<Request> type; public JsonExtractor(Class<Request> type) { - this.objectMapper = new ObjectMapper(); + this.objectMapper = new ObjectMapper() + .registerModule(new Jdk8Module()); this.type = type; } public Request parse(String text) throws JsonExtractException { try { return objectMapper.readValue(text, type); - } catch (IOException e) { + } catch (IOException | IllegalArgumentException e) { throw new JsonExtractException(e); } } http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java new file mode 100644 index 0000000..5403349 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java @@ -0,0 +1,277 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.webadmin.routes; + +import static com.jayway.restassured.RestAssured.given; +import static com.jayway.restassured.RestAssured.when; +import static com.jayway.restassured.RestAssured.with; +import static com.jayway.restassured.config.EncoderConfig.encoderConfig; +import static com.jayway.restassured.config.RestAssuredConfig.newConfig; +import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +import java.util.UUID; +import java.util.concurrent.CountDownLatch; + +import org.apache.james.metrics.logger.DefaultMetricFactory; +import org.apache.james.task.MemoryTaskManager; +import org.apache.james.task.Task; +import org.apache.james.task.TaskId; +import org.apache.james.task.TaskManager; +import org.apache.james.webadmin.WebAdminServer; +import org.apache.james.webadmin.WebAdminUtils; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.eclipse.jetty.http.HttpStatus; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.base.Charsets; +import com.google.common.base.Throwables; +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +public class TasksRoutesTest { + + private MemoryTaskManager taskManager; + private WebAdminServer webAdminServer; + + @Before + public void setUp() throws Exception { + taskManager = new MemoryTaskManager(); + + webAdminServer = WebAdminUtils.createWebAdminServer( + new DefaultMetricFactory(), + new TasksRoutes(taskManager, new JsonTransformer())); + + webAdminServer.configure(NO_CONFIGURATION); + webAdminServer.await(); + + RestAssured.requestSpecification = new RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setAccept(ContentType.JSON) + .setBasePath(TasksRoutes.BASE) + .setPort(webAdminServer.getPort().get().getValue()) + .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8))) + .build(); + } + + @After + public void tearDown() { + taskManager.stop(); + webAdminServer.destroy(); + } + + @Test + public void listShouldReturnEmptyWhenNoTask() { + when() + .get() + .then() + .body("", hasSize(0)); + } + + @Test + public void listShouldReturnTaskDetailsWhenTaskInProgress() { + TaskId taskId = taskManager.submit(() -> { + await(); + return Task.Result.COMPLETED; + }); + + when() + .get() + .then() + .statusCode(HttpStatus.OK_200) + .body("", hasSize(1)) + .body("[0].status", is(TaskManager.Status.IN_PROGRESS.getValue())) + .body("[0].taskId", is(taskId.getValue().toString())) + .body("[0].class", is(not(empty()))); + } + + private void await() { + try { + new CountDownLatch(1).await(); + } catch (InterruptedException e) { + Throwables.propagate(e); + } + } + + @Test + public void listShouldListTaskWhenStatusFilter() { + TaskId taskId = taskManager.submit(() -> { + await(); + return Task.Result.COMPLETED; + }); + + given() + .param("status", TaskManager.Status.IN_PROGRESS.getValue()) + .when() + .get() + .then() + .statusCode(HttpStatus.OK_200) + .body("", hasSize(1)) + .body("[0].status", is(TaskManager.Status.IN_PROGRESS.getValue())) + .body("[0].taskId", is(taskId.getValue().toString())) + .body("[0].type", is(Task.UNKNOWN)); + } + + @Test + public void listShouldReturnEmptyWhenNonMatchingStatusFilter() { + taskManager.submit(() -> { + await(); + return Task.Result.COMPLETED; + }); + + given() + .param("status", TaskManager.Status.WAITING.getValue()) + .when() + .get() + .then() + .statusCode(HttpStatus.OK_200) + .body("", hasSize(0)); + } + + @Test + public void getShouldReturnTaskDetails() { + TaskId taskId = taskManager.submit(() -> { + await(); + return Task.Result.COMPLETED; + }); + + when() + .get("/" + taskId.getValue()) + .then() + .statusCode(HttpStatus.OK_200) + .body("status", is("inProgress")); + } + + @Test + public void getAwaitShouldAwaitTaskCompletion() { + TaskId taskId = taskManager.submit(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw Throwables.propagate(e); + } + return Task.Result.COMPLETED; + }); + + when() + .get("/" + taskId.getValue() + "/await") + .then() + .statusCode(HttpStatus.OK_200) + .body("status", is("completed")); + } + + @Test + public void deleteShouldReturnOk() { + TaskId taskId = taskManager.submit(() -> { + await(); + return Task.Result.COMPLETED; + }); + + when() + .delete("/" + taskId.getValue()) + .then() + .statusCode(HttpStatus.NO_CONTENT_204); + } + + @Test + public void deleteShouldCancelMatchingTask() { + TaskId taskId = taskManager.submit(() -> { + await(); + return Task.Result.COMPLETED; + }); + + with() + .delete("/" + taskId.getValue()); + + when() + .get("/" + taskId.getValue()) + .then() + .statusCode(HttpStatus.OK_200) + .body("status", is("canceled")); + } + + @Test + public void getShouldReturnNotFoundWhenIdDoesNotExist() { + String taskId = UUID.randomUUID().toString(); + + when() + .get("/" + taskId) + .then() + .statusCode(HttpStatus.NOT_FOUND_404) + .body("statusCode", is(HttpStatus.NOT_FOUND_404)) + .body("type", is("notFound")) + .body("message", is(String.format("%s can not be found", taskId))); + } + + @Test + public void getShouldReturnErrorWhenInvalidId() { + String taskId = "invalid"; + + when() + .get("/" + taskId) + .then() + .statusCode(HttpStatus.BAD_REQUEST_400) + .body("statusCode", is(HttpStatus.BAD_REQUEST_400)) + .body("type", is("InvalidArgument")) + .body("message", is("Invalid task id")); + } + + @Test + public void deleteShouldReturnOkWhenNonExistingId() { + String taskId = UUID.randomUUID().toString(); + + when() + .delete("/" + taskId) + .then() + .statusCode(HttpStatus.NO_CONTENT_204); + } + + @Test + public void deleteShouldReturnAnErrorOnInvalidId() { + String taskId = "invalid"; + + when() + .delete("/" + taskId) + .then() + .statusCode(HttpStatus.BAD_REQUEST_400) + .body("statusCode", is(HttpStatus.BAD_REQUEST_400)) + .body("type", is("InvalidArgument")) + .body("message", is("Invalid task id")); + } + + @Test + public void listShouldReturnErrorWhenNonExistingStatus() { + given() + .param("status", "invalid") + .get() + .then() + .statusCode(HttpStatus.BAD_REQUEST_400) + .body("statusCode", is(HttpStatus.BAD_REQUEST_400)) + .body("type", is("InvalidArgument")) + .body("message", is("Invalid status query parameter")); + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
