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:47 UTC

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

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