You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2023/05/26 10:30:48 UTC
[james-project] 03/07: JAMES-3909 IdentityUserDeletionTaskStep
This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 8d732379d4d768154dc574be3a65a08702e25781
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri May 19 15:28:24 2023 +0700
JAMES-3909 IdentityUserDeletionTaskStep
---
.../identity/CassandraCustomIdentityDAO.scala | 9 ++++
.../jmap/api/identity/CustomIdentityDAO.scala | 2 +
.../identity/IdentityUserDeletionTaskStep.scala | 35 ++++++++++++++
.../memory/identity/MemoryCustomIdentityDAO.scala | 2 +
.../api/identity/CustomIdentityDAOContract.scala | 27 ++++++++++-
.../IdentityUserDeletionTaskStepTest.scala | 55 ++++++++++++++++++++++
6 files changed, 128 insertions(+), 2 deletions(-)
diff --git a/server/data/data-jmap-cassandra/src/main/scala/org/apache/james/jmap/cassandra/identity/CassandraCustomIdentityDAO.scala b/server/data/data-jmap-cassandra/src/main/scala/org/apache/james/jmap/cassandra/identity/CassandraCustomIdentityDAO.scala
index 1aa1628fc5..9078b49b31 100644
--- a/server/data/data-jmap-cassandra/src/main/scala/org/apache/james/jmap/cassandra/identity/CassandraCustomIdentityDAO.scala
+++ b/server/data/data-jmap-cassandra/src/main/scala/org/apache/james/jmap/cassandra/identity/CassandraCustomIdentityDAO.scala
@@ -71,6 +71,10 @@ case class CassandraCustomIdentityDAO @Inject()(session: CqlSession,
.whereColumn(ID).isEqualTo(bindMarker(ID))
.build())
+ val deleteAllStatement: PreparedStatement = session.prepare(deleteFrom(TABLE_NAME)
+ .whereColumn(USER).isEqualTo(bindMarker(USER))
+ .build())
+
override def save(user: Username, creationRequest: IdentityCreationRequest): SMono[Identity] =
save(user, IdentityId.generate, creationRequest)
@@ -106,6 +110,11 @@ case class CassandraCustomIdentityDAO @Inject()(session: CqlSession,
.setUuid(ID, id.id)))
.`then`()
+ override def delete(username: Username): SMono[Unit] =
+ SMono(executor.executeVoid(deleteOneStatement.bind()
+ .setString(USER, username.asString())))
+ .`then`()
+
private def insert(username: Username, identity: Identity): SMono[Identity] = {
val replyTo: java.util.Set[UdtValue] = toJavaSet(identity.replyTo.getOrElse(List()))
val bcc: java.util.Set[UdtValue] = toJavaSet(identity.bcc.getOrElse(List()))
diff --git a/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/CustomIdentityDAO.scala b/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/CustomIdentityDAO.scala
index c3f80b8836..2a2fc672a7 100644
--- a/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/CustomIdentityDAO.scala
+++ b/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/CustomIdentityDAO.scala
@@ -147,6 +147,8 @@ trait CustomIdentityDAO {
def upsert(user: Username, patch: Identity): SMono[Unit]
def delete(username: Username, ids: Seq[IdentityId]): Publisher[Unit]
+
+ def delete(username: Username): Publisher[Unit]
}
class DefaultIdentitySupplier @Inject()(canSendFrom: CanSendFrom, usersRepository: UsersRepository) {
diff --git a/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/IdentityUserDeletionTaskStep.scala b/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/IdentityUserDeletionTaskStep.scala
new file mode 100644
index 0000000000..7eb3f2eb96
--- /dev/null
+++ b/server/data/data-jmap/src/main/scala/org/apache/james/jmap/api/identity/IdentityUserDeletionTaskStep.scala
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.jmap.api.identity
+
+import com.google.inject.Inject
+import org.apache.james.core.Username
+import org.apache.james.user.api.DeleteUserDataTaskStep
+import org.apache.james.user.api.DeleteUserDataTaskStep.StepName
+import org.reactivestreams.Publisher
+import reactor.core.publisher.Mono
+
+class IdentityUserDeletionTaskStep @Inject()(customIdentityDAO: CustomIdentityDAO) extends DeleteUserDataTaskStep {
+ override val name: StepName = new StepName("IdentityUserDeletionTaskStep")
+
+ override val priority: Int = 1
+
+ override def deleteUserData(username: Username): Publisher[Void] = Mono.from(customIdentityDAO.delete(username)).`then`()
+}
diff --git a/server/data/data-jmap/src/main/scala/org/apache/james/jmap/memory/identity/MemoryCustomIdentityDAO.scala b/server/data/data-jmap/src/main/scala/org/apache/james/jmap/memory/identity/MemoryCustomIdentityDAO.scala
index 9935e1570e..f9810ff176 100644
--- a/server/data/data-jmap/src/main/scala/org/apache/james/jmap/memory/identity/MemoryCustomIdentityDAO.scala
+++ b/server/data/data-jmap/src/main/scala/org/apache/james/jmap/memory/identity/MemoryCustomIdentityDAO.scala
@@ -56,4 +56,6 @@ class MemoryCustomIdentityDAO extends CustomIdentityDAO {
override def delete(username: Username, ids: Seq[IdentityId]): Publisher[Unit] = SFlux.fromIterable(ids)
.doOnNext(id => table.remove(username, id))
.`then`()
+
+ override def delete(username: Username): Publisher[Unit] = SMono.fromCallable(() => table.rowMap().remove(username))
}
\ No newline at end of file
diff --git a/server/data/data-jmap/src/test/scala/org/apache/james/jmap/api/identity/CustomIdentityDAOContract.scala b/server/data/data-jmap/src/test/scala/org/apache/james/jmap/api/identity/CustomIdentityDAOContract.scala
index 7b12a95d6c..f692860d84 100644
--- a/server/data/data-jmap/src/test/scala/org/apache/james/jmap/api/identity/CustomIdentityDAOContract.scala
+++ b/server/data/data-jmap/src/test/scala/org/apache/james/jmap/api/identity/CustomIdentityDAOContract.scala
@@ -30,8 +30,8 @@ import reactor.core.scala.publisher.{SFlux, SMono}
import scala.jdk.OptionConverters._
object CustomIdentityDAOContract {
- private val bob: Username = Username.of("bob@localhost")
- private val CREATION_REQUEST: IdentityCreationRequest = IdentityCreationRequest(name = Some(IdentityName("Bob (custom address)")),
+ val bob: Username = Username.of("bob@localhost")
+ val CREATION_REQUEST: IdentityCreationRequest = IdentityCreationRequest(name = Some(IdentityName("Bob (custom address)")),
email = bob.asMailAddress(),
replyTo = Some(List(EmailAddress(Some(EmailerName("My Boss")), new MailAddress("boss@domain.tld")))),
bcc = Some(List(EmailAddress(Some(EmailerName("My Boss 2")), new MailAddress("boss2@domain.tld")))),
@@ -124,6 +124,17 @@ trait CustomIdentityDAOContract {
.isEmpty()
}
+ @Test
+ def saveShouldNotReturnDeletedAllValues(): Unit = {
+ val identity: Identity = SMono(testee().save(bob, CREATION_REQUEST))
+ .block()
+
+ SMono(testee().delete(bob)).block()
+
+ assertThat(SFlux(testee().list(bob)).asJava().collectList().block())
+ .isEmpty()
+ }
+
@Test
def saveShouldDefineDefaultValuesInCaseSomePropertiesEmpty(): Unit = {
val identity: Identity = SMono(testee().save(bob,
@@ -158,6 +169,18 @@ trait CustomIdentityDAOContract {
.isEmpty()
}
+ @Test
+ def deleteAllShouldBeIdempotent(): Unit = {
+ val identity: Identity = SMono(testee().save(bob, CREATION_REQUEST))
+ .block()
+
+ SMono(testee().delete(bob)).block()
+ SMono(testee().delete(bob)).block()
+
+ assertThat(SFlux(testee().list(bob)).asJava().collectList().block())
+ .isEmpty()
+ }
+
@Test
def updateShouldModifyUnderlyingRecord(): Unit = {
val identity: Identity = SMono(testee().save(bob, CREATION_REQUEST))
diff --git a/server/data/data-jmap/src/test/scala/org/apache/james/jmap/memory/identity/IdentityUserDeletionTaskStepTest.scala b/server/data/data-jmap/src/test/scala/org/apache/james/jmap/memory/identity/IdentityUserDeletionTaskStepTest.scala
new file mode 100644
index 0000000000..23e9db7df9
--- /dev/null
+++ b/server/data/data-jmap/src/test/scala/org/apache/james/jmap/memory/identity/IdentityUserDeletionTaskStepTest.scala
@@ -0,0 +1,55 @@
+/****************************************************************
+ * 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.jmap.memory.identity
+
+import org.apache.james.jmap.api.identity.CustomIdentityDAOContract.{CREATION_REQUEST, bob}
+import org.apache.james.jmap.api.identity.IdentityUserDeletionTaskStep
+import org.assertj.core.api.Assertions.{assertThat, assertThatCode}
+import org.junit.jupiter.api.{BeforeEach, Test}
+import reactor.core.publisher.Flux
+import reactor.core.scala.publisher.SMono
+
+class IdentityUserDeletionTaskStepTest {
+ var identityDAO: MemoryCustomIdentityDAO = _
+ var testee: IdentityUserDeletionTaskStep = _
+
+ @BeforeEach
+ def setUp(): Unit = {
+ identityDAO = new MemoryCustomIdentityDAO()
+ testee = new IdentityUserDeletionTaskStep(identityDAO)
+ }
+
+ @Test
+ def shouldDeleteUserIdentity(): Unit = {
+ SMono(identityDAO
+ .save(bob, CREATION_REQUEST))
+ .block()
+
+ SMono(testee.deleteUserData(bob)).block()
+
+ assertThat(Flux.from(identityDAO.list(bob)).collectList().block()).isEmpty()
+ }
+
+ @Test
+ def shouldBeIdempotent(): Unit = {
+ assertThatCode(() => SMono(testee.deleteUserData(bob)).block())
+ .doesNotThrowAnyException()
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org