This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 6ed167f6bc JAMES-4181 Webadmin: Ability to move emails between
repositories (#2954)
6ed167f6bc is described below
commit 6ed167f6bc811e8b843460396f20c09548f190a6
Author: Benoit TELLIER <[email protected]>
AuthorDate: Mon Mar 2 14:35:15 2026 +0100
JAMES-4181 Webadmin: Ability to move emails between repositories (#2954)
---
.../modules/servers/partials/operate/webadmin.adoc | 55 ++++++
.../webadmin/dto/MoveMailRepositoryRequest.java | 36 ++++
.../webadmin/routes/MailRepositoriesRoutes.java | 118 ++++++++++--
.../service/MailRepositoryStoreService.java | 35 ++++
.../routes/MailRepositoriesRoutesTest.java | 201 +++++++++++++++++++++
5 files changed, 433 insertions(+), 12 deletions(-)
diff --git a/docs/modules/servers/partials/operate/webadmin.adoc
b/docs/modules/servers/partials/operate/webadmin.adoc
index d9cc8e5628..da74695d2f 100644
--- a/docs/modules/servers/partials/operate/webadmin.adoc
+++ b/docs/modules/servers/partials/operate/webadmin.adoc
@@ -4136,6 +4136,61 @@ the following `additionalInformation`:
}
....
+=== Moving mails from a mail repository to another
+
+To move all mails from one repository to another:
+
+....
+curl -XPATCH
http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails \
+ -d '{"mailRepository": "/var/mail/error-saved"}' \
+ -H "Content-Type: application/json"
+....
+
+Resource name `encodedPathOfTheRepository` should be the URL-encoded path of
an existing mail
+repository. The request body must contain the path of an existing target mail
repository.
+
+For instance:
+
+....
+curl -XPATCH http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails \
+ -d '{"mailRepository": "var/mail/error-saved"}' \
+ -H "Content-Type: application/json"
+....
+
+Response codes:
+
+* 204: Mails were successfully moved.
+* 400: The target repository does not exist, or the request body is invalid.
+* 404: The source repository does not exist.
+
+=== Moving a specific mail to another repository
+
+To move a specific mail from one repository to another:
+
+....
+curl -XPATCH
http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/{mailKey} \
+ -d '{"mailRepository": "/var/mail/error-saved"}' \
+ -H "Content-Type: application/json"
+....
+
+Resource name `encodedPathOfTheRepository` should be the URL-encoded path of
an existing mail
+repository. Resource name `mailKey` should be the key of a mail stored in that
repository.
+The request body must contain the path of an existing target mail repository.
+
+For instance:
+
+....
+curl -XPATCH http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/name1
\
+ -d '{"mailRepository": "var/mail/error-saved"}' \
+ -H "Content-Type: application/json"
+....
+
+Response codes:
+
+* 204: The mail was successfully moved (or the mail key was not found, in
which case nothing is done).
+* 400: The target repository does not exist, or the request body is invalid.
+* 404: The source repository does not exist.
+
== Administrating mail queues
=== Listing mail queues
diff --git
a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MoveMailRepositoryRequest.java
b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MoveMailRepositoryRequest.java
new file mode 100644
index 0000000000..c7d31ff32c
--- /dev/null
+++
b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MoveMailRepositoryRequest.java
@@ -0,0 +1,36 @@
+/****************************************************************
+ * 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 com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MoveMailRepositoryRequest {
+ private final String mailRepository;
+
+ @JsonCreator
+ public MoveMailRepositoryRequest(@JsonProperty("mailRepository") String
mailRepository) {
+ this.mailRepository = mailRepository;
+ }
+
+ public String getMailRepository() {
+ return mailRepository;
+ }
+}
diff --git
a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
index 2777aea39c..c1a986c1ce 100644
---
a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
+++
b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
@@ -52,6 +52,7 @@ import
org.apache.james.webadmin.dto.ExtendedMailRepositoryResponse;
import org.apache.james.webadmin.dto.InaccessibleFieldException;
import org.apache.james.webadmin.dto.MailDto;
import org.apache.james.webadmin.dto.MailDto.AdditionalField;
+import org.apache.james.webadmin.dto.MoveMailRepositoryRequest;
import org.apache.james.webadmin.service.MailRepositoryStoreService;
import org.apache.james.webadmin.service.ReprocessingAllMailsTask;
import org.apache.james.webadmin.service.ReprocessingOneMailTask;
@@ -61,6 +62,8 @@ import
org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
import org.apache.james.webadmin.tasks.TaskRegistrationKey;
import org.apache.james.webadmin.utils.ErrorResponder;
import org.apache.james.webadmin.utils.ErrorResponder.ErrorType;
+import org.apache.james.webadmin.utils.JsonExtractException;
+import org.apache.james.webadmin.utils.JsonExtractor;
import org.apache.james.webadmin.utils.JsonTransformer;
import org.apache.james.webadmin.utils.ParametersExtractor;
import org.apache.james.webadmin.utils.Responses;
@@ -73,12 +76,16 @@ import com.google.common.collect.ImmutableSet;
import spark.HaltException;
import spark.Request;
+import spark.Response;
+import spark.Route;
import spark.Service;
public class MailRepositoriesRoutes implements Routes {
public static final String MAIL_REPOSITORIES = "mailRepositories";
private static final TaskRegistrationKey REPROCESS_ACTION =
TaskRegistrationKey.of("reprocess");
+ private static final JsonExtractor<MoveMailRepositoryRequest>
MOVE_REQUEST_EXTRACTOR =
+ new JsonExtractor<>(MoveMailRepositoryRequest.class);
private final JsonTransformer jsonTransformer;
private final MailRepositoryStoreService repositoryStoreService;
@@ -117,9 +124,9 @@ public class MailRepositoriesRoutes implements Routes {
defineDeleteAll();
- defineReprocessAll();
+ definePatchAll();
- defineReprocessOne();
+ definePatchOne();
}
public void definePutMailRepository() {
@@ -328,11 +335,14 @@ public class MailRepositoriesRoutes implements Routes {
service.delete(MAIL_REPOSITORIES + "/:encodedPath/mails",
taskFromRequest.asRoute(taskManager), jsonTransformer);
}
- public void defineReprocessAll() {
- service.patch(MAIL_REPOSITORIES + "/:encodedPath/mails",
- TaskFromRequestRegistry.of(REPROCESS_ACTION, this::reprocessAll)
- .asRoute(taskManager),
- jsonTransformer);
+ public void definePatchAll() {
+ Route reprocessRoute = TaskFromRequestRegistry.of(REPROCESS_ACTION,
this::reprocessAll).asRoute(taskManager);
+ service.patch(MAIL_REPOSITORIES + "/:encodedPath/mails", (request,
response) -> {
+ if (hasMoveRequestBody(request)) {
+ return moveAllMails(request, response);
+ }
+ return reprocessRoute.handle(request, response);
+ }, jsonTransformer);
}
private Task reprocessAll(Request request) throws
MailRepositoryStore.MailRepositoryStoreException {
@@ -351,11 +361,14 @@ public class MailRepositoriesRoutes implements Routes {
parseLimit(request));
}
- public void defineReprocessOne() {
- service.patch(MAIL_REPOSITORIES + "/:encodedPath/mails/:key",
- TaskFromRequestRegistry.of(REPROCESS_ACTION, this::reprocessOne)
- .asRoute(taskManager),
- jsonTransformer);
+ public void definePatchOne() {
+ Route reprocessOneRoute = TaskFromRequestRegistry.of(REPROCESS_ACTION,
this::reprocessOne).asRoute(taskManager);
+ service.patch(MAIL_REPOSITORIES + "/:encodedPath/mails/:key",
(request, response) -> {
+ if (hasMoveRequestBody(request)) {
+ return moveOneMail(request, response);
+ }
+ return reprocessOneRoute.handle(request, response);
+ }, jsonTransformer);
}
private Task reprocessOne(Request request) {
@@ -365,6 +378,87 @@ public class MailRepositoriesRoutes implements Routes {
return new ReprocessingOneMailTask(reprocessingService, path,
extractConfiguration(request), key, Clock.systemUTC());
}
+ private boolean hasMoveRequestBody(Request request) {
+ String body = request.body();
+ return body != null && !body.isBlank();
+ }
+
+ private Object moveAllMails(Request request, Response response) {
+ MailRepositoryPath sourcePath = getRepositoryPath(request);
+ MoveMailRepositoryRequest moveRequest = parseMoveRequest(request);
+ MailRepositoryPath targetPath =
MailRepositoryPath.from(moveRequest.getMailRepository());
+ try {
+ if (!repositoryStoreService.repositoryExists(sourcePath)) {
+ throw repositoryNotFound(request.params("encodedPath"),
sourcePath);
+ }
+ if (!repositoryStoreService.repositoryExists(targetPath)) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .type(ErrorType.INVALID_ARGUMENT)
+ .message("The target repository '%s' does not exist",
moveRequest.getMailRepository())
+ .haltError();
+ }
+ repositoryStoreService.moveAllMails(sourcePath, targetPath);
+ return Responses.returnNoContent(response);
+ } catch (MailRepositoryStore.MailRepositoryStoreException |
MessagingException e) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+ .type(ErrorType.SERVER_ERROR)
+ .cause(e)
+ .message("Error while moving mails")
+ .haltError();
+ }
+ }
+
+ private Object moveOneMail(Request request, Response response) {
+ MailRepositoryPath sourcePath = getRepositoryPath(request);
+ MailKey mailKey = new MailKey(request.params("key"));
+ MoveMailRepositoryRequest moveRequest = parseMoveRequest(request);
+ MailRepositoryPath targetPath =
MailRepositoryPath.from(moveRequest.getMailRepository());
+ try {
+ if (!repositoryStoreService.repositoryExists(sourcePath)) {
+ throw repositoryNotFound(request.params("encodedPath"),
sourcePath);
+ }
+ if (!repositoryStoreService.repositoryExists(targetPath)) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .type(ErrorType.INVALID_ARGUMENT)
+ .message("The target repository '%s' does not exist",
moveRequest.getMailRepository())
+ .haltError();
+ }
+ repositoryStoreService.moveMail(sourcePath, targetPath, mailKey);
+ return Responses.returnNoContent(response);
+ } catch (MailRepositoryStore.MailRepositoryStoreException |
MessagingException e) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+ .type(ErrorType.SERVER_ERROR)
+ .cause(e)
+ .message("Error while moving mail")
+ .haltError();
+ }
+ }
+
+ private MoveMailRepositoryRequest parseMoveRequest(Request request) {
+ try {
+ MoveMailRepositoryRequest moveRequest =
MOVE_REQUEST_EXTRACTOR.parse(request.body());
+ if (moveRequest.getMailRepository() == null ||
moveRequest.getMailRepository().isBlank()) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .type(ErrorType.INVALID_ARGUMENT)
+ .message("'mailRepository' field is mandatory in request
body")
+ .haltError();
+ }
+ return moveRequest;
+ } catch (JsonExtractException e) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .type(ErrorType.INVALID_ARGUMENT)
+ .cause(e)
+ .message("Invalid JSON body")
+ .haltError();
+ }
+ }
+
private Set<AdditionalField> extractAdditionalFields(String
additionalFieldsParam) throws IllegalArgumentException {
return Splitter
.on(',')
diff --git
a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
index e386234413..2099a9abd3 100644
---
a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
+++
b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
@@ -29,6 +29,7 @@ import jakarta.inject.Inject;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.lifecycle.api.LifecycleUtil;
import org.apache.james.mailrepository.api.MailKey;
import org.apache.james.mailrepository.api.MailRepository;
@@ -116,6 +117,40 @@ public class MailRepositoryStoreService {
.forEach(Throwing.consumer((MailRepository repository) ->
repository.remove(mailKey)).sneakyThrow());
}
+ public boolean repositoryExists(MailRepositoryPath path) throws
MailRepositoryStore.MailRepositoryStoreException {
+ return mailRepositoryStore.getByPath(path).findAny().isPresent();
+ }
+
+ public void moveMail(MailRepositoryPath sourcePath, MailRepositoryPath
targetPath, MailKey mailKey) throws
MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+ MailRepository source = getRepositories(sourcePath).findFirst()
+ .orElseThrow(() -> new
MailRepositoryStore.MailRepositoryStoreException("No repository found for path:
" + sourcePath.asString()));
+ MailRepository target = getRepositories(targetPath).findFirst()
+ .orElseThrow(() -> new
MailRepositoryStore.MailRepositoryStoreException("No repository found for path:
" + targetPath.asString()));
+
+ moveMail(source, target, mailKey);
+ }
+
+ public void moveAllMails(MailRepositoryPath sourcePath, MailRepositoryPath
targetPath) throws MailRepositoryStore.MailRepositoryStoreException,
MessagingException {
+ MailRepository target = getRepositories(targetPath).findFirst()
+ .orElseThrow(() -> new
MailRepositoryStore.MailRepositoryStoreException("No repository found for path:
" + targetPath.asString()));
+
+ getRepositories(sourcePath)
+ .flatMap(Throwing.function(repo ->
Iterators.toStream(repo.list()).map(key -> Pair.of(repo, key))))
+ .forEach(Throwing.<Pair<MailRepository, MailKey>>consumer(pair ->
moveMail(pair.getKey(), target, pair.getValue())).sneakyThrow());
+ }
+
+ private void moveMail(MailRepository source, MailRepository target,
MailKey key) throws MessagingException {
+ Optional.ofNullable(source.retrieve(key))
+ .ifPresent(Throwing.consumer(mail -> {
+ try {
+ target.store(mail);
+ source.remove(key);
+ } finally {
+ LifecycleUtil.dispose(mail);
+ }
+ }));
+ }
+
public Task createClearMailRepositoryTask(MailRepositoryPath path) throws
MailRepositoryStore.MailRepositoryStoreException, MessagingException {
getRepositories(path);
return new ClearMailRepositoryTask(mailRepositoryStore, path);
diff --git
a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
index 35b177f711..133e9f5157 100644
---
a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
+++
b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
@@ -2358,6 +2358,207 @@ class MailRepositoriesRoutesTest {
.body("status", is("failed"));
}
+ @Test
+ void moveAllMailsShouldReturn404WhenSourceRepositoryDoesNotExist() {
+ given()
+ .body("{\"mailRepository\": \"myTargetRepo\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails")
+ .then()
+ .statusCode(HttpStatus.NOT_FOUND_404)
+ .body("statusCode", is(404))
+ .body("type", is(ErrorResponder.ErrorType.NOT_FOUND.getType()));
+ }
+
+ @Test
+ void moveAllMailsShouldReturn400WhenTargetRepositoryDoesNotExist() throws
Exception {
+ mailRepositoryStore.create(URL_MY_REPO);
+
+ given()
+ .body("{\"mailRepository\": \"nonExistingTarget\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails")
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type",
is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("The target repository 'nonExistingTarget'
does not exist"));
+ }
+
+ @Test
+ void moveAllMailsShouldReturn400WhenMailRepositoryFieldIsMissing() throws
Exception {
+ mailRepositoryStore.create(URL_MY_REPO);
+
+ given()
+ .body("{}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails")
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type",
is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("'mailRepository' field is mandatory in
request body"));
+ }
+
+ @Test
+ void moveAllMailsShouldReturn400WhenBodyIsInvalidJson() throws Exception {
+ mailRepositoryStore.create(URL_MY_REPO);
+
+ given()
+ .body("not-json")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails")
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type",
is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("Invalid JSON body"));
+ }
+
+ @Test
+ void moveAllMailsShouldMoveMailsFromSourceToTarget() throws Exception {
+ MailRepository sourceRepo = mailRepositoryStore.create(URL_MY_REPO);
+ MailRepository targetRepo =
mailRepositoryStore.create(MailRepositoryUrl.from("memory://myTargetRepo"));
+
+ sourceRepo.store(FakeMail.builder().name(NAME_1).build());
+ sourceRepo.store(FakeMail.builder().name(NAME_2).build());
+
+ given()
+ .body("{\"mailRepository\": \"myTargetRepo\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails")
+ .then()
+ .statusCode(HttpStatus.NO_CONTENT_204);
+
+ assertThat(sourceRepo.list()).toIterable().isEmpty();
+ assertThat(targetRepo.list()).toIterable()
+ .containsExactlyInAnyOrder(new MailKey(NAME_1), new
MailKey(NAME_2));
+ }
+
+ @Test
+ void moveAllMailsShouldReturn204WhenSourceRepositoryIsEmpty() throws
Exception {
+ mailRepositoryStore.create(URL_MY_REPO);
+
mailRepositoryStore.create(MailRepositoryUrl.from("memory://myTargetRepo"));
+
+ given()
+ .body("{\"mailRepository\": \"myTargetRepo\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails")
+ .then()
+ .statusCode(HttpStatus.NO_CONTENT_204);
+ }
+
+ @Test
+ void moveOneMailShouldReturn404WhenSourceRepositoryDoesNotExist() {
+ given()
+ .body("{\"mailRepository\": \"myTargetRepo\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails/" + NAME_1)
+ .then()
+ .statusCode(HttpStatus.NOT_FOUND_404)
+ .body("statusCode", is(404))
+ .body("type", is(ErrorResponder.ErrorType.NOT_FOUND.getType()));
+ }
+
+ @Test
+ void moveOneMailShouldReturn400WhenTargetRepositoryDoesNotExist() throws
Exception {
+ MailRepository sourceRepo = mailRepositoryStore.create(URL_MY_REPO);
+ sourceRepo.store(FakeMail.builder().name(NAME_1).build());
+
+ given()
+ .body("{\"mailRepository\": \"nonExistingTarget\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails/" + NAME_1)
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type",
is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("The target repository 'nonExistingTarget'
does not exist"));
+ }
+
+ @Test
+ void moveOneMailShouldReturn400WhenMailRepositoryFieldIsMissing() throws
Exception {
+ MailRepository sourceRepo = mailRepositoryStore.create(URL_MY_REPO);
+ sourceRepo.store(FakeMail.builder().name(NAME_1).build());
+
+ given()
+ .body("{}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails/" + NAME_1)
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type",
is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("'mailRepository' field is mandatory in
request body"));
+ }
+
+ @Test
+ void moveOneMailShouldReturn400WhenBodyIsInvalidJson() throws Exception {
+ MailRepository sourceRepo = mailRepositoryStore.create(URL_MY_REPO);
+ sourceRepo.store(FakeMail.builder().name(NAME_1).build());
+
+ given()
+ .body("not-json")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails/" + NAME_1)
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type",
is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("Invalid JSON body"));
+ }
+
+ @Test
+ void moveOneMailShouldMoveMailFromSourceToTarget() throws Exception {
+ MailRepository sourceRepo = mailRepositoryStore.create(URL_MY_REPO);
+ MailRepository targetRepo =
mailRepositoryStore.create(MailRepositoryUrl.from("memory://myTargetRepo"));
+
+ sourceRepo.store(FakeMail.builder().name(NAME_1).build());
+ sourceRepo.store(FakeMail.builder().name(NAME_2).build());
+
+ given()
+ .body("{\"mailRepository\": \"myTargetRepo\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails/" + NAME_1)
+ .then()
+ .statusCode(HttpStatus.NO_CONTENT_204);
+
+ assertThat(sourceRepo.list()).toIterable()
+ .containsOnly(new MailKey(NAME_2));
+ assertThat(targetRepo.list()).toIterable()
+ .containsOnly(new MailKey(NAME_1));
+ }
+
+ @Test
+ void moveOneMailShouldReturn204WhenMailKeyDoesNotExist() throws Exception {
+ MailRepository sourceRepo = mailRepositoryStore.create(URL_MY_REPO);
+
mailRepositoryStore.create(MailRepositoryUrl.from("memory://myTargetRepo"));
+
+ sourceRepo.store(FakeMail.builder().name(NAME_1).build());
+
+ given()
+ .body("{\"mailRepository\": \"myTargetRepo\"}")
+ .contentType(io.restassured.http.ContentType.JSON)
+ .when()
+ .patch(PATH_ESCAPED_MY_REPO + "/mails/unknown")
+ .then()
+ .statusCode(HttpStatus.NO_CONTENT_204);
+
+ assertThat(sourceRepo.list()).toIterable()
+ .containsOnly(new MailKey(NAME_1));
+ }
+
private void createMailRepositoryStore() throws Exception {
MemoryMailRepositoryUrlStore urlStore = new
MemoryMailRepositoryUrlStore();
MailRepositoryStoreConfiguration configuration =
MailRepositoryStoreConfiguration.forItems(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]