You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2023/05/24 07:18:44 UTC

[james-project] branch master updated (34f74b9e67 -> cfdf2aeeda)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


    from 34f74b9e67 [ADR] Fix typo in ADR-66 (#1567)
     new 23842d4511 JAMES-3909 DeleteUserDataTaskStep interface
     new 71d438aa8c JAMES-3909 Service for delete user data
     new 6d3443116e JAMES-3909 Task for delete user data
     new 42dee87a6d JAMES-3909 Webadmin route for user data deletion
     new eafbb9d008 JAMES-3909 Guice bindings for delete user data routes
     new cfdf2aeeda JAMES-3909 Documentation for user data deletion webadmin routes

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../docs/modules/ROOT/pages/operate/webadmin.adoc  |  51 +++++++++
 .../main/java/org/apache/james/CoreDataModule.java |   2 +
 .../james/modules/server/DataRoutesModules.java    |  21 ++++
 ...geTaskStep.java => DeleteUserDataTaskStep.java} |   4 +-
 ...ChangeRoutes.java => DeleteUserDataRoutes.java} |  40 +++----
 ...angeService.java => DeleteUserDataService.java} |  38 +++----
 ...nameChangeTask.java => DeleteUserDataTask.java} |  51 ++++-----
 ...eleteUserDataTaskAdditionalInformationDTO.java} |  44 ++++----
 ...angeTaskDTO.java => DeleteUserDataTaskDTO.java} |  45 ++++----
 ...utesTest.java => DeleteUserDataRoutesTest.java} | 116 ++++++++++-----------
 ...va => DeleteUserDataTaskSerializationTest.java} |  83 +++++++--------
 src/site/markdown/server/manage-webadmin.md        |  53 ++++++++++
 12 files changed, 316 insertions(+), 232 deletions(-)
 copy server/data/data-api/src/main/java/org/apache/james/user/api/{UsernameChangeTaskStep.java => DeleteUserDataTaskStep.java} (94%)
 copy server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/{UsernameChangeRoutes.java => DeleteUserDataRoutes.java} (59%)
 copy server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/{UsernameChangeService.java => DeleteUserDataService.java} (77%)
 copy server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/{UsernameChangeTask.java => DeleteUserDataTask.java} (66%)
 copy server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/{UsernameChangeTaskAdditionalInformationDTO.java => DeleteUserDataTaskAdditionalInformationDTO.java} (72%)
 copy server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/{UsernameChangeTaskDTO.java => DeleteUserDataTaskDTO.java} (62%)
 copy server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/{UsernameChangeRoutesTest.java => DeleteUserDataRoutesTest.java} (66%)
 copy server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/service/{UsernameChangeTaskSerializationTest.java => DeleteUserDataTaskSerializationTest.java} (55%)


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 06/06: JAMES-3909 Documentation for user data deletion webadmin routes

Posted by bt...@apache.org.
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

commit cfdf2aeeda887c27d0e14f625216c5a25c0f9a85
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Fri May 19 16:45:15 2023 +0700

    JAMES-3909 Documentation for user data deletion webadmin routes
---
 .../docs/modules/ROOT/pages/operate/webadmin.adoc  | 51 +++++++++++++++++++++
 src/site/markdown/server/manage-webadmin.md        | 53 ++++++++++++++++++++++
 2 files changed, 104 insertions(+)

diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
index bf37c8343f..1a343c7d30 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
@@ -674,6 +674,57 @@ The scheduled task will have the following type `UsernameChangeTask` and the fol
 }
 ....
 
+Valid status includes:
+
+ - `SKIPPED`: bypassed via `fromStep` setting
+ - `WAITING`: Awaits execution
+ - `IN_PROGRESS`: Currently executed
+ - `FAILED`: Error encountered while executing this step. Check the logs.
+ - `ABORTED`: Won't be executed because of previous step failures.
+
+=== Delete data of a user
+
+....
+curl -XPOST http://ip:port/users/usernameToBeUsed?action=deleteData
+....
+
+Would create a task that deletes data of the user.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning a task].
+
+Implemented deletion steps are:
+
+ - `RecipientRewriteTableUserDeletionTaskStep`: deletes all rewriting rules related to this user.
+ - `FilterUserDeletionTaskStep`: deletes all filters belonging to the user.
+ - `DelegationUserDeletionTaskStep`: deletes all delegations from / to the user.
+ - `MailboxUserDeletionTaskStep`: deletes mailboxes of this user, all ACLs of this user, as well as his subscriptions.
+ - `WebPushUserDeletionTaskStep`: deletes push data registered for this user.
+ - `IdentityUserDeletionTaskStep`: deletes identities registered for this user.
+ - `VacationUserDeletionTaskStep`: deletes vacations registered for this user.
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The `fromStep` query parameter allows skipping previous steps, allowing to resume the user data deletion from a failed step.
+
+The scheduled task will have the following type `DeleteUserDataTask` and the following `additionalInformation`:
+
+....
+{
+        "type": "DeleteUserDataTask",
+        "username": "jessy.jones@domain.tld",
+        "status": {
+            "A": "DONE",
+            "B": "FAILED",
+            "C": "ABORTED"
+        },
+        "fromStep": null,
+        "timestamp": "2023-02-17T02:54:01.246477Z"
+}
+....
+
 Valid status includes:
 
  - `SKIPPED`: bypassed via `fromStep` setting
diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 4f59df86d3..329cd488ad 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -310,6 +310,8 @@ Response codes:
    - [Deleting a user](#Deleting_a_user)
    - [Retrieving the user list](#Retrieving_the_user_list)
    - [Retrieving the list of allowed `From` headers for a given user](Retrieving_the_list_of_allowed_From_headers_for_a_given_user)
+   - [Change a username](#change-a-username)
+   - [Delete data of a user](#delete-data-of-a-user)
 
 ### Create a user
 
@@ -515,6 +517,57 @@ The scheduled task will have the following type `UsernameChangeTask` and the fol
 }
 ```
 
+Valid status includes:
+
+ - `SKIPPED`: bypassed via `fromStep` setting
+ - `WAITING`: Awaits execution
+ - `IN_PROGRESS`: Currently executed
+ - `FAILED`: Error encountered while executing this step. Check the logs.
+ - `ABORTED`: Won't be executed because of previous step failures.
+
+### Delete data of a user
+
+```
+curl -XPOST http://ip:port/users/usernameToBeUsed?action=deleteData
+```
+
+Would create a task that deletes data of the user.
+
+[More details about endpoints returning a task](#_endpoints_returning_a_task).
+
+Implemented deletion steps are:
+
+- `RecipientRewriteTableUserDeletionTaskStep`: deletes all rewriting rules related to this user.
+- `FilterUserDeletionTaskStep`: deletes all filters belonging to the user.
+- `DelegationUserDeletionTaskStep`: deletes all delegations from / to the user.
+- `MailboxUserDeletionTaskStep`: deletes mailboxes of this user, all ACLs of this user, as well as his subscriptions.
+- `WebPushUserDeletionTaskStep`: deletes push data registered for this user.
+- `IdentityUserDeletionTaskStep`: deletes identities registered for this user.
+- `VacationUserDeletionTaskStep`: deletes vacations registered for this user.
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The `fromStep` query parameter allows skipping previous steps, allowing to resume the user data deletion from a failed step.
+
+The scheduled task will have the following type `DeleteUserDataTask` and the following `additionalInformation`:
+
+```
+{
+        "type": "DeleteUserDataTask",
+        "username": "jessy.jones@domain.tld",
+        "status": {
+            "A": "DONE",
+            "B": "FAILED",
+            "C": "ABORTED"
+        },
+        "fromStep": null,
+        "timestamp": "2023-02-17T02:54:01.246477Z"
+}
+```
+
 Valid status includes:
 
  - `SKIPPED`: bypassed via `fromStep` setting


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 01/06: JAMES-3909 DeleteUserDataTaskStep interface

Posted by bt...@apache.org.
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

commit 23842d4511599f492a77c6e73524d084cd1d592a
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Thu May 18 13:25:32 2023 +0700

    JAMES-3909 DeleteUserDataTaskStep interface
    
    Co-authored-by: Benoit Tellier <bt...@linagora.com>
---
 .../james/user/api/DeleteUserDataTaskStep.java     | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/server/data/data-api/src/main/java/org/apache/james/user/api/DeleteUserDataTaskStep.java b/server/data/data-api/src/main/java/org/apache/james/user/api/DeleteUserDataTaskStep.java
new file mode 100644
index 0000000000..5f72d3806a
--- /dev/null
+++ b/server/data/data-api/src/main/java/org/apache/james/user/api/DeleteUserDataTaskStep.java
@@ -0,0 +1,60 @@
+/****************************************************************
+ * 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.user.api;
+
+import java.util.Objects;
+
+import org.apache.james.core.Username;
+import org.reactivestreams.Publisher;
+
+public interface DeleteUserDataTaskStep {
+    class StepName {
+        private final String value;
+
+        public StepName(String value) {
+            this.value = value;
+        }
+
+        public String asString() {
+            return value;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof StepName) {
+                StepName other = (StepName) o;
+
+                return Objects.equals(this.value, other.value);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(value);
+        }
+    }
+
+    StepName name();
+
+    int priority();
+
+    Publisher<Void> deleteUserData(Username username);
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 02/06: JAMES-3909 Service for delete user data

Posted by bt...@apache.org.
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

commit 71d438aa8c91b5f181ef8982cfa30ca92522fa18
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Thu May 18 13:36:56 2023 +0700

    JAMES-3909 Service for delete user data
    
    Plug steps together, running them one after theother and provides ordering of steps, execution tacking.
    
    Stops at first failed steps and aborts the following steps.
    
    Co-authored-by: Benoit Tellier <bt...@linagora.com>
---
 .../webadmin/service/DeleteUserDataService.java    | 146 +++++++++++++++++++++
 1 file changed, 146 insertions(+)

diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataService.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataService.java
new file mode 100644
index 0000000000..2bd572b68c
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataService.java
@@ -0,0 +1,146 @@
+/****************************************************************
+ * 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.service;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.user.api.DeleteUserDataTaskStep;
+import org.apache.james.user.api.DeleteUserDataTaskStep.StepName;
+
+import com.google.common.collect.ImmutableMap;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class DeleteUserDataService {
+    public enum StepState {
+        WAITING,
+        IN_PROGRESS,
+        DONE,
+        FAILED,
+        ABORTED,
+        SKIPPED
+    }
+
+    public static class DeleteUserDataStatus {
+        private final Map<StepName, StepState> states;
+
+        public DeleteUserDataStatus(Set<DeleteUserDataTaskStep> steps) {
+            states = new ConcurrentHashMap<>(steps.stream()
+                .collect(ImmutableMap.toImmutableMap(DeleteUserDataTaskStep::name, any -> StepState.WAITING)));
+        }
+
+        public void beginStep(StepName step) {
+            states.put(step, StepState.IN_PROGRESS);
+        }
+
+        public void endStep(StepName step) {
+            states.put(step, StepState.DONE);
+        }
+
+        public void failedStep(StepName step) {
+            states.put(step, StepState.FAILED);
+        }
+
+        public void abortStep(StepName step) {
+            states.put(step, StepState.ABORTED);
+        }
+
+        public void skipStep(StepName step) {
+            states.put(step, StepState.SKIPPED);
+        }
+
+        public void abort() {
+            states.entrySet()
+                .stream()
+                .filter(entry -> entry.getValue() == StepState.WAITING || entry.getValue() == StepState.IN_PROGRESS)
+                .forEach(entry -> abortStep(entry.getKey()));
+        }
+
+        public Map<StepName, StepState> getStates() {
+            return ImmutableMap.copyOf(states);
+        }
+    }
+
+    public static class Performer {
+        private final Set<DeleteUserDataTaskStep> steps;
+        private final DeleteUserDataStatus status;
+        private final Optional<Integer> correspondingPriority;
+
+        public Performer(Set<DeleteUserDataTaskStep> steps, DeleteUserDataStatus status, Optional<StepName> fromStep) {
+            this.steps = steps;
+            this.status = status;
+            this.correspondingPriority = fromStep.map(this::correspondingPriority);
+        }
+
+        public Mono<Void> deleteUserData(Username username) {
+            correspondingPriority.ifPresent(priority -> steps.stream()
+                .filter(step -> step.priority() < priority)
+                .forEach(step -> status.skipStep(step.name())));
+
+            return steps()
+                .concatMap(step -> Mono.fromRunnable(() -> status.beginStep(step.name()))
+                    .then(Mono.from(step.deleteUserData(username)))
+                    .then(Mono.fromRunnable(() -> status.endStep(step.name())))
+                    .doOnError(e -> status.failedStep(step.name())))
+                .doOnError(e -> status.abort())
+                .then();
+        }
+
+        private Flux<DeleteUserDataTaskStep> steps() {
+            return correspondingPriority
+                .map(priority -> Flux.fromIterable(steps)
+                    .filter(step -> step.priority() >= priority)
+                    .sort(Comparator.comparingInt(DeleteUserDataTaskStep::priority)))
+                .orElseGet(() -> Flux.fromIterable(steps)
+                    .sort(Comparator.comparingInt(DeleteUserDataTaskStep::priority)));
+        }
+
+        private int correspondingPriority(StepName stepName) {
+            return steps.stream()
+                .filter(step -> step.name().equals(stepName))
+                .map(DeleteUserDataTaskStep::priority)
+                .findAny()
+                .orElseThrow(() -> new IllegalArgumentException("Starting step not found: " + stepName.asString()));
+        }
+
+        public DeleteUserDataStatus getStatus() {
+            return status;
+        }
+    }
+
+    private final Set<DeleteUserDataTaskStep> steps;
+
+    @Inject
+    public DeleteUserDataService(Set<DeleteUserDataTaskStep> steps) {
+        this.steps = steps;
+    }
+
+    public Performer performer(Optional<StepName> fromStep) {
+        return new Performer(steps, new DeleteUserDataStatus(steps), fromStep);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 04/06: JAMES-3909 Webadmin route for user data deletion

Posted by bt...@apache.org.
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

commit 42dee87a6d8a8f083283d9956a20b142e5463e8d
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Thu May 18 16:51:56 2023 +0700

    JAMES-3909 Webadmin route for user data deletion
    
    Co-authored-by: Benoit Tellier <bt...@linagora.com>
---
 .../webadmin/routes/DeleteUserDataRoutes.java      |  84 +++++++
 .../webadmin/routes/DeleteUserDataRoutesTest.java  | 278 +++++++++++++++++++++
 2 files changed, 362 insertions(+)

diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DeleteUserDataRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DeleteUserDataRoutes.java
new file mode 100644
index 0000000000..f581ed7127
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DeleteUserDataRoutes.java
@@ -0,0 +1,84 @@
+/****************************************************************
+ * 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 javax.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.task.TaskManager;
+import org.apache.james.user.api.DeleteUserDataTaskStep.StepName;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.service.DeleteUserDataService;
+import org.apache.james.webadmin.service.DeleteUserDataTask;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
+import org.apache.james.webadmin.tasks.TaskRegistrationKey;
+import org.apache.james.webadmin.utils.JsonTransformer;
+
+import com.google.common.base.Preconditions;
+
+import spark.Route;
+import spark.Service;
+
+public class DeleteUserDataRoutes implements Routes {
+    private static final String USER_PATH_PARAM = ":username";
+    private static final String ROOT_PATH = "/users/" + USER_PATH_PARAM;
+    private static final TaskRegistrationKey DELETE_USER_DATA = TaskRegistrationKey.of("deleteData");
+
+    private final UsersRepository usersRepository;
+    private final DeleteUserDataService service;
+    private final TaskManager taskManager;
+    private final JsonTransformer jsonTransformer;
+
+    @Inject
+    DeleteUserDataRoutes(UsersRepository usersRepository, DeleteUserDataService service, TaskManager taskManager, JsonTransformer jsonTransformer) {
+        this.usersRepository = usersRepository;
+        this.service = service;
+        this.taskManager = taskManager;
+        this.jsonTransformer = jsonTransformer;
+    }
+
+    @Override
+    public String getBasePath() {
+        return ROOT_PATH;
+    }
+
+    @Override
+    public void define(Service service) {
+        service.post(ROOT_PATH, deleteUserData(), jsonTransformer);
+    }
+
+    public Route deleteUserData() {
+        return TaskFromRequestRegistry.builder()
+            .parameterName("action")
+            .register(DELETE_USER_DATA, request -> {
+                Username username = Username.of(request.params(USER_PATH_PARAM));
+
+                Preconditions.checkArgument(usersRepository.contains(username), "'username' parameter should be an existing user");
+
+                Optional<StepName> fromStep = Optional.ofNullable(request.queryParams("fromStep")).map(StepName::new);
+
+                return new DeleteUserDataTask(service, username, fromStep);
+            })
+            .buildAsRoute(taskManager);
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DeleteUserDataRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DeleteUserDataRoutesTest.java
new file mode 100644
index 0000000000..f3939320e1
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DeleteUserDataRoutesTest.java
@@ -0,0 +1,278 @@
+/****************************************************************
+ * 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 io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.with;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.james.core.Domain;
+import org.apache.james.core.Username;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.domainlist.lib.DomainListConfiguration;
+import org.apache.james.domainlist.memory.MemoryDomainList;
+import org.apache.james.json.DTOConverter;
+import org.apache.james.task.Hostname;
+import org.apache.james.task.MemoryTaskManager;
+import org.apache.james.user.api.DeleteUserDataTaskStep;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.service.DeleteUserDataService;
+import org.apache.james.webadmin.service.DeleteUserDataTaskAdditionalInformationDTO;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.reactivestreams.Publisher;
+
+import com.google.common.collect.ImmutableSet;
+
+import io.restassured.RestAssured;
+import reactor.core.publisher.Mono;
+
+class DeleteUserDataRoutesTest {
+
+    private static final Username USER = Username.of("jessy.jones@domain.tld");
+
+    public static class StepImpl implements DeleteUserDataTaskStep {
+        private final StepName name;
+        private final int priority;
+        private final Mono<Void> behaviour;
+
+        public StepImpl(StepName name, int priority, Mono<Void> behaviour) {
+            this.name = name;
+            this.priority = priority;
+            this.behaviour = behaviour;
+        }
+
+        @Override
+        public StepName name() {
+            return name;
+        }
+
+        @Override
+        public int priority() {
+            return priority;
+        }
+
+        @Override
+        public Publisher<Void> deleteUserData(Username username) {
+            return behaviour;
+        }
+    }
+
+    private MemoryUsersRepository usersRepository;
+
+    WebAdminServer setUp(ImmutableSet<DeleteUserDataTaskStep> steps) {
+        MemoryTaskManager taskManager = new MemoryTaskManager(new Hostname("foo"));
+        DeleteUserDataService service = new DeleteUserDataService(steps);
+        WebAdminServer webAdminServer = WebAdminUtils
+            .createWebAdminServer(new DeleteUserDataRoutes(usersRepository, service, taskManager, new JsonTransformer()),
+                new TasksRoutes(taskManager, new JsonTransformer(), DTOConverter.of(DeleteUserDataTaskAdditionalInformationDTO.module())))
+            .start();
+
+        RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer)
+            .build();
+
+        return webAdminServer;
+    }
+
+    @BeforeEach
+    void setUpUsersRepo() throws Exception {
+        DNSService dnsService = mock(DNSService.class);
+        MemoryDomainList domainList = new MemoryDomainList(dnsService);
+        domainList.configure(DomainListConfiguration.DEFAULT);
+        domainList.addDomain(Domain.of("domain.tld"));
+        usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
+    }
+
+    @Nested
+    class BasicTests {
+        private WebAdminServer webAdminServer;
+        private AtomicBoolean behaviour1;
+        private AtomicBoolean behaviour2;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            behaviour1 = new AtomicBoolean(false);
+            behaviour2 = new AtomicBoolean(false);
+            webAdminServer = DeleteUserDataRoutesTest.this.setUp(
+                ImmutableSet.of(new StepImpl(new DeleteUserDataTaskStep.StepName("A"), 35, Mono.fromRunnable(() -> behaviour1.set(true))),
+                    new StepImpl(new DeleteUserDataTaskStep.StepName("B"), 3, Mono.fromRunnable(() -> behaviour2.set(true)))));
+
+            usersRepository.addUser(USER, "pass");
+        }
+
+        @AfterEach
+        void stop() {
+            webAdminServer.destroy();
+        }
+
+        @Test
+        void shouldPerformDataDeletion() {
+            String taskId = with()
+                .queryParam("action", "deleteData")
+                .post("/users/" + USER.asString())
+                .jsonPath()
+                .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("type", is("DeleteUserDataTask"))
+                .body("status", is("completed"))
+                .body("additionalInformation.type", is("DeleteUserDataTask"))
+                .body("additionalInformation.username", is("jessy.jones@domain.tld"))
+                .body("additionalInformation.status.A", is("DONE"))
+                .body("additionalInformation.status.B", is("DONE"));
+
+            assertThat(behaviour1.get()).isTrue();
+            assertThat(behaviour2.get()).isTrue();
+        }
+
+        @Test
+        void shouldFailWhenInvalidAction() {
+            given()
+                .queryParam("action", "invalid")
+            .post("/users/" + USER.asString())
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .body("statusCode", is(400))
+                .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+                .body("message", is("Invalid arguments supplied in the user request"))
+                .body("details", is("Invalid value supplied for query parameter 'action': invalid. Supported values are [deleteData]"));
+        }
+
+        @Test
+        void shouldRejectUnknownUser() {
+            given()
+                .queryParam("action", "deleteData")
+            .when()
+                .post("/users/unknown@domain.tld")
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .body("statusCode", Matchers.is(400))
+                .body("type", Matchers.is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+                .body("message", Matchers.is("Invalid arguments supplied in the user request"))
+                .body("details", Matchers.is("'username' parameter should be an existing user"));
+        }
+    }
+
+    @Nested
+    class ResumeFailureTests {
+        private WebAdminServer webAdminServer;
+        private AtomicBoolean behaviour1;
+        private AtomicBoolean behaviour2;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            behaviour1 = new AtomicBoolean(false);
+            behaviour2 = new AtomicBoolean(false);
+            webAdminServer = DeleteUserDataRoutesTest.this.setUp(
+                ImmutableSet.of(new StepImpl(new DeleteUserDataTaskStep.StepName("A"), 1, Mono.fromRunnable(() -> behaviour1.set(true))),
+                    new StepImpl(new DeleteUserDataTaskStep.StepName("B"), 2, Mono.error(RuntimeException::new)),
+                    new StepImpl(new DeleteUserDataTaskStep.StepName("C"), 3, Mono.fromRunnable(() -> behaviour2.set(true)))));
+
+            usersRepository.addUser(USER, "pass");
+        }
+
+        @AfterEach
+        void stop() {
+            webAdminServer.destroy();
+        }
+
+        @Test
+        void shouldReportFailures() {
+            String taskId = with()
+                .queryParam("action", "deleteData")
+                .post("/users/" + USER.asString())
+                .jsonPath()
+                .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("type", is("DeleteUserDataTask"))
+                .body("status", is("failed"))
+                .body("additionalInformation.type", is("DeleteUserDataTask"))
+                .body("additionalInformation.username", is("jessy.jones@domain.tld"))
+                .body("additionalInformation.status.A", is("DONE"))
+                .body("additionalInformation.status.B", is("FAILED"))
+                .body("additionalInformation.status.C", is("ABORTED"));
+
+            assertThat(behaviour1.get()).isTrue();
+            assertThat(behaviour2.get()).isFalse();
+        }
+
+        @Test
+        void shouldSupportResumeWhenFailure() {
+            String taskId = with()
+                .queryParam("action", "deleteData")
+                .queryParam("fromStep", "B")
+                .post("/users/" + USER.asString())
+                .jsonPath()
+                .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("type", is("DeleteUserDataTask"))
+                .body("status", is("failed"))
+                .body("additionalInformation.type", is("DeleteUserDataTask"))
+                .body("additionalInformation.username", is("jessy.jones@domain.tld"))
+                .body("additionalInformation.status.A", is("SKIPPED"))
+                .body("additionalInformation.status.B", is("FAILED"))
+                .body("additionalInformation.status.C", is("ABORTED"));
+
+            assertThat(behaviour1.get()).isFalse();
+            assertThat(behaviour2.get()).isFalse();
+        }
+
+        @Test
+        void shouldRejectInvalidFromStep() {
+            given()
+                .queryParam("action", "deleteData")
+                .queryParam("fromStep", "invalid")
+            .when()
+                .post("/users/" + USER.asString())
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .body("statusCode", Matchers.is(400))
+                .body("type", Matchers.is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+                .body("message", Matchers.is("Invalid arguments supplied in the user request"))
+                .body("details", Matchers.is("Starting step not found: invalid"));
+        }
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 03/06: JAMES-3909 Task for delete user data

Posted by bt...@apache.org.
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

commit 6d3443116e22d44cc789a5228a3b1772b2c9a187
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Thu May 18 15:10:37 2023 +0700

    JAMES-3909 Task for delete user data
    
    Simple task wrapper around the service
    
    Co-authored-by: Benoit Tellier <bt...@linagora.com>
---
 .../james/webadmin/service/DeleteUserDataTask.java | 108 ++++++++++++++++
 ...DeleteUserDataTaskAdditionalInformationDTO.java |  98 +++++++++++++++
 .../webadmin/service/DeleteUserDataTaskDTO.java    |  78 ++++++++++++
 .../DeleteUserDataTaskSerializationTest.java       | 136 +++++++++++++++++++++
 4 files changed, 420 insertions(+)

diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTask.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTask.java
new file mode 100644
index 0000000000..2f068eb418
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTask.java
@@ -0,0 +1,108 @@
+/****************************************************************
+ * 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.service;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.james.core.Username;
+import org.apache.james.task.Task;
+import org.apache.james.task.TaskExecutionDetails;
+import org.apache.james.task.TaskType;
+import org.apache.james.user.api.DeleteUserDataTaskStep.StepName;
+
+import reactor.core.publisher.Mono;
+
+public class DeleteUserDataTask implements Task {
+    static final TaskType TYPE = TaskType.of("DeleteUserDataTask");
+
+    public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
+        private final Instant timestamp;
+        private final Username username;
+        private final Map<StepName, DeleteUserDataService.StepState> status;
+        private final Optional<StepName> fromStep;
+
+        public AdditionalInformation(Instant timestamp, Username username, Map<StepName, DeleteUserDataService.StepState> status, Optional<StepName> fromStep) {
+            this.timestamp = timestamp;
+            this.username = username;
+            this.status = status;
+            this.fromStep = fromStep;
+        }
+
+        public Optional<StepName> getFromStep() {
+            return fromStep;
+        }
+
+        public Username getUsername() {
+            return username;
+        }
+
+        public Map<StepName, DeleteUserDataService.StepState> getStatus() {
+            return status;
+        }
+
+        @Override
+        public Instant timestamp() {
+            return timestamp;
+        }
+    }
+
+    private final Username username;
+    private final DeleteUserDataService.Performer performer;
+    private final Optional<StepName> fromStep;
+
+    public DeleteUserDataTask(DeleteUserDataService service, Username username, Optional<StepName> fromStep) {
+        this.username = username;
+        this.performer = service.performer(fromStep);
+        this.fromStep = fromStep;
+    }
+
+
+    @Override
+    public Result run() {
+        return performer.deleteUserData(username)
+            .thenReturn(Result.COMPLETED)
+            .onErrorResume(e -> {
+                LOGGER.error("Error while deleting data of the user {}", username.asString(), e);
+                return Mono.just(Result.PARTIAL);
+            })
+            .block();
+    }
+
+    @Override
+    public TaskType type() {
+        return TYPE;
+    }
+
+    @Override
+    public Optional<TaskExecutionDetails.AdditionalInformation> details() {
+        return Optional.of(new AdditionalInformation(Clock.systemUTC().instant(), username, performer.getStatus().getStates(), fromStep));
+    }
+
+    public Username getUsername() {
+        return username;
+    }
+
+    public Optional<StepName> getFromStep() {
+        return fromStep;
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTaskAdditionalInformationDTO.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTaskAdditionalInformationDTO.java
new file mode 100644
index 0000000000..491acbd0eb
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTaskAdditionalInformationDTO.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.service;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.james.core.Username;
+import org.apache.james.json.DTOModule;
+import org.apache.james.server.task.json.dto.AdditionalInformationDTO;
+import org.apache.james.server.task.json.dto.AdditionalInformationDTOModule;
+import org.apache.james.user.api.DeleteUserDataTaskStep.StepName;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableMap;
+
+public class DeleteUserDataTaskAdditionalInformationDTO implements AdditionalInformationDTO {
+    public static AdditionalInformationDTOModule<DeleteUserDataTask.AdditionalInformation, DeleteUserDataTaskAdditionalInformationDTO> module() {
+        return DTOModule.forDomainObject(DeleteUserDataTask.AdditionalInformation.class)
+            .convertToDTO(DeleteUserDataTaskAdditionalInformationDTO.class)
+            .toDomainObjectConverter(dto -> new DeleteUserDataTask.AdditionalInformation(
+                dto.timestamp,
+                Username.of(dto.username),
+                dto.status.entrySet().stream()
+                    .collect(ImmutableMap.toImmutableMap(
+                        entry -> new StepName(entry.getKey()),
+                        entry -> DeleteUserDataService.StepState.valueOf(entry.getValue()))),
+                dto.fromStep.map(StepName::new)))
+            .toDTOConverter((details, type) -> new DeleteUserDataTaskAdditionalInformationDTO(
+                type,
+                details.getUsername().asString(),
+                details.getStatus().entrySet().stream()
+                    .collect(ImmutableMap.toImmutableMap(
+                        entry -> entry.getKey().asString(),
+                        entry -> entry.getValue().toString())),
+                details.getFromStep().map(StepName::asString),
+                details.timestamp()))
+            .typeName(DeleteUserDataTask.TYPE.asString())
+            .withFactory(AdditionalInformationDTOModule::new);
+    }
+
+    private final String type;
+    private final String username;
+    private final Map<String, String> status;
+    private final Optional<String> fromStep;
+    private final Instant timestamp;
+
+    public DeleteUserDataTaskAdditionalInformationDTO(@JsonProperty("type") String type,
+                                                      @JsonProperty("username") String username,
+                                                      @JsonProperty("status") Map<String, String> status,
+                                                      @JsonProperty("fromStep") Optional<String> fromStep,
+                                                      @JsonProperty("timestamp") Instant timestamp) {
+        this.type = type;
+        this.username = username;
+        this.status = status;
+        this.timestamp = timestamp;
+        this.fromStep = fromStep;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public Map<String, String> getStatus() {
+        return status;
+    }
+
+    public Instant getTimestamp() {
+        return timestamp;
+    }
+
+    public Optional<String> getFromStep() {
+        return fromStep;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTaskDTO.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTaskDTO.java
new file mode 100644
index 0000000000..93e28db9a3
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DeleteUserDataTaskDTO.java
@@ -0,0 +1,78 @@
+/****************************************************************
+ * 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.service;
+
+import java.util.Optional;
+
+import org.apache.james.core.Username;
+import org.apache.james.json.DTOModule;
+import org.apache.james.server.task.json.dto.TaskDTO;
+import org.apache.james.server.task.json.dto.TaskDTOModule;
+import org.apache.james.user.api.DeleteUserDataTaskStep.StepName;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class DeleteUserDataTaskDTO implements TaskDTO {
+
+    public static TaskDTOModule<DeleteUserDataTask, DeleteUserDataTaskDTO> module(DeleteUserDataService service) {
+        return DTOModule
+            .forDomainObject(DeleteUserDataTask.class)
+            .convertToDTO(DeleteUserDataTaskDTO.class)
+            .toDomainObjectConverter(dto -> dto.fromDTO(service))
+            .toDTOConverter(DeleteUserDataTaskDTO::toDTO)
+            .typeName(DeleteUserDataTask.TYPE.asString())
+            .withFactory(TaskDTOModule::new);
+    }
+
+    public static DeleteUserDataTaskDTO toDTO(DeleteUserDataTask domainObject, String typeName) {
+        return new DeleteUserDataTaskDTO(typeName,
+            domainObject.getUsername().asString(),
+            domainObject.getFromStep().map(StepName::asString));
+    }
+
+    private final String type;
+    private final String username;
+    private final Optional<String> fromStep;
+
+    public DeleteUserDataTaskDTO(@JsonProperty("type") String type,
+                                 @JsonProperty("username") String username,
+                                 @JsonProperty("fromStep") Optional<String> fromStep) {
+        this.type = type;
+        this.username = username;
+        this.fromStep = fromStep;
+    }
+
+    public DeleteUserDataTask fromDTO(DeleteUserDataService service) {
+        return new DeleteUserDataTask(service, Username.of(username), fromStep.map(StepName::new));
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public Optional<String> getFromStep() {
+        return fromStep;
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/service/DeleteUserDataTaskSerializationTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/service/DeleteUserDataTaskSerializationTest.java
new file mode 100644
index 0000000000..0a6ba532ff
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/service/DeleteUserDataTaskSerializationTest.java
@@ -0,0 +1,136 @@
+/****************************************************************
+ * 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.service;
+
+import java.time.Instant;
+import java.util.Optional;
+
+import org.apache.james.JsonSerializationVerifier;
+import org.apache.james.core.Username;
+import org.apache.james.user.api.DeleteUserDataTaskStep;
+import org.junit.jupiter.api.Test;
+import org.reactivestreams.Publisher;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import reactor.core.publisher.Mono;
+
+class DeleteUserDataTaskSerializationTest {
+    private static final Instant TIMESTAMP = Instant.parse("2018-11-13T12:00:55Z");
+    private static final Username USERNAME = Username.of("user");
+    private static final DeleteUserDataTaskStep.StepName STEP_A = new DeleteUserDataTaskStep.StepName("A");
+    private static final DeleteUserDataTaskStep.StepName STEP_B = new DeleteUserDataTaskStep.StepName("B");
+    private static final DeleteUserDataTaskStep.StepName STEP_C = new DeleteUserDataTaskStep.StepName("C");
+    private static final DeleteUserDataTaskStep.StepName STEP_D = new DeleteUserDataTaskStep.StepName("D");
+    private static final DeleteUserDataTaskStep A = asStep(STEP_A);
+    private static final DeleteUserDataTaskStep B = asStep(STEP_B);
+    private static final DeleteUserDataTaskStep C = asStep(STEP_C);
+    private static final DeleteUserDataTaskStep D = asStep(STEP_D);
+
+    private static DeleteUserDataTaskStep asStep(DeleteUserDataTaskStep.StepName name) {
+        return new DeleteUserDataTaskStep() {
+            @Override
+            public StepName name() {
+                return name;
+            }
+
+            @Override
+            public int priority() {
+                return 0;
+            }
+
+            @Override
+            public Publisher<Void> deleteUserData(Username username) {
+                return Mono.empty();
+            }
+        };
+    }
+
+    private static final String SERIALIZED_TASK = "{\"username\":\"user\",\"type\":\"DeleteUserDataTask\"}";
+    private static final String SERIALIZED_TASK_WITH_FROM_STEP = "{\"fromStep\":\"B\",\"username\":\"user\",\"type\":\"DeleteUserDataTask\"}";
+    private static final String SERIALIZED_ADDITIONAL_INFORMATION = "{" +
+        "  \"username\":\"user\"," +
+        "  \"status\":{" +
+        "    \"A\":\"DONE\"," +
+        "    \"B\":\"WAITING\"," +
+        "    \"C\":\"FAILED\"," +
+        "    \"D\":\"ABORTED\"}," +
+        "  \"timestamp\":\"2018-11-13T12:00:55Z\"," +
+        "  \"type\":\"DeleteUserDataTask\"" +
+        "}";
+    private static final String SERIALIZED_ADDITIONAL_INFORMATION_WITH_STEP_NAME = "{" +
+        "  \"username\":\"user\"," +
+        "  \"fromStep\":\"B\"," +
+        "  \"status\":{" +
+        "    \"A\":\"DONE\"," +
+        "    \"B\":\"WAITING\"," +
+        "    \"C\":\"FAILED\"," +
+        "    \"D\":\"ABORTED\"}," +
+        "  \"timestamp\":\"2018-11-13T12:00:55Z\"," +
+        "  \"type\":\"DeleteUserDataTask\"" +
+        "}";
+
+    private static final DeleteUserDataService SERVICE = new DeleteUserDataService(ImmutableSet.of(A, B, C, D));
+
+    @Test
+    void taskShouldBeSerializable() throws Exception {
+        JsonSerializationVerifier.dtoModule(DeleteUserDataTaskDTO.module(SERVICE))
+            .bean(new DeleteUserDataTask(SERVICE, USERNAME, Optional.empty()))
+            .json(SERIALIZED_TASK)
+            .verify();
+    }
+
+    @Test
+    void taskShouldBeSerializableWithFromStep() throws Exception {
+        JsonSerializationVerifier.dtoModule(DeleteUserDataTaskDTO.module(SERVICE))
+            .bean(new DeleteUserDataTask(SERVICE, USERNAME, Optional.of(STEP_B)))
+            .json(SERIALIZED_TASK_WITH_FROM_STEP)
+            .verify();
+    }
+
+    @Test
+    void additionalInformationShouldBeSerializable() throws Exception {
+        JsonSerializationVerifier.dtoModule(DeleteUserDataTaskAdditionalInformationDTO.module())
+            .bean(new DeleteUserDataTask.AdditionalInformation(
+                TIMESTAMP,
+                USERNAME,
+                ImmutableMap.of(STEP_A, DeleteUserDataService.StepState.DONE,
+                    STEP_B, DeleteUserDataService.StepState.WAITING,
+                    STEP_C, DeleteUserDataService.StepState.FAILED,
+                    STEP_D, DeleteUserDataService.StepState.ABORTED), Optional.empty()))
+            .json(SERIALIZED_ADDITIONAL_INFORMATION)
+            .verify();
+    }
+
+    @Test
+    void additionalInformationShouldBeSerializableWithStepName() throws Exception {
+        JsonSerializationVerifier.dtoModule(DeleteUserDataTaskAdditionalInformationDTO.module())
+            .bean(new DeleteUserDataTask.AdditionalInformation(
+                TIMESTAMP,
+                USERNAME,
+                ImmutableMap.of(STEP_A, DeleteUserDataService.StepState.DONE,
+                    STEP_B, DeleteUserDataService.StepState.WAITING,
+                    STEP_C, DeleteUserDataService.StepState.FAILED,
+                    STEP_D, DeleteUserDataService.StepState.ABORTED), Optional.of(STEP_B)))
+            .json(SERIALIZED_ADDITIONAL_INFORMATION_WITH_STEP_NAME)
+            .verify();
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 05/06: JAMES-3909 Guice bindings for delete user data routes

Posted by bt...@apache.org.
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

commit eafbb9d00890dbe67ba77b1d89d75b1404223a7b
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Fri May 19 16:31:33 2023 +0700

    JAMES-3909 Guice bindings for delete user data routes
---
 .../main/java/org/apache/james/CoreDataModule.java  |  2 ++
 .../james/modules/server/DataRoutesModules.java     | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java b/server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java
index 7ee574f0b6..ae54975465 100644
--- a/server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java
+++ b/server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java
@@ -25,6 +25,7 @@ import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.james.domainlist.lib.DomainListConfiguration;
 import org.apache.james.rrt.ForwardUsernameChangeTaskStep;
 import org.apache.james.server.core.configuration.ConfigurationProvider;
+import org.apache.james.user.api.DeleteUserDataTaskStep;
 import org.apache.james.user.api.UsernameChangeTaskStep;
 
 import com.google.inject.AbstractModule;
@@ -39,6 +40,7 @@ public class CoreDataModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), UserEntityValidator.class).addBinding().to(RecipientRewriteTableUserEntityValidator.class);
 
         Multibinder.newSetBinder(binder(), UsernameChangeTaskStep.class).addBinding().to(ForwardUsernameChangeTaskStep.class);
+        Multibinder.newSetBinder(binder(), DeleteUserDataTaskStep.class);
     }
 
     @Provides
diff --git a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
index fd64948fd5..5b3addf079 100644
--- a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
+++ b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
@@ -31,6 +31,7 @@ import org.apache.james.webadmin.dto.MappingSourceModule;
 import org.apache.james.webadmin.mdc.RequestLogger;
 import org.apache.james.webadmin.routes.AddressMappingRoutes;
 import org.apache.james.webadmin.routes.AliasRoutes;
+import org.apache.james.webadmin.routes.DeleteUserDataRoutes;
 import org.apache.james.webadmin.routes.DomainMappingsRoutes;
 import org.apache.james.webadmin.routes.DomainsRoutes;
 import org.apache.james.webadmin.routes.ForwardRoutes;
@@ -40,6 +41,9 @@ import org.apache.james.webadmin.routes.RegexMappingRoutes;
 import org.apache.james.webadmin.routes.UserCreationRequestLogger;
 import org.apache.james.webadmin.routes.UserRoutes;
 import org.apache.james.webadmin.routes.UsernameChangeRoutes;
+import org.apache.james.webadmin.service.DeleteUserDataService;
+import org.apache.james.webadmin.service.DeleteUserDataTaskAdditionalInformationDTO;
+import org.apache.james.webadmin.service.DeleteUserDataTaskDTO;
 import org.apache.james.webadmin.service.UsernameChangeService;
 import org.apache.james.webadmin.service.UsernameChangeTaskAdditionalInformationDTO;
 import org.apache.james.webadmin.service.UsernameChangeTaskDTO;
@@ -65,6 +69,7 @@ public class DataRoutesModules extends AbstractModule {
         routesMultibinder.addBinding().to(RegexMappingRoutes.class);
         routesMultibinder.addBinding().to(UserRoutes.class);
         routesMultibinder.addBinding().to(UsernameChangeRoutes.class);
+        routesMultibinder.addBinding().to(DeleteUserDataRoutes.class);
 
         Multibinder<JsonTransformerModule> jsonTransformerModuleMultibinder = Multibinder.newSetBinder(binder(), JsonTransformerModule.class);
         jsonTransformerModuleMultibinder.addBinding().to(MappingSourceModule.class);
@@ -87,4 +92,20 @@ public class DataRoutesModules extends AbstractModule {
     public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends  AdditionalInformationDTO> webAdminUsernameChangeTaskAdditionalInformationDTO() {
         return UsernameChangeTaskAdditionalInformationDTO.module();
     }
+
+    @ProvidesIntoSet
+    public TaskDTOModule<? extends Task, ? extends TaskDTO> deleteUserDataTaskDTO(DeleteUserDataService service) {
+        return DeleteUserDataTaskDTO.module(service);
+    }
+
+    @ProvidesIntoSet
+    public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends AdditionalInformationDTO> deleteUserDataTaskAdditionalInformationDTO() {
+        return DeleteUserDataTaskAdditionalInformationDTO.module();
+    }
+
+    @Named(DTOModuleInjections.WEBADMIN_DTO)
+    @ProvidesIntoSet
+    public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends AdditionalInformationDTO> webAdminDeleteUserDataTaskAdditionalInformationDTO() {
+        return DeleteUserDataTaskAdditionalInformationDTO.module();
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org