diff --git a/tmail-backend/apps/distributed/src/main/java/com/linagora/tmail/james/app/DistributedServer.java b/tmail-backend/apps/distributed/src/main/java/com/linagora/tmail/james/app/DistributedServer.java
index 5eaca9a202..875663f2f1 100644
--- a/tmail-backend/apps/distributed/src/main/java/com/linagora/tmail/james/app/DistributedServer.java
+++ b/tmail-backend/apps/distributed/src/main/java/com/linagora/tmail/james/app/DistributedServer.java
@@ -130,7 +130,6 @@
import com.linagora.tmail.blob.guice.BlobStoreCacheModulesChooser;
import com.linagora.tmail.blob.guice.BlobStoreConfiguration;
import com.linagora.tmail.blob.guice.BlobStoreModulesChooser;
-import com.linagora.tmail.contact.RabbitMQEmailAddressContactModule;
import com.linagora.tmail.encrypted.ClearEmailContentFactory;
import com.linagora.tmail.encrypted.EncryptedMailboxManager;
import com.linagora.tmail.encrypted.KeystoreManager;
@@ -145,6 +144,7 @@
import com.linagora.tmail.imap.TMailIMAPModule;
import com.linagora.tmail.james.jmap.ContactSupportCapabilitiesModule;
import com.linagora.tmail.james.jmap.TMailJMAPModule;
+import com.linagora.tmail.james.jmap.contact.RabbitMQEmailAddressContactModule;
import com.linagora.tmail.james.jmap.firebase.CassandraFirebaseSubscriptionRepositoryModule;
import com.linagora.tmail.james.jmap.firebase.FirebaseCommonModule;
import com.linagora.tmail.james.jmap.firebase.FirebaseModuleChooserConfiguration;
diff --git a/tmail-backend/guice/distributed/src/main/java/com/linagora/tmail/contact/RabbitMQEmailAddressContactModule.java b/tmail-backend/guice/distributed/src/main/java/com/linagora/tmail/james/jmap/contact/RabbitMQEmailAddressContactModule.java
similarity index 99%
rename from tmail-backend/guice/distributed/src/main/java/com/linagora/tmail/contact/RabbitMQEmailAddressContactModule.java
rename to tmail-backend/guice/distributed/src/main/java/com/linagora/tmail/james/jmap/contact/RabbitMQEmailAddressContactModule.java
index 0c8471004b..538ba9fe42 100644
--- a/tmail-backend/guice/distributed/src/main/java/com/linagora/tmail/contact/RabbitMQEmailAddressContactModule.java
+++ b/tmail-backend/guice/distributed/src/main/java/com/linagora/tmail/james/jmap/contact/RabbitMQEmailAddressContactModule.java
@@ -1,4 +1,4 @@
-package com.linagora.tmail.contact;
+package com.linagora.tmail.james.jmap.contact;
import jakarta.inject.Named;
import jakarta.inject.Provider;
diff --git a/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/LabelChangesMethodContract.scala b/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/LabelChangesMethodContract.scala
index 0341149e36..b3bec41da4 100644
--- a/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/LabelChangesMethodContract.scala
+++ b/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/LabelChangesMethodContract.scala
@@ -6,8 +6,8 @@ import com.google.common.collect.ImmutableList
import com.linagora.tmail.james.common.LabelChangesMethodContract.firebasePushClient
import com.linagora.tmail.james.common.probe.JmapGuiceLabelProbe
import com.linagora.tmail.james.jmap.firebase.{FirebasePushClient, FirebasePushRequest}
-import com.linagora.tmail.james.jmap.label.{LabelChange, LabelTypeName}
-import com.linagora.tmail.james.jmap.model.LabelId
+import com.linagora.tmail.james.jmap.label.{LabelTypeName}
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelId}
import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
import io.restassured.RestAssured.{`given`, requestSpecification}
import io.restassured.http.ContentType.JSON
diff --git a/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/probe/JmapGuiceLabelProbe.java b/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/probe/JmapGuiceLabelProbe.java
index 826109f282..5adb8e4420 100644
--- a/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/probe/JmapGuiceLabelProbe.java
+++ b/tmail-backend/integration-tests/jmap/jmap-integration-tests-common/src/main/scala/com/linagora/tmail/james/common/probe/JmapGuiceLabelProbe.java
@@ -7,10 +7,10 @@
import org.apache.james.core.Username;
import org.apache.james.utils.GuiceProbe;
-import com.linagora.tmail.james.jmap.label.LabelChange;
import com.linagora.tmail.james.jmap.label.LabelChangeRepository;
import com.linagora.tmail.james.jmap.label.LabelRepository;
import com.linagora.tmail.james.jmap.model.Label;
+import com.linagora.tmail.james.jmap.model.LabelChange;
import com.linagora.tmail.james.jmap.model.LabelCreationRequest;
import reactor.core.publisher.Flux;
diff --git a/tmail-backend/jmap/extensions-api/pom.xml b/tmail-backend/jmap/extensions-api/pom.xml
new file mode 100644
index 0000000000..2db88b9577
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+ com.linagora.tmail
+ tmail-backend
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+
+ jmap-extensions-api
+ Twake Mail :: JMAP :: Extensions :: API
+
+
+ 5.3.7
+
+
+
+
+ ${james.groupId}
+ james-server-data-jmap
+
+
+ ${james.groupId}
+ james-server-jmap-rfc-8621
+
+
+ ${james.groupId}
+ james-server-testing
+ test
+
+
+ ${james.groupId}
+ testing-base
+ test
+
+
+ com.github.f4b6a3
+ uuid-creator
+ ${uuid-creator.version}
+
+
+ com.google.guava
+ guava
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+
+
+ io.github.evis
+ scalafix-maven-plugin_2.13
+
+ ${project.parent.parent.basedir}/.scalafix.conf
+
+
+
+
+
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/EmailAddressContactInjectKeys.java b/tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/EmailAddressContactInjectKeys.java
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/EmailAddressContactInjectKeys.java
rename to tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/EmailAddressContactInjectKeys.java
diff --git a/tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionHelper.java b/tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionHelper.java
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionHelper.java
rename to tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionHelper.java
diff --git a/tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepository.java b/tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepository.java
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepository.java
rename to tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepository.java
diff --git a/tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionUserDeletionTaskStep.java b/tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionUserDeletionTaskStep.java
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionUserDeletionTaskStep.java
rename to tmail-backend/jmap/extensions-api/src/main/java/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionUserDeletionTaskStep.java
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/PublicAssetTotalSizeLimit.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/PublicAssetTotalSizeLimit.scala
new file mode 100644
index 0000000000..5a2c9f6b5b
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/PublicAssetTotalSizeLimit.scala
@@ -0,0 +1,20 @@
+package com.linagora.tmail.james.jmap
+
+import eu.timepit.refined
+import org.apache.james.jmap.core.UnsignedInt.{UnsignedInt, UnsignedIntConstraint}
+import org.apache.james.util.Size
+
+import scala.util.{Failure, Success, Try}
+
+object PublicAssetTotalSizeLimit {
+ val DEFAULT: PublicAssetTotalSizeLimit = PublicAssetTotalSizeLimit.of(Size.of(20L, Size.Unit.M)).get
+
+ def of(size: Size): Try[PublicAssetTotalSizeLimit] = refined.refineV[UnsignedIntConstraint](size.asBytes()) match {
+ case Right(value) => Success(PublicAssetTotalSizeLimit(value))
+ case Left(error) => Failure(new NumberFormatException(error))
+ }
+}
+
+case class PublicAssetTotalSizeLimit(value: UnsignedInt) {
+ def asLong(): Long = value.value
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStep.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStep.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStep.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStep.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStep.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStep.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStep.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStep.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContact.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContact.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContact.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContact.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactEventModule.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactEventModule.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactEventModule.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactEventModule.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListener.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListener.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListener.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListener.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessage.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessage.scala
similarity index 93%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessage.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessage.scala
index c11b14a1b9..acd0def1c9 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessage.scala
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessage.scala
@@ -2,8 +2,8 @@ package com.linagora.tmail.james.jmap.contact
import java.util.Locale
-import com.linagora.tmail.james.jmap.contact.TmailContactMessageScope.{DOMAIN, USER}
-import com.linagora.tmail.james.jmap.contact.TmailContactMessageType.{ADDITION, REMOVAL, UPDATE}
+import TmailContactMessageScope.{DOMAIN, USER}
+import TmailContactMessageType.{ADDITION, REMOVAL, UPDATE}
import org.apache.james.core.{Domain, MailAddress, Username}
import scala.util.Try
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessageHandler.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessageHandler.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessageHandler.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactMessageHandler.scala
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepository.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepository.scala
new file mode 100644
index 0000000000..02050e6624
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepository.scala
@@ -0,0 +1,19 @@
+package com.linagora.tmail.james.jmap.label
+
+import com.linagora.tmail.james.jmap.label.LabelChangeRepository.DEFAULT_MAX_IDS_TO_RETURN
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelChanges}
+import org.apache.james.jmap.api.change.{Limit, State}
+import org.apache.james.jmap.api.model.AccountId
+import org.reactivestreams.Publisher
+
+object LabelChangeRepository {
+ val DEFAULT_MAX_IDS_TO_RETURN : Limit = Limit.of(256)
+}
+
+trait LabelChangeRepository {
+ def save(labelChange: LabelChange): Publisher[Void]
+
+ def getSinceState(accountId: AccountId, state: State, maxIdsToReturn: Option[Limit] = Some(DEFAULT_MAX_IDS_TO_RETURN)): Publisher[LabelChanges]
+
+ def getLatestState(accountId: AccountId): Publisher[State]
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelRepository.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelRepository.scala
new file mode 100644
index 0000000000..b96f5dd83b
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelRepository.scala
@@ -0,0 +1,25 @@
+package com.linagora.tmail.james.jmap.label
+
+import java.util
+
+import com.linagora.tmail.james.jmap.model.{Color, DisplayName, Label, LabelCreationRequest, LabelId}
+import org.apache.james.core.Username
+import org.reactivestreams.Publisher
+
+trait LabelRepository {
+ def addLabel(username: Username, labelCreationRequest: LabelCreationRequest): Publisher[Label]
+
+ def addLabel(username: Username, label: Label): Publisher[Void]
+
+ def addLabels(username: Username, labelCreationRequests: util.Collection[LabelCreationRequest]): Publisher[Label]
+
+ def updateLabel(username: Username, labelId: LabelId, newDisplayName: Option[DisplayName] = None, newColor: Option[Color] = None): Publisher[Void]
+
+ def getLabels(username: Username, ids: util.Collection[LabelId]): Publisher[Label]
+
+ def listLabels(username: Username): Publisher[Label]
+
+ def deleteLabel(username: Username, labelId: LabelId): Publisher[Void]
+
+ def deleteAllLabels(username: Username): Publisher[Void]
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUserDeletionTaskStep.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUserDeletionTaskStep.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUserDeletionTaskStep.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUserDeletionTaskStep.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUsernameChangeTaskStep.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUsernameChangeTaskStep.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUsernameChangeTaskStep.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/label/LabelUsernameChangeTaskStep.scala
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala
new file mode 100644
index 0000000000..b0769e00a7
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala
@@ -0,0 +1,117 @@
+package com.linagora.tmail.james.jmap.model
+
+import java.time.ZonedDateTime
+import java.util.UUID
+
+import eu.timepit.refined.auto._
+import org.apache.james.jmap.api.model.ExpireTimeInvalidException.TIME_FORMATTER
+import org.apache.james.jmap.api.model.TypeName
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.{Id, Properties}
+import org.apache.james.jmap.method.WithoutAccountId
+
+import scala.util.Try
+
+object FirebaseSubscriptionId {
+ def generate(): FirebaseSubscriptionId = FirebaseSubscriptionId(UUID.randomUUID)
+
+ def liftOrThrow(unparsedId: UnparsedFirebaseSubscriptionId): Either[IllegalArgumentException, FirebaseSubscriptionId] =
+ liftOrThrow(unparsedId.id.value)
+
+ def liftOrThrow(value: String): Either[IllegalArgumentException, FirebaseSubscriptionId] =
+ Try(UUID.fromString(value))
+ .map(value1 => FirebaseSubscriptionId(value1))
+ .toEither
+ .left.map(e => new IllegalArgumentException("FirebaseSubscriptionId is invalid", e))
+}
+
+case class FirebaseSubscriptionId(value: UUID) {
+ def serialize: String = value.toString
+
+ def asUnparsedFirebaseSubscriptionId: UnparsedFirebaseSubscriptionId =
+ UnparsedFirebaseSubscriptionId(Id.validate(serialize).toOption.get)
+}
+
+case class DeviceClientId(value: String) extends AnyVal
+
+case class FirebaseToken(value: String) extends AnyVal
+
+case class FirebaseSubscriptionExpiredTime(value: ZonedDateTime) {
+ def isAfter(date: ZonedDateTime): Boolean = value.isAfter(date)
+
+ def isBefore(date: ZonedDateTime): Boolean = value.isBefore(date)
+}
+
+object FirebaseSubscriptionCreation {
+ val serverSetProperty: Set[String] = Set("id")
+ val assignableProperties: Set[String] = Set("deviceClientId", "token", "expires", "types")
+ val knownProperties: Set[String] = assignableProperties ++ serverSetProperty
+}
+
+case class FirebaseSubscriptionCreationRequest(deviceClientId: DeviceClientId,
+ token: FirebaseToken,
+ expires: Option[FirebaseSubscriptionExpiredTime] = None,
+ types: Seq[TypeName]) {
+
+ def validate: Either[IllegalArgumentException, FirebaseSubscriptionCreationRequest] =
+ validateTypes
+
+ private def validateTypes: Either[IllegalArgumentException, FirebaseSubscriptionCreationRequest] =
+ if (types.isEmpty) {
+ scala.Left(new IllegalArgumentException("types must not be empty"))
+ } else {
+ Right(this)
+ }
+}
+
+object FirebaseSubscription {
+ val EXPIRES_TIME_MAX_DAY: Int = 7
+ val allProperties: Properties = Properties("id", "deviceClientId", "expires", "types")
+ val idProperty: Properties = Properties("id")
+
+ def from(creationRequest: FirebaseSubscriptionCreationRequest,
+ expireTime: FirebaseSubscriptionExpiredTime): FirebaseSubscription =
+ FirebaseSubscription(id = FirebaseSubscriptionId.generate(),
+ deviceClientId = creationRequest.deviceClientId,
+ token = creationRequest.token,
+ expires = expireTime,
+ types = creationRequest.types)
+}
+
+case class FirebaseSubscription(id: FirebaseSubscriptionId,
+ deviceClientId: DeviceClientId,
+ token: FirebaseToken,
+ expires: FirebaseSubscriptionExpiredTime,
+ types: Seq[TypeName]) {
+ def withTypes(types: Seq[TypeName]): FirebaseSubscription = copy(types = types)
+
+ def withExpires(expires: FirebaseSubscriptionExpiredTime): FirebaseSubscription = copy(expires = expires)
+}
+
+case class FirebaseSubscriptionNotFoundException(id: FirebaseSubscriptionId) extends RuntimeException
+
+case class ExpireTimeInvalidException(expires: ZonedDateTime, message: String) extends IllegalStateException(s"`${expires.format(TIME_FORMATTER)}` $message")
+
+case class DeviceClientIdInvalidException(deviceClientId: DeviceClientId, message: String) extends IllegalArgumentException(s"`${deviceClientId.value}` $message")
+
+case class TokenInvalidException(message: String) extends IllegalArgumentException(message)
+
+case class MissingOrInvalidFirebaseCredentialException(message: String) extends IllegalArgumentException(message)
+
+case class UnparsedFirebaseSubscriptionId(id: Id)
+
+case class FirebaseSubscriptionIds(list: List[UnparsedFirebaseSubscriptionId])
+
+case class FirebaseSubscriptionGetRequest(ids: Option[FirebaseSubscriptionIds],
+ properties: Option[Properties]) extends WithoutAccountId {
+
+ def validateProperties: Either[IllegalArgumentException, Properties] =
+ properties match {
+ case None => Right(FirebaseSubscription.allProperties)
+ case Some(value) =>
+ value -- FirebaseSubscription.allProperties match {
+ case invalidProperties if invalidProperties.isEmpty() => Right(value ++ FirebaseSubscription.idProperty)
+ case invalidProperties: Properties => Left(new IllegalArgumentException(s"The following properties [${invalidProperties.format()}] do not exist."))
+ }
+ }
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala
new file mode 100644
index 0000000000..2a38004603
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala
@@ -0,0 +1,86 @@
+package com.linagora.tmail.james.jmap.model
+
+import java.util.UUID
+
+import eu.timepit.refined
+import eu.timepit.refined.auto._
+import eu.timepit.refined.string.MatchesRegex
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.{AccountId, Id, Properties, SetError, UuidState}
+import org.apache.james.jmap.mail.Keyword
+import org.apache.james.jmap.method.WithAccountId
+
+case class LabelCreationParseException(setError: SetError) extends Exception
+
+object LabelId {
+ def fromKeyword(keyword: Keyword): LabelId =
+ LabelId(Id.validate(keyword.flagName).toOption.get)
+
+ def generate(): LabelId =
+ LabelId(Id.validate(UUID.randomUUID().toString).toOption.get)
+}
+
+case class LabelId(id: Id) {
+ def toKeyword: Keyword =
+ Keyword.of(id.value).get
+
+ def asUnparsedLabelId: UnparsedLabelId =
+ UnparsedLabelId(id)
+
+ def serialize: String = id.value
+}
+
+object KeywordUtil {
+ def generate(): Keyword =
+ Keyword.of(UUID.randomUUID().toString).get
+}
+
+case class DisplayName(value: String)
+
+object Color {
+ private type ColorRegex = MatchesRegex["^#[a-fA-F0-9]{6}$"]
+
+ def validate(string: String): Either[IllegalArgumentException, Color] =
+ refined.refineV[ColorRegex](string) match {
+ case Left(_) => scala.Left(new IllegalArgumentException(s"The string should be a valid hexadecimal color value following this pattern #[a-fA-F0-9]{6}"))
+ case Right(value) => scala.Right(Color(value))
+ }
+}
+
+case class Color(value: String)
+
+object LabelCreationRequest {
+ val serverSetProperty = Set("id", "keyword")
+ val assignableProperties = Set("displayName", "color")
+ val knownProperties = assignableProperties ++ serverSetProperty
+}
+
+case class LabelCreationRequest(displayName: DisplayName, color: Option[Color]) {
+ def toLabel: Label = {
+ val keyword: Keyword = KeywordUtil.generate()
+
+ Label(id = LabelId.fromKeyword(keyword),
+ displayName = displayName,
+ keyword = keyword,
+ color = color)
+ }
+}
+
+object Label {
+ val allProperties: Properties = Properties("id", "displayName", "keyword", "color")
+ val idProperty: Properties = Properties("id")
+}
+
+case class Label(id: LabelId, displayName: DisplayName, keyword: Keyword, color: Option[Color]) {
+ def update(newDisplayName: Option[DisplayName], newColor: Option[Color]): Label =
+ copy(displayName = newDisplayName.getOrElse(displayName),
+ color = newColor.orElse(color))
+}
+
+case class LabelNotFoundException(id: LabelId) extends RuntimeException
+
+case class UnparsedLabelId(id: Id) {
+ def asLabelId: LabelId = LabelId(id)
+}
+
+case class LabelIds(list: List[UnparsedLabelId])
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala
new file mode 100644
index 0000000000..9108cebc20
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala
@@ -0,0 +1,64 @@
+package com.linagora.tmail.james.jmap.model
+
+import java.util.function.Supplier
+
+import org.apache.james.jmap.api.change.{JmapChange, State}
+import org.apache.james.jmap.api.model.AccountId
+
+case class LabelChange(accountId: AccountId,
+ created: Set[LabelId] = Set(),
+ updated: Set[LabelId] = Set(),
+ destroyed: Set[LabelId] = Set(),
+ state: State) extends JmapChange {
+ override def getAccountId: AccountId = accountId
+
+ override def isNoop: Boolean = created.isEmpty && updated.isEmpty && destroyed.isEmpty
+
+ override def forSharee(accountId: AccountId, state: Supplier[State]): JmapChange =
+ LabelChange(accountId = accountId,
+ created = created,
+ updated = updated,
+ destroyed = destroyed,
+ state = state.get())
+}
+
+object LabelChanges {
+ def from(labelChange: LabelChange): LabelChanges = LabelChanges(
+ created = labelChange.created,
+ updated = labelChange.updated,
+ destroyed = labelChange.destroyed,
+ newState = labelChange.state)
+
+ def initial(): LabelChanges = LabelChanges(
+ created = Set(),
+ updated = Set(),
+ destroyed = Set(),
+ newState = State.INITIAL)
+
+ def merge(limit: Int, change1: LabelChanges, change2: LabelChanges): LabelChanges =
+ change1.canAppendMoreItem match {
+ case false => change1
+ case true if change1.newState.equals(State.INITIAL) => change2
+ case true =>
+ val createdTemp: Set[LabelId] = (change1.created ++ change2.created).diff(change2.destroyed)
+ val updatedTemp: Set[LabelId] = (change1.updated ++ change2.updated.diff(createdTemp)).diff(change2.destroyed)
+ val destroyedTemp: Set[LabelId] = change1.destroyed ++ change2.destroyed.diff(change1.created)
+ if (createdTemp.size + updatedTemp.size + destroyedTemp.size > limit) {
+ change1.copy(hasMoreChanges = true, canAppendMoreItem = false)
+ } else {
+ change1.copy(created = createdTemp,
+ updated = updatedTemp,
+ destroyed = destroyedTemp,
+ newState = change2.newState)
+ }
+ }
+}
+
+case class LabelChanges(created: Set[LabelId] = Set(),
+ updated: Set[LabelId] = Set(),
+ destroyed: Set[LabelId] = Set(),
+ hasMoreChanges: Boolean = false,
+ newState: State,
+ private val canAppendMoreItem: Boolean = true) {
+ def getAllChanges: Set[LabelId] = created ++ updated ++ destroyed
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepository.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepository.scala
new file mode 100644
index 0000000000..0db1323e75
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepository.scala
@@ -0,0 +1,34 @@
+package com.linagora.tmail.james.jmap.publicAsset
+
+import org.apache.james.blob.api.BlobId
+import org.apache.james.core.Username
+import org.apache.james.jmap.api.model.IdentityId
+import org.reactivestreams.Publisher
+import reactor.core.scala.publisher.SMono
+
+trait PublicAssetRepository {
+ def create(username: Username, creationRequest: PublicAssetCreationRequest): Publisher[PublicAssetStorage]
+
+ def update(username: Username, id: PublicAssetId, identityIds: Set[IdentityId]): Publisher[Void]
+
+ def remove(username: Username, id: PublicAssetId): Publisher[Void]
+
+ def revoke(username: Username): Publisher[Void]
+
+ def get(username: Username, ids: Set[PublicAssetId]): Publisher[PublicAssetStorage]
+
+ def get(username: Username, id: PublicAssetId): Publisher[PublicAssetStorage] = get(username, Set(id))
+
+ def list(username: Username): Publisher[PublicAssetStorage]
+
+ def listPublicAssetMetaDataOrderByIdAsc(username: Username): Publisher[PublicAssetMetadata]
+
+ def listAllBlobIds(): Publisher[BlobId]
+
+ def updateIdentityIds(username: Username, id: PublicAssetId, identityIdsToAdd: Seq[IdentityId], identityIdsToRemove: Seq[IdentityId]): Publisher[Void] =
+ SMono(get(username, id))
+ .map(publicAsset => (publicAsset.identityIds.toSet ++ identityIdsToAdd.toSet) -- identityIdsToRemove.toSet)
+ .flatMap(identityIds => SMono(update(username, id, identityIds)))
+
+ def getTotalSize(username: Username): Publisher[Long]
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetStorage.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRequest.scala
similarity index 91%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetStorage.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRequest.scala
index daf863edbf..9152612436 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetStorage.scala
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRequest.scala
@@ -2,7 +2,6 @@ package com.linagora.tmail.james.jmap.publicAsset
import java.io.InputStream
import java.net.URI
-import java.time.Instant
import java.util.UUID
import com.github.f4b6a3.uuid.UuidCreator
@@ -16,32 +15,21 @@ import org.apache.james.core.Username
import org.apache.james.jmap.api.model.IdentityId
import org.apache.james.jmap.api.model.Size.Size
import org.apache.james.jmap.core.JmapRfc8621Configuration
+import org.apache.james.jmap.mail.{BlobId => JmapBlobId}
import org.apache.james.mailbox.model.ContentType
import scala.util.Try
-object PublicAssetIdFactory {
- def generate(): PublicAssetId = PublicAssetId(UuidCreator.getTimeBased)
-
- def from(value: String): Either[(String, IllegalArgumentException), PublicAssetId] =
- Try(PublicAssetId(UUID.fromString(value)))
- .toEither
- .left.map(e => value -> new IllegalArgumentException(e))
+object PublicAssetSetCreationRequest {
+ val knownProperties: Set[String] = Set("blobId", "identityIds")
}
-object PublicAssetId {
- def fromString(value: String): Try[PublicAssetId] =
- Try(PublicAssetId(UUID.fromString(value)))
-}
+case class PublicAssetSetCreationRequest(blobId: JmapBlobId, identityIds: Option[Map[IdentityId, Boolean]] = None)
-case class PublicAssetId(value: UUID) {
- def asString(): String = value.toString
-}
-
-object PublicAssetURIPrefix {
- def fromConfiguration(configuration: JmapRfc8621Configuration): Either[Throwable, URI] =
- Try(new URI(configuration.urlPrefixString)).toEither
-}
+case class PublicAssetCreationRequest(size: Size,
+ contentType: ImageContentType,
+ identityIds: Seq[IdentityId] = Seq.empty,
+ content: () => InputStream)
object PublicURI {
def fromString(value: String): Either[Throwable, PublicURI] = Try(new URI(value))
@@ -62,6 +50,29 @@ object PublicURI {
}
}
+object PublicAssetIdFactory {
+ def generate(): PublicAssetId = PublicAssetId(UuidCreator.getTimeBased)
+
+ def from(value: String): Either[(String, IllegalArgumentException), PublicAssetId] =
+ Try(PublicAssetId(UUID.fromString(value)))
+ .toEither
+ .left.map(e => value -> new IllegalArgumentException(e))
+}
+
+object PublicAssetId {
+ def fromString(value: String): Try[PublicAssetId] =
+ Try(PublicAssetId(UUID.fromString(value)))
+}
+
+case class PublicAssetId(value: UUID) {
+ def asString(): String = value.toString
+}
+
+object PublicAssetURIPrefix {
+ def fromConfiguration(configuration: JmapRfc8621Configuration): Either[Throwable, URI] =
+ Try(new URI(configuration.urlPrefixString)).toEither
+}
+
case class PublicURI(value: URI) extends AnyVal
object ImageContentType {
@@ -87,24 +98,6 @@ object ImageContentType {
.map(e => PublicAssetInvalidContentTypeException(e))
}
-trait PublicAssetException extends RuntimeException {
- def message: String
-
- override def getMessage: String = message
-}
-
-case class PublicAssetInvalidContentTypeException(contentType: String) extends PublicAssetException {
- override val message: String = s"Invalid content type: $contentType"
-}
-
-case class PublicAssetNotFoundException(id: PublicAssetId) extends PublicAssetException {
- override val message: String = s"Public asset not found: ${id.asString()}"
-}
-
-case class PublicAssetQuotaLimitExceededException(limitAsByte: Long) extends PublicAssetException {
- override val message: String = s"Exceeding public asset quota limit of $limitAsByte bytes"
-}
-
case class PublicAssetStorage(id: PublicAssetId,
publicURI: PublicURI,
size: Size,
@@ -117,11 +110,6 @@ case class PublicAssetStorage(id: PublicAssetId,
def contentTypeAsString(): String = contentType.value
}
-case class PublicAssetCreationRequest(size: Size,
- contentType: ImageContentType,
- identityIds: Seq[IdentityId] = Seq.empty,
- content: () => InputStream)
-
object PublicAssetMetadata {
def from(publicAsset: PublicAssetStorage): PublicAssetMetadata =
PublicAssetMetadata(
@@ -150,4 +138,26 @@ case class PublicAssetMetadata(id: PublicAssetId,
content = () => content)
def sizeAsLong(): java.lang.Long = size.value
+}
+
+trait PublicAssetException extends RuntimeException {
+ def message: String
+
+ override def getMessage: String = message
+}
+
+case class PublicAssetNotFoundException(id: PublicAssetId) extends PublicAssetException {
+ override val message: String = s"Public asset not found: ${id.asString()}"
+}
+
+case class PublicAssetQuotaLimitExceededException(limitAsByte: Long) extends PublicAssetException {
+ override val message: String = s"Exceeding public asset quota limit of $limitAsByte bytes"
+}
+
+case class PublicAssetInvalidContentTypeException(contentType: String) extends PublicAssetException {
+ override val message: String = s"Invalid content type: $contentType"
+}
+
+case class PublicAssetIdentityIdNotFoundException(identityIds: Seq[IdentityId]) extends PublicAssetException {
+ override val message: String = s"IdentityId not found: ${identityIds.map(_.id.toString).mkString(", ")}"
}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetService.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetService.scala
similarity index 92%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetService.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetService.scala
index ee1390b409..bff522b6ef 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetService.scala
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetService.scala
@@ -1,6 +1,6 @@
package com.linagora.tmail.james.jmap.publicAsset
-import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration
+import com.linagora.tmail.james.jmap.PublicAssetTotalSizeLimit
import jakarta.inject.Inject
import org.apache.james.core.Username
import org.apache.james.jmap.api.identity.IdentityRepository
@@ -10,7 +10,7 @@ import reactor.core.scala.publisher.{SFlux, SMono}
class PublicAssetSetService @Inject()(val identityRepository: IdentityRepository,
val publicAssetRepository: PublicAssetRepository,
- val configuration: JMAPExtensionConfiguration) {
+ val publicAssetTotalSizeLimit: PublicAssetTotalSizeLimit) {
def checkIdentityIdsExist(identityIds: Seq[IdentityId], session: MailboxSession): SMono[Seq[IdentityId]] =
SFlux(identityRepository.list(session.getUser))
@@ -27,7 +27,7 @@ class PublicAssetSetService @Inject()(val identityRepository: IdentityRepository
.onErrorResume {
case _: PublicAssetQuotaLimitExceededException => cleanUpPublicAsset(username, creationRequest.size.value)
.filter(cleanedUpSize => cleanedUpSize >= creationRequest.size.value)
- .switchIfEmpty(SMono.error(new PublicAssetQuotaLimitExceededException(configuration.publicAssetTotalSizeLimit.asLong())))
+ .switchIfEmpty(SMono.error(new PublicAssetQuotaLimitExceededException(publicAssetTotalSizeLimit.asLong())))
.flatMap(_ => SMono(publicAssetRepository.create(username, creationRequest)))
}
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettings.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettings.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettings.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettings.scala
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepository.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepository.scala
new file mode 100644
index 0000000000..581da104cb
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepository.scala
@@ -0,0 +1,17 @@
+package com.linagora.tmail.james.jmap.settings
+
+import org.apache.james.core.Username
+import org.apache.james.jmap.core.UuidState
+import org.reactivestreams.Publisher
+
+trait JmapSettingsRepository {
+ def get(username: Username): Publisher[JmapSettings]
+
+ def getLatestState(username: Username): Publisher[UuidState]
+
+ def reset(username: Username, settings: JmapSettingsUpsertRequest): Publisher[SettingsStateUpdate]
+
+ def updatePartial(username: Username, settingsPatch: JmapSettingsPatch): Publisher[SettingsStateUpdate]
+
+ def delete(username: Username): Publisher[Void]
+}
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUserDeletionTaskStep.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUserDeletionTaskStep.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUserDeletionTaskStep.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUserDeletionTaskStep.scala
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUsernameChangeTaskStep.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUsernameChangeTaskStep.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUsernameChangeTaskStep.scala
rename to tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsUsernameChangeTaskStep.scala
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/ForbiddenException.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/ForbiddenException.scala
new file mode 100644
index 0000000000..79a83e3bb5
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/ForbiddenException.scala
@@ -0,0 +1,3 @@
+package com.linagora.tmail.james.jmap.ticket
+
+case class ForbiddenException() extends RuntimeException
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/Ticket.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/Ticket.scala
new file mode 100644
index 0000000000..9f4ea8d490
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/Ticket.scala
@@ -0,0 +1,12 @@
+package com.linagora.tmail.james.jmap.ticket
+
+import java.net.InetAddress
+
+import org.apache.james.core.Username
+import org.apache.james.jmap.core.UTCDate
+
+case class Ticket(clientAddress: InetAddress,
+ value: TicketValue,
+ generatedOn: UTCDate,
+ validUntil: UTCDate,
+ username: Username)
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketStore.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketStore.scala
new file mode 100644
index 0000000000..ee36efa622
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketStore.scala
@@ -0,0 +1,11 @@
+package com.linagora.tmail.james.jmap.ticket
+
+import reactor.core.scala.publisher.SMono
+
+trait TicketStore {
+ def persist(ticket: Ticket): SMono[Unit]
+
+ def retrieve(value: TicketValue): SMono[Ticket]
+
+ def delete(ticketValue: TicketValue): SMono[Unit]
+}
diff --git a/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketValue.scala b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketValue.scala
new file mode 100644
index 0000000000..8fdeba45e2
--- /dev/null
+++ b/tmail-backend/jmap/extensions-api/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketValue.scala
@@ -0,0 +1,15 @@
+package com.linagora.tmail.james.jmap.ticket
+
+import java.util.UUID
+
+import scala.util.Try
+
+object TicketValue {
+ def parse(string: String): Either[IllegalArgumentException, TicketValue] = Try(UUID.fromString(string))
+ .map(TicketValue(_))
+ .fold(e => Left(new IllegalArgumentException("TicketValue must be backed by a UUID", e)), Right(_))
+
+ def generate: TicketValue = TicketValue(UUID.randomUUID())
+}
+
+case class TicketValue(value: UUID)
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactSearchEngineContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactSearchEngineContract.scala
similarity index 98%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactSearchEngineContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactSearchEngineContract.scala
index b68bdfa53f..c9b109f87c 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactSearchEngineContract.scala
+++ b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactSearchEngineContract.scala
@@ -2,7 +2,7 @@ package com.linagora.tmail.james.jmap.contact
import java.util.stream.IntStream
-import com.linagora.tmail.james.jmap.contact.EmailAddressContactSearchEngineContract.{accountId, accountIdB, bigContactsNumber, contactEmptyNameFieldsA, contactEmptyNameFieldsB, contactFieldsA, contactFieldsB, contactFieldsFrench, domain, firstnameB, mailAddressA, otherContactEmptyNameFields, otherContactFields, otherContactFieldsWithUppercaseEmail, otherMailAddress, surnameB}
+import EmailAddressContactSearchEngineContract.{accountId, accountIdB, bigContactsNumber, contactEmptyNameFieldsA, contactEmptyNameFieldsB, contactFieldsA, contactFieldsB, contactFieldsFrench, domain, firstnameB, mailAddressA, otherContactEmptyNameFields, otherContactFields, otherContactFieldsWithUppercaseEmail, otherMailAddress, surnameB}
import org.apache.james.core.{Domain, MailAddress, Username}
import org.apache.james.jmap.api.model.AccountId
import org.assertj.core.api.Assertions.{assertThat, assertThatCode, assertThatThrownBy}
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/QueryType.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/contact/QueryType.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/QueryType.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/contact/QueryType.scala
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepositoryContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepositoryContract.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepositoryContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/firebase/FirebaseSubscriptionRepositoryContract.scala
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepositoryContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepositoryContract.scala
similarity index 95%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepositoryContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepositoryContract.scala
index 622f2f90e5..952a7662a9 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepositoryContract.scala
+++ b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepositoryContract.scala
@@ -4,7 +4,7 @@ import java.time.ZonedDateTime
import java.util.stream.IntStream
import com.linagora.tmail.james.jmap.label.LabelChangeRepositoryContract.DATE
-import com.linagora.tmail.james.jmap.model.LabelId
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelChanges, LabelId}
import org.apache.james.core.Username
import org.apache.james.jmap.api.change.{Limit, State}
import org.apache.james.jmap.api.exception.ChangeNotFoundException
@@ -344,20 +344,3 @@ trait LabelChangeRepositoryContract {
}
}
-
-class MemoryLabelChangeRepositoryTest extends LabelChangeRepositoryContract {
- var repository: MemoryLabelChangeRepository = _
- var updatableTickingClock: UpdatableTickingClock = _
-
- override def testee: LabelChangeRepository = repository
-
- override def stateFactory: State.Factory = State.Factory.DEFAULT
-
- override def setClock(newTime: ZonedDateTime): Unit = updatableTickingClock.setInstant(newTime.toInstant)
-
- @BeforeEach
- def setup(): Unit = {
- updatableTickingClock = new UpdatableTickingClock(DATE.toInstant)
- repository = MemoryLabelChangeRepository(updatableTickingClock)
- }
-}
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/LabelRepositoryContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/label/LabelRepositoryContract.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/LabelRepositoryContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/label/LabelRepositoryContract.scala
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepositoryContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepositoryContract.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepositoryContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepositoryContract.scala
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetServiceContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetServiceContract.scala
similarity index 98%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetServiceContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetServiceContract.scala
index a9f30d3f2c..a4d92cedc1 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetServiceContract.scala
+++ b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetServiceContract.scala
@@ -3,7 +3,7 @@ package com.linagora.tmail.james.jmap.publicAsset
import java.time.Instant
import java.time.temporal.ChronoUnit
-import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT
+import com.linagora.tmail.james.jmap.PublicAssetTotalSizeLimit
import com.linagora.tmail.james.jmap.publicAsset.PublicAssetServiceContract.{CLOCK, IDENTITY1, IDENTITY_ID1, PUBLIC_ASSET_TOTAL_SIZE_LIMIT_IN_CONFIGURED, identityRepository}
import org.apache.james.core.Username
import org.apache.james.jmap.api.identity.IdentityRepository
@@ -30,7 +30,7 @@ object PublicAssetServiceContract {
val identityRepository: IdentityRepository = mock(classOf[IdentityRepository])
- val PUBLIC_ASSET_TOTAL_SIZE_LIMIT_IN_CONFIGURED: Long = PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT.asLong()
+ val PUBLIC_ASSET_TOTAL_SIZE_LIMIT_IN_CONFIGURED: Long = PublicAssetTotalSizeLimit.DEFAULT.asLong()
val CLOCK = new UpdatableTickingClock(Instant.now())
}
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepositoryContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepositoryContract.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepositoryContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepositoryContract.scala
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/ticket/TicketStoreContract.scala b/tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/ticket/TicketStoreContract.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/ticket/TicketStoreContract.scala
rename to tmail-backend/jmap/extensions-api/src/test/scala/com/linagora/tmail/james/jmap/ticket/TicketStoreContract.scala
diff --git a/tmail-backend/jmap/extensions-cassandra/pom.xml b/tmail-backend/jmap/extensions-cassandra/pom.xml
index 0fe78b4f63..86f7af7002 100644
--- a/tmail-backend/jmap/extensions-cassandra/pom.xml
+++ b/tmail-backend/jmap/extensions-cassandra/pom.xml
@@ -12,17 +12,6 @@
Twake Mail :: JMAP :: Extensions :: Cassandra
-
- ${project.groupId}
- jmap-extensions
-
-
- ${project.groupId}
- jmap-extensions
- test-jar
- test
-
-
${james.groupId}
apache-james-backends-cassandra
@@ -53,6 +42,10 @@
james-server-guice-common
test
+
+ ${james.groupId}
+ james-server-guice-configuration
+
${james.groupId}
james-server-testing
@@ -64,8 +57,13 @@
test
- org.testcontainers
- testcontainers
+ ${project.groupId}
+ jmap-extensions-api
+
+
+ ${project.groupId}
+ jmap-extensions-api
+ test-jar
test
@@ -73,6 +71,11 @@
mockito-core
test
+
+ org.testcontainers
+ testcontainers
+ test
+
diff --git a/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeDAO.scala b/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeDAO.scala
index c997b6d1b1..54892df980 100644
--- a/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeDAO.scala
+++ b/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeDAO.scala
@@ -8,7 +8,7 @@ import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder.{ASC, DE
import com.datastax.oss.driver.api.core.{CqlIdentifier, CqlSession}
import com.datastax.oss.driver.api.querybuilder.QueryBuilder.{bindMarker, insertInto, selectFrom}
import com.datastax.oss.driver.api.querybuilder.SchemaBuilder.RowsPerPartition.rows
-import com.linagora.tmail.james.jmap.model.LabelId
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelId}
import jakarta.inject.Inject
import org.apache.james.backends.cassandra.components.CassandraModule
import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor
diff --git a/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepository.scala b/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepository.scala
index 6ef7f36e96..24779c5f5d 100644
--- a/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepository.scala
+++ b/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepository.scala
@@ -1,6 +1,7 @@
package com.linagora.tmail.james.jmap.label
import com.linagora.tmail.james.jmap.label.LabelChangeRepository.DEFAULT_MAX_IDS_TO_RETURN
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelChanges}
import jakarta.inject.Inject
import org.apache.james.jmap.api.change.{Limit, State}
import org.apache.james.jmap.api.exception.ChangeNotFoundException
diff --git a/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepository.scala b/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepository.scala
index 64d3dd7e96..6b52009ab4 100644
--- a/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepository.scala
+++ b/tmail-backend/jmap/extensions-cassandra/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepository.scala
@@ -2,11 +2,10 @@ package com.linagora.tmail.james.jmap.publicAsset
import java.io.ByteArrayInputStream
import java.net.URI
-import java.time.Clock
import com.google.inject.multibindings.Multibinder
import com.google.inject.{AbstractModule, Scopes}
-import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration
+import com.linagora.tmail.james.jmap.PublicAssetTotalSizeLimit
import jakarta.inject.{Inject, Named}
import org.apache.james.backends.cassandra.components.CassandraModule
import org.apache.james.blob.api.{BlobId, BlobStore, BucketName}
@@ -18,15 +17,15 @@ import reactor.core.scala.publisher.SMono
class CassandraPublicAssetRepository @Inject()(val dao: CassandraPublicAssetDAO,
val blobStore: BlobStore,
- val configuration: JMAPExtensionConfiguration,
+ val publicAssetTotalSizeLimit: PublicAssetTotalSizeLimit,
@Named("publicAssetUriPrefix") publicAssetUriPrefix: URI) extends PublicAssetRepository {
private val bucketName: BucketName = blobStore.getDefaultBucketName
override def create(username: Username, creationRequest: PublicAssetCreationRequest): Publisher[PublicAssetStorage] =
SMono(getTotalSize(username))
- .filter(totalSize => (totalSize + creationRequest.size.value) <= configuration.publicAssetTotalSizeLimit.asLong())
+ .filter(totalSize => (totalSize + creationRequest.size.value) <= publicAssetTotalSizeLimit.asLong())
.flatMap(_ => SMono(createAsset(username, creationRequest)))
- .switchIfEmpty(SMono.error(PublicAssetQuotaLimitExceededException(configuration.publicAssetTotalSizeLimit.asLong())))
+ .switchIfEmpty(SMono.error(PublicAssetQuotaLimitExceededException(publicAssetTotalSizeLimit.asLong())))
private def createAsset(username: Username, creationRequest: PublicAssetCreationRequest): Publisher[PublicAssetStorage] =
SMono.fromCallable(() => creationRequest.content.apply().readAllBytes())
diff --git a/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepositoryTest.java b/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepositoryTest.java
index b96e3198b3..73cc5fa882 100644
--- a/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepositoryTest.java
+++ b/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/label/CassandraLabelChangeRepositoryTest.java
@@ -19,6 +19,8 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import com.linagora.tmail.james.jmap.model.LabelChange;
+
public class CassandraLabelChangeRepositoryTest implements LabelChangeRepositoryContract {
static final CassandraModule MODULE = CassandraLabelChangeTable.MODULE();
diff --git a/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepositoryTest.java b/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepositoryTest.java
index 3a0541ea0d..07c8e09f19 100644
--- a/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepositoryTest.java
+++ b/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetRepositoryTest.java
@@ -5,10 +5,11 @@
import org.apache.james.blob.api.BucketName;
import org.apache.james.blob.memory.MemoryBlobStoreDAO;
import org.apache.james.server.blob.deduplication.DeDuplicationBlobStore;
+import org.apache.james.util.Size;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
-import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration;
+import com.linagora.tmail.james.jmap.PublicAssetTotalSizeLimit;
class CassandraPublicAssetRepositoryTest implements PublicAssetRepositoryContract {
@RegisterExtension
@@ -18,10 +19,11 @@ class CassandraPublicAssetRepositoryTest implements PublicAssetRepositoryContrac
@BeforeEach
void setup(CassandraCluster cassandra) {
+ PublicAssetTotalSizeLimit publicAssetTotalSizeLimit = PublicAssetTotalSizeLimit.DEFAULT();
publicAssetRepository = new CassandraPublicAssetRepository(
new CassandraPublicAssetDAO(cassandra.getConf(), blobIdFactory()),
new DeDuplicationBlobStore(new MemoryBlobStoreDAO(), BucketName.DEFAULT, blobIdFactory()),
- new JMAPExtensionConfiguration(JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT()),
+ publicAssetTotalSizeLimit,
PublicAssetRepositoryContract.PUBLIC_ASSET_URI_PREFIX());
}
diff --git a/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetServiceTest.java b/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetServiceTest.java
index 327ad23b05..66d7c3aa72 100644
--- a/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetServiceTest.java
+++ b/tmail-backend/jmap/extensions-cassandra/src/test/java/com/linagora/tmail/james/jmap/publicAsset/CassandraPublicAssetServiceTest.java
@@ -5,10 +5,11 @@
import org.apache.james.blob.api.BucketName;
import org.apache.james.blob.memory.MemoryBlobStoreDAO;
import org.apache.james.server.blob.deduplication.DeDuplicationBlobStore;
+import org.apache.james.util.Size;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
-import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration;
+import com.linagora.tmail.james.jmap.PublicAssetTotalSizeLimit;
class CassandraPublicAssetServiceTest implements PublicAssetServiceContract {
@@ -21,14 +22,14 @@ class CassandraPublicAssetServiceTest implements PublicAssetServiceContract {
@BeforeEach
void setup(CassandraCluster cassandra) {
- JMAPExtensionConfiguration jmapExtensionConfiguration = new JMAPExtensionConfiguration(JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT());
+ PublicAssetTotalSizeLimit publicAssetTotalSizeLimit = PublicAssetTotalSizeLimit.DEFAULT();
publicAssetRepository = new CassandraPublicAssetRepository(
new CassandraPublicAssetDAO(cassandra.getConf(), blobIdFactory()),
new DeDuplicationBlobStore(new MemoryBlobStoreDAO(), BucketName.DEFAULT, blobIdFactory()),
- jmapExtensionConfiguration,
+ publicAssetTotalSizeLimit,
PublicAssetRepositoryContract.PUBLIC_ASSET_URI_PREFIX());
- publicAssetSetService = new PublicAssetSetService(PublicAssetServiceContract.identityRepository(), publicAssetRepository, jmapExtensionConfiguration);
+ publicAssetSetService = new PublicAssetSetService(PublicAssetServiceContract.identityRepository(), publicAssetRepository, publicAssetTotalSizeLimit);
}
@Override
diff --git a/tmail-backend/jmap/extensions-opensearch/pom.xml b/tmail-backend/jmap/extensions-opensearch/pom.xml
index 33fcc19859..7caa403f32 100644
--- a/tmail-backend/jmap/extensions-opensearch/pom.xml
+++ b/tmail-backend/jmap/extensions-opensearch/pom.xml
@@ -12,16 +12,6 @@
Twake Mail :: JMAP :: Extensions :: Opensearch
-
- ${project.groupId}
- jmap-extensions
-
-
- ${project.groupId}
- jmap-extensions
- test
- test-jar
-
${james.groupId}
apache-james-backends-opensearch
@@ -42,6 +32,16 @@
testing-base
test
+
+ ${project.groupId}
+ jmap-extensions-api
+
+
+ ${project.groupId}
+ jmap-extensions-api
+ test
+ test-jar
+
com.google.guava
guava
diff --git a/tmail-backend/jmap/extensions-rabbitmq/pom.xml b/tmail-backend/jmap/extensions-rabbitmq/pom.xml
index 4afc3a66dc..b455e4e683 100644
--- a/tmail-backend/jmap/extensions-rabbitmq/pom.xml
+++ b/tmail-backend/jmap/extensions-rabbitmq/pom.xml
@@ -12,16 +12,6 @@
Twake Mail :: JMAP :: Extensions :: RabbitMQ
-
- ${project.groupId}
- jmap-extensions
-
-
- ${project.groupId}
- jmap-extensions
- test
- test-jar
-
${james.groupId}
apache-james-backends-rabbitmq
@@ -47,11 +37,41 @@
testing-base
test
+
+ ${project.groupId}
+ jmap-extensions-api
+
+
+ ${project.groupId}
+ jmap-extensions-api
+ test
+ test-jar
+
+
+ com.typesafe.play
+ play-json_${scala.base}
+ 2.10.5
+
org.mockito
mockito-core
test
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+
+
+ io.github.evis
+ scalafix-maven-plugin_2.13
+
+ ${project.parent.parent.basedir}/.scalafix.conf
+
+
+
+
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializer.scala b/tmail-backend/jmap/extensions-rabbitmq/src/main/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializer.scala
similarity index 82%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializer.scala
rename to tmail-backend/jmap/extensions-rabbitmq/src/main/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializer.scala
index faaa2f09c9..32322b5042 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializer.scala
+++ b/tmail-backend/jmap/extensions-rabbitmq/src/main/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializer.scala
@@ -1,12 +1,20 @@
package com.linagora.tmail.james.jmap.json
import com.linagora.tmail.james.jmap.contact.{ContactOwner, EmailAddressContactMessage, MessageEntry, TmailContactMessageScope, TmailContactMessageType}
+import org.apache.james.core.MailAddress
import play.api.libs.functional.syntax.toFunctionalBuilderOps
-import play.api.libs.json.{JsError, JsPath, JsResult, JsString, JsSuccess, JsValue, Json, Reads}
+import play.api.libs.json.{JsError, JsPath, JsResult, JsString, JsSuccess, JsValue, Json, Reads, Writes}
import scala.util.{Failure, Success, Try}
object EmailAddressContactMessageSerializer {
+ implicit val mailAddressReads: Reads[MailAddress] = {
+ case JsString(value) => Try(JsSuccess(new MailAddress(value)))
+ .fold(e => JsError(s"Invalid mailAddress: ${e.getMessage}"), mailAddress => mailAddress)
+ case _ => JsError("Expecting mailAddress to be represented by a JsString")
+ }
+
+ implicit val mailAddressWrites: Writes[MailAddress] = mail => JsString(mail.toString)
private implicit val contactMessageTypeReads: Reads[TmailContactMessageType] = {
case JsString(value) => TmailContactMessageType.from(value)
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializerTest.scala b/tmail-backend/jmap/extensions-rabbitmq/src/test/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializerTest.scala
similarity index 100%
rename from tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializerTest.scala
rename to tmail-backend/jmap/extensions-rabbitmq/src/test/scala/com/linagora/tmail/james/jmap/json/EmailAddressContactMessageSerializerTest.scala
diff --git a/tmail-backend/jmap/extensions/pom.xml b/tmail-backend/jmap/extensions/pom.xml
index cedc4de34f..3babafc141 100644
--- a/tmail-backend/jmap/extensions/pom.xml
+++ b/tmail-backend/jmap/extensions/pom.xml
@@ -16,14 +16,6 @@
-
- ${project.groupId}
- mailbox-encrypted-api
-
-
- ${project.groupId}
- team-mailboxes
-
${james.groupId}
apache-james-mailbox-deleted-messages-vault
@@ -38,10 +30,6 @@
blob-storage-strategy
test
-
- ${james.groupId}
- james-server-webadmin-mailbox-deleted-message-vault
-
${james.groupId}
event-bus-api
@@ -72,6 +60,10 @@
james-server-testing
test
+
+ ${james.groupId}
+ james-server-webadmin-mailbox-deleted-message-vault
+
${james.groupId}
metrics-tests
@@ -83,9 +75,34 @@
test
- com.github.f4b6a3
- uuid-creator
- ${uuid-creator.version}
+ ${project.groupId}
+ jmap-extensions-api
+
+
+ ${project.groupId}
+ jmap-extensions-api
+ test-jar
+ test
+
+
+ ${project.groupId}
+ mailbox-encrypted-api
+
+
+ ${project.groupId}
+ team-mailboxes
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+
+
+ com.github.spullara.mustache.java
+ compiler
com.google.firebase
@@ -100,8 +117,13 @@
guice
- com.github.spullara.mustache.java
- compiler
+ org.mnode.ical4j
+ ical4j
+ 3.2.9
+
+
+ com.ibm.icu
+ icu4j
io.jsonwebtoken
@@ -117,28 +139,16 @@
jjwt-jackson
runtime
-
- org.bouncycastle
- bcpkix-jdk18on
-
-
- org.bouncycastle
- bcprov-jdk18on
-
-
- org.mnode.ical4j
- ical4j
- 3.2.9
-
-
- com.ibm.icu
- icu4j
-
org.mockito
mockito-core
test
+
+ com.github.f4b6a3
+ uuid-creator
+ ${uuid-creator.version}
+
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/JMAPExtensionConfiguration.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/JMAPExtensionConfiguration.scala
index f301c0d27b..76fc9e2c5c 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/JMAPExtensionConfiguration.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/JMAPExtensionConfiguration.scala
@@ -9,10 +9,8 @@ import com.google.common.base.Preconditions
import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration.{CALENDAR_EVENT_REPLY_SUPPORTED_LANGUAGES_DEFAULT, PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT, TICKET_IP_VALIDATION_ENABLED}
import com.linagora.tmail.james.jmap.method.CalendarEventReplySupportedLanguage.LANGUAGE_DEFAULT
import com.linagora.tmail.james.jmap.model.LanguageLocation
-import eu.timepit.refined
import org.apache.commons.configuration2.Configuration
import org.apache.james.core.MailAddress
-import org.apache.james.jmap.core.UnsignedInt.{UnsignedInt, UnsignedIntConstraint}
import org.apache.james.server.core.MissingArgumentException
import org.apache.james.util.{DurationParser, Size}
@@ -20,7 +18,7 @@ import scala.util.{Failure, Success, Try}
object JMAPExtensionConfiguration {
val PUBLIC_ASSET_TOTAL_SIZE_LIMIT_PROPERTY: String = "public.asset.total.size"
- val PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT: PublicAssetTotalSizeLimit = PublicAssetTotalSizeLimit.of(Size.of(20L, Size.Unit.M)).get
+ val PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT: PublicAssetTotalSizeLimit = PublicAssetTotalSizeLimit.DEFAULT
val TICKET_IP_VALIDATION_PROPERTY: String = "authentication.strategy.rfc8621.tickets.ip.validation.enabled"
val TICKET_IP_VALIDATION_ENABLED: TicketIpValidationEnable = TicketIpValidationEnable(true)
val CALENDAR_EVENT_REPLY_SUPPORTED_LANGUAGES_PROPERTY: String = "calendarEvent.reply.supportedLanguages"
@@ -72,13 +70,6 @@ object JMAPExtensionConfiguration {
}
}
-object PublicAssetTotalSizeLimit {
- def of(size: Size): Try[PublicAssetTotalSizeLimit] = refined.refineV[UnsignedIntConstraint](size.asBytes()) match {
- case Right(value) => Success(PublicAssetTotalSizeLimit(value))
- case Left(error) => Failure(new NumberFormatException(error))
- }
-}
-
case class JMAPExtensionConfiguration(publicAssetTotalSizeLimit: PublicAssetTotalSizeLimit = PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT,
supportMailAddress: Option[MailAddress] = None,
supportHttpLink: Option[URL] = None,
@@ -91,10 +82,6 @@ case class JMAPExtensionConfiguration(publicAssetTotalSizeLimit: PublicAssetTota
}
}
-case class PublicAssetTotalSizeLimit(value: UnsignedInt) {
- def asLong(): Long = value.value
-}
-
case class TicketIpValidationEnable(value: Boolean)
case class CalendarEventReplySupportedLanguagesConfig(supportedLanguages: Set[Locale])
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/TMailJMAPModule.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/TMailJMAPModule.scala
index 6d69eb7c62..a393b1fe65 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/TMailJMAPModule.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/TMailJMAPModule.scala
@@ -24,4 +24,7 @@ class TMailJMAPModule extends AbstractModule {
@Provides
def provideJMAPExtensionConfiguration(@Named("jmap") configuration: Configuration): JMAPExtensionConfiguration = JMAPExtensionConfiguration.from(configuration)
+
+ @Provides
+ def providePublicAssetTotalSizeLimit(jmapExtensionConfiguration: JMAPExtensionConfiguration): PublicAssetTotalSizeLimit = jmapExtensionConfiguration.publicAssetTotalSizeLimit
}
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/FirebaseSubscriptionSerializer.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/FirebaseSubscriptionSerializer.scala
index 385dd23765..6e0b58b479 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/FirebaseSubscriptionSerializer.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/FirebaseSubscriptionSerializer.scala
@@ -1,10 +1,14 @@
package com.linagora.tmail.james.jmap.json
import com.linagora.tmail.james.jmap.method.{ApiKey, AppId, AuthDomain, DatabaseUrl, FirebaseCapabilityProperties, MessagingSenderId, ProjectId, StorageBucket, VapidPublicKey}
-import com.linagora.tmail.james.jmap.model.{DeviceClientId, FirebaseSubscription, FirebaseSubscriptionCreationId, FirebaseSubscriptionCreationRequest, FirebaseSubscriptionCreationResponse, FirebaseSubscriptionExpiredTime, FirebaseSubscriptionGetRequest, FirebaseSubscriptionGetResponse, FirebaseSubscriptionId, FirebaseSubscriptionIds, FirebaseSubscriptionPatchObject, FirebaseSubscriptionSetRequest, FirebaseSubscriptionSetResponse, FirebaseSubscriptionUpdateResponse, FirebaseToken, UnparsedFirebaseSubscriptionId}
+import com.linagora.tmail.james.jmap.model.{DeviceClientId, FirebaseSubscription, FirebaseSubscriptionCreation, FirebaseSubscriptionCreationId, FirebaseSubscriptionCreationParseException, FirebaseSubscriptionCreationRequest, FirebaseSubscriptionCreationResponse, FirebaseSubscriptionExpiredTime, FirebaseSubscriptionGetRequest, FirebaseSubscriptionGetResponse, FirebaseSubscriptionId, FirebaseSubscriptionIds, FirebaseSubscriptionPatchObject, FirebaseSubscriptionSetRequest, FirebaseSubscriptionSetResponse, FirebaseSubscriptionUpdateResponse, FirebaseToken, UnparsedFirebaseSubscriptionId}
+import eu.timepit.refined.collection.NonEmpty
+import eu.timepit.refined.refineV
+import eu.timepit.refined.types.string.NonEmptyString
import jakarta.inject.Inject
import org.apache.james.jmap.api.change.TypeStateFactory
import org.apache.james.jmap.api.model.TypeName
+import org.apache.james.jmap.core.SetError.SetErrorDescription
import org.apache.james.jmap.core.{Properties, SetError, UTCDate}
import org.apache.james.jmap.json.mapWrites
import play.api.libs.json._
@@ -85,7 +89,37 @@ class FirebaseSubscriptionSerializer @Inject()(typeStateFactory: TypeStateFactor
def deserializeFirebaseSubscriptionSetRequest(input: JsValue): JsResult[FirebaseSubscriptionSetRequest] = Json.fromJson[FirebaseSubscriptionSetRequest](input)
- def deserializeFirebaseSubscriptionCreationRequest(input: JsValue): JsResult[FirebaseSubscriptionCreationRequest] = Json.fromJson[FirebaseSubscriptionCreationRequest](input)
+ private val subscriptionCreationRequestStandardReads: Reads[FirebaseSubscriptionCreationRequest] = Json.reads[FirebaseSubscriptionCreationRequest]
+
+ implicit val subscriptionCreationRequestReads: Reads[FirebaseSubscriptionCreationRequest] = new Reads[FirebaseSubscriptionCreationRequest] {
+ override def reads(json: JsValue): JsResult[FirebaseSubscriptionCreationRequest] =
+ subscriptionCreationRequestStandardReads.reads(json)
+ .flatMap(request => {
+ validateProperties(json.as[JsObject])
+ .fold(_ => JsError("Failed to validate properties"), _ => JsSuccess(request))
+ })
+
+ def validateProperties(jsObject: JsObject): Either[FirebaseSubscriptionCreationParseException, JsObject] =
+ (jsObject.keys.intersect(FirebaseSubscriptionCreation.serverSetProperty), jsObject.keys.diff(FirebaseSubscriptionCreation.knownProperties)) match {
+ case (_, unknownProperties) if unknownProperties.nonEmpty =>
+ Left(FirebaseSubscriptionCreationParseException(SetError.invalidArguments(
+ SetErrorDescription("Some unknown properties were specified"),
+ Some(toProperties(unknownProperties.toSet)))))
+ case (specifiedServerSetProperties, _) if specifiedServerSetProperties.nonEmpty =>
+ Left(FirebaseSubscriptionCreationParseException(SetError.invalidArguments(
+ SetErrorDescription("Some server-set properties were specified"),
+ Some(toProperties(specifiedServerSetProperties.toSet)))))
+ case _ => scala.Right(jsObject)
+ }
+
+ private def toProperties(strings: Set[String]): Properties = Properties(strings
+ .flatMap(string => {
+ val refinedValue: Either[String, NonEmptyString] = refineV[NonEmpty](string)
+ refinedValue.fold(_ => None, Some(_))
+ }))
+ }
+
+ def deserializeFirebaseSubscriptionCreationRequest(input: JsValue): JsResult[FirebaseSubscriptionCreationRequest] = Json.fromJson[FirebaseSubscriptionCreationRequest](input)(subscriptionCreationRequestReads)
def serialize(response: FirebaseSubscriptionGetResponse, properties: Properties): JsValue =
Json.toJson(response)
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/LabelSerializer.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/LabelSerializer.scala
index 9b4876a6c1..8c6406dc8a 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/LabelSerializer.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/LabelSerializer.scala
@@ -1,10 +1,15 @@
package com.linagora.tmail.james.jmap.json
-import com.linagora.tmail.james.jmap.model.{Color, DisplayName, Label, LabelCreationId, LabelCreationRequest, LabelCreationResponse, LabelGetRequest, LabelGetResponse, LabelId, LabelIds, LabelPatchObject, LabelSetRequest, LabelSetResponse, LabelUpdateResponse, UnparsedLabelId}
+import com.linagora.tmail.james.jmap.method.JSON_CUSTOM_VALIDATION_ERROR
+import com.linagora.tmail.james.jmap.model.{Color, DisplayName, Label, LabelCreationId, LabelCreationParseException, LabelCreationRequest, LabelCreationResponse, LabelGetRequest, LabelGetResponse, LabelId, LabelIds, LabelPatchObject, LabelSetRequest, LabelSetResponse, LabelUpdateResponse, UnparsedLabelId}
+import eu.timepit.refined.collection.NonEmpty
+import eu.timepit.refined.refineV
+import eu.timepit.refined.types.string.NonEmptyString
+import org.apache.james.jmap.core.SetError.SetErrorDescription
import org.apache.james.jmap.core.{Properties, SetError, UuidState}
import org.apache.james.jmap.json.mapWrites
import org.apache.james.jmap.mail.Keyword
-import play.api.libs.json.{Format, JsArray, JsError, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, Reads, Writes, __}
+import play.api.libs.json.{Format, JsArray, JsError, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, JsonValidationError, Reads, Writes, __}
object LabelSerializer {
private implicit val labelIdFormat: Format[LabelId] = Json.valueFormat[LabelId]
@@ -48,7 +53,39 @@ object LabelSerializer {
private implicit val labelMapUpdateRequestReads: Reads[Map[UnparsedLabelId, LabelPatchObject]] =
Reads.mapReads[UnparsedLabelId, LabelPatchObject] { string => unparsedLabelIdReads.reads(JsString(string)) }
- implicit val labelCreationRequest: Reads[LabelCreationRequest] = Json.reads[LabelCreationRequest]
+ implicit val labelCreationRequestStandardWrites: Writes[LabelCreationRequest] = Json.writes[LabelCreationRequest]
+
+ private val labelCreationRequestStandardReads: Reads[LabelCreationRequest] = Json.reads[LabelCreationRequest]
+
+ implicit val labelCreationRequestReads: Reads[LabelCreationRequest] = new Reads[LabelCreationRequest] {
+ override def reads(json: JsValue): JsResult[LabelCreationRequest] = {
+ validateProperties(json.as[JsObject])
+ .fold(exception => JsError(JsonValidationError(JSON_CUSTOM_VALIDATION_ERROR, exception.setError)),
+ _ => labelCreationRequestStandardReads.reads(json))
+ }
+
+ def validateProperties(jsObject: JsObject): Either[LabelCreationParseException, JsObject] =
+ (jsObject.keys.intersect(LabelCreationRequest.serverSetProperty),
+ jsObject.keys.diff(LabelCreationRequest.knownProperties)) match {
+ case (_, unknownProperties) if unknownProperties.nonEmpty =>
+ Left(LabelCreationParseException(SetError.invalidArguments(
+ SetErrorDescription("Some unknown properties were specified"),
+ Some(toProperties(unknownProperties.toSet)))))
+ case (specifiedServerSetProperties, _) if specifiedServerSetProperties.nonEmpty =>
+ Left(LabelCreationParseException(SetError.invalidArguments(
+ SetErrorDescription("Some server-set properties were specified"),
+ Some(toProperties(specifiedServerSetProperties.toSet)))))
+ case _ => scala.Right(jsObject)
+ }
+
+ private def toProperties(strings: Set[String]): Properties = Properties(strings
+ .flatMap(string => {
+ val refinedValue: Either[String, NonEmptyString] = refineV[NonEmpty](string)
+ refinedValue.fold(_ => None, Some(_))
+ }))
+ }
+
+ def deserializeLabelCreationRequest(input: JsValue): JsResult[LabelCreationRequest] = Json.fromJson[LabelCreationRequest](input)
private implicit val labelMapUpdateResponseWrites: Writes[Map[LabelId, LabelUpdateResponse]] =
mapWrites[LabelId, LabelUpdateResponse](_.id.value, labelUpdateResponseWrites)
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializer.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializer.scala
index c621359cae..e24c9d94b3 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializer.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializer.scala
@@ -1,11 +1,12 @@
package com.linagora.tmail.james.jmap.json
-import com.linagora.tmail.james.jmap.method.standardErrorMessage
+import com.linagora.tmail.james.jmap.method.{JSON_CUSTOM_VALIDATION_ERROR, standardErrorMessage}
import com.linagora.tmail.james.jmap.model.{PublicAssetDTO, PublicAssetGetRequest, PublicAssetGetResponse}
import com.linagora.tmail.james.jmap.publicAsset.ImageContentType.ImageContentType
-import com.linagora.tmail.james.jmap.publicAsset.{PublicAssetCreationId, PublicAssetCreationResponse, PublicAssetId, PublicAssetPatchObject, PublicAssetSetCreationRequest, PublicAssetSetRequest, PublicAssetSetResponse, PublicAssetUpdateResponse, PublicURI, UnparsedPublicAssetId, ValidatedPublicAssetPatchObject}
+import com.linagora.tmail.james.jmap.publicAsset.{PublicAssetCreationId, PublicAssetCreationParseException, PublicAssetCreationResponse, PublicAssetId, PublicAssetPatchObject, PublicAssetSetCreationRequest, PublicAssetSetRequest, PublicAssetSetResponse, PublicAssetUpdateResponse, PublicURI, UnparsedPublicAssetId, ValidatedPublicAssetPatchObject}
import org.apache.james.jmap.api.model.IdentityId
-import org.apache.james.jmap.core.{SetError, UuidState}
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{Properties, SetError, UuidState}
import org.apache.james.jmap.json.{mapMarkerReads, mapWrites}
import org.apache.james.jmap.mail.{IdentityIds, UnparsedIdentityId, BlobId => JmapBlobId}
import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsPath, JsResult, JsString, JsSuccess, JsValue, Json, JsonValidationError, Reads, Writes}
@@ -91,7 +92,23 @@ object PublicAssetSerializer {
private implicit val blobIdReads: Reads[JmapBlobId] = Json.valueReads[JmapBlobId]
private implicit val unparsedIdentityIdReads: Reads[UnparsedIdentityId] = Json.valueReads[UnparsedIdentityId]
private implicit val identityIdsReads: Reads[IdentityIds] = Json.valueReads[IdentityIds]
- private implicit val publicAssetCreationRequestReads: Reads[PublicAssetSetCreationRequest] = Json.reads[PublicAssetSetCreationRequest]
+
+ private implicit val publicAssetCreationRequestStandardReads: Reads[PublicAssetSetCreationRequest] = Json.reads[PublicAssetSetCreationRequest]
+ private implicit val publicAssetCreationRequestReads: Reads[PublicAssetSetCreationRequest] = new Reads[PublicAssetSetCreationRequest] {
+ override def reads(json: JsValue): JsResult[PublicAssetSetCreationRequest] =
+ validateProperties(json.as[JsObject])
+ .fold(exception => JsError(JsonValidationError(JSON_CUSTOM_VALIDATION_ERROR, exception.setError)),
+ _ => publicAssetCreationRequestStandardReads.reads(json))
+
+ def validateProperties(jsObject: JsObject): Either[PublicAssetCreationParseException, JsObject] = {
+ jsObject.fields.find(mapEntry => !PublicAssetSetCreationRequest.knownProperties.contains(mapEntry._1))
+ .map(e => Left(PublicAssetCreationParseException(SetError.invalidArguments(
+ SetErrorDescription("Some unknown properties were specified"),
+ Some(Properties.toProperties(Set(e._1)))))))
+ .getOrElse(Right(jsObject))
+ }
+ }
+
private implicit val publicAssetCreationIdReads: Reads[PublicAssetCreationId] = Json.reads[PublicAssetCreationId]
private implicit val unparsedPublicAssetIdReads: Reads[UnparsedPublicAssetId] = Json.valueReads[UnparsedPublicAssetId]
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangesPopulate.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangesPopulate.scala
index 6e897838c8..b2ed5ce709 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangesPopulate.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangesPopulate.scala
@@ -4,7 +4,7 @@ import com.google.inject.Inject
import com.linagora.tmail.james.jmap.method.LabelSetCreatePerformer.LabelCreationResults
import com.linagora.tmail.james.jmap.method.LabelSetDeletePerformer.LabelDeletionResults
import com.linagora.tmail.james.jmap.method.LabelUpdateResults
-import com.linagora.tmail.james.jmap.model.LabelId
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelId}
import jakarta.inject.Named
import org.apache.james.core.Username
import org.apache.james.events.Event.EventId
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepository.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/MemoryLabelChangeRepository.scala
similarity index 50%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepository.scala
rename to tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/MemoryLabelChangeRepository.scala
index 5efec10892..2fad813a4d 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelChangeRepository.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/MemoryLabelChangeRepository.scala
@@ -1,13 +1,12 @@
package com.linagora.tmail.james.jmap.label
import java.time.{Clock, Instant}
-import java.util.function.Supplier
import com.google.common.collect.{HashBasedTable, Table, Tables}
import com.linagora.tmail.james.jmap.label.LabelChangeRepository.DEFAULT_MAX_IDS_TO_RETURN
-import com.linagora.tmail.james.jmap.model.LabelId
+import com.linagora.tmail.james.jmap.model.{LabelChange, LabelChanges}
import jakarta.inject.Inject
-import org.apache.james.jmap.api.change.{JmapChange, Limit, State}
+import org.apache.james.jmap.api.change.{Limit, State}
import org.apache.james.jmap.api.exception.ChangeNotFoundException
import org.apache.james.jmap.api.model
import org.apache.james.jmap.api.model.{AccountId, TypeName}
@@ -15,35 +14,6 @@ import org.apache.james.jmap.core.UuidState
import org.reactivestreams.Publisher
import reactor.core.scala.publisher.{SFlux, SMono}
-object LabelChangeRepository {
- val DEFAULT_MAX_IDS_TO_RETURN : Limit = Limit.of(256)
-}
-
-trait LabelChangeRepository {
- def save(labelChange: LabelChange): Publisher[Void]
-
- def getSinceState(accountId: AccountId, state: State, maxIdsToReturn: Option[Limit] = Some(DEFAULT_MAX_IDS_TO_RETURN)): Publisher[LabelChanges]
-
- def getLatestState(accountId: AccountId): Publisher[State]
-}
-
-case class LabelChange(accountId: AccountId,
- created: Set[LabelId] = Set(),
- updated: Set[LabelId] = Set(),
- destroyed: Set[LabelId] = Set(),
- state: State) extends JmapChange {
- override def getAccountId: AccountId = accountId
-
- override def isNoop: Boolean = created.isEmpty && updated.isEmpty && destroyed.isEmpty
-
- override def forSharee(accountId: AccountId, state: Supplier[State]): JmapChange =
- LabelChange(accountId = accountId,
- created = created,
- updated = updated,
- destroyed = destroyed,
- state = state.get())
-}
-
case object LabelTypeName extends TypeName {
override val asString: String = "Label"
@@ -55,47 +25,6 @@ case object LabelTypeName extends TypeName {
override def parseState(string: String): Either[IllegalArgumentException, model.State] = UuidState.parse(string)
}
-object LabelChanges {
- def from(labelChange: LabelChange): LabelChanges = LabelChanges(
- created = labelChange.created,
- updated = labelChange.updated,
- destroyed = labelChange.destroyed,
- newState = labelChange.state)
-
- def initial(): LabelChanges = LabelChanges(
- created = Set(),
- updated = Set(),
- destroyed = Set(),
- newState = State.INITIAL)
-
- def merge(limit: Int, change1: LabelChanges, change2: LabelChanges): LabelChanges =
- change1.canAppendMoreItem match {
- case false => change1
- case true if change1.newState.equals(State.INITIAL) => change2
- case true =>
- val createdTemp: Set[LabelId] = (change1.created ++ change2.created).diff(change2.destroyed)
- val updatedTemp: Set[LabelId] = (change1.updated ++ change2.updated.diff(createdTemp)).diff(change2.destroyed)
- val destroyedTemp: Set[LabelId] = change1.destroyed ++ change2.destroyed.diff(change1.created)
- if (createdTemp.size + updatedTemp.size + destroyedTemp.size > limit) {
- change1.copy(hasMoreChanges = true, canAppendMoreItem = false)
- } else {
- change1.copy(created = createdTemp,
- updated = updatedTemp,
- destroyed = destroyedTemp,
- newState = change2.newState)
- }
- }
-}
-
-case class LabelChanges(created: Set[LabelId] = Set(),
- updated: Set[LabelId] = Set(),
- destroyed: Set[LabelId] = Set(),
- hasMoreChanges: Boolean = false,
- newState: State,
- private val canAppendMoreItem: Boolean = true) {
- def getAllChanges: Set[LabelId] = created ++ updated ++ destroyed
-}
-
case class MemoryLabelChangeRepository @Inject()(clock: Clock) extends LabelChangeRepository {
import scala.jdk.CollectionConverters._
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelRepository.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/MemoryLabelRepository.scala
similarity index 82%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelRepository.scala
rename to tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/MemoryLabelRepository.scala
index 911d1490bc..edc2566e27 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/LabelRepository.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/label/MemoryLabelRepository.scala
@@ -14,24 +14,6 @@ import reactor.core.scala.publisher.{SFlux, SMono}
import scala.jdk.CollectionConverters._
-trait LabelRepository {
- def addLabel(username: Username, labelCreationRequest: LabelCreationRequest): Publisher[Label]
-
- def addLabel(username: Username, label: Label): Publisher[Void]
-
- def addLabels(username: Username, labelCreationRequests: util.Collection[LabelCreationRequest]): Publisher[Label]
-
- def updateLabel(username: Username, labelId: LabelId, newDisplayName: Option[DisplayName] = None, newColor: Option[Color] = None): Publisher[Void]
-
- def getLabels(username: Username, ids: util.Collection[LabelId]): Publisher[Label]
-
- def listLabels(username: Username): Publisher[Label]
-
- def deleteLabel(username: Username, labelId: LabelId): Publisher[Void]
-
- def deleteAllLabels(username: Username): Publisher[Void]
-}
-
class MemoryLabelRepository extends LabelRepository {
private val labelsTable: Table[Username, Keyword, Label] = Tables.synchronizedTable(HashBasedTable.create())
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/FirebaseSubscriptionSetCreatePerformer.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/FirebaseSubscriptionSetCreatePerformer.scala
index 9b67e402a9..5f1886a199 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/FirebaseSubscriptionSetCreatePerformer.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/FirebaseSubscriptionSetCreatePerformer.scala
@@ -68,8 +68,7 @@ class FirebaseSubscriptionSetCreatePerformer @Inject()(val repository: FirebaseS
private def parseCreate(json: JsObject): Either[Exception, FirebaseSubscriptionCreationRequest] = for {
- validJsObject <- FirebaseSubscriptionCreation.validateProperties(json)
- parsedRequest <- serializer.deserializeFirebaseSubscriptionCreationRequest(validJsObject).asEither
+ parsedRequest <- serializer.deserializeFirebaseSubscriptionCreationRequest(json).asEither
.left.map(errors => FirebaseSubscriptionCreationParseException.from(errors))
validatedRequest <- parsedRequest.validate
.left.map(e => FirebaseSubscriptionCreationParseException(SetError.invalidArguments(SetErrorDescription(e.getMessage))))
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelChangesMethod.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelChangesMethod.scala
index ecaf45d1d6..5ec194bd98 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelChangesMethod.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelChangesMethod.scala
@@ -2,9 +2,9 @@ package com.linagora.tmail.james.jmap.method
import com.google.inject.Inject
import com.linagora.tmail.james.jmap.json.{LabelChangesSerializer => Serializer}
-import com.linagora.tmail.james.jmap.label.{LabelChangeRepository, LabelChanges}
+import com.linagora.tmail.james.jmap.label.LabelChangeRepository
import com.linagora.tmail.james.jmap.method.CapabilityIdentifier.LINAGORA_LABEL
-import com.linagora.tmail.james.jmap.model.{LabelChangesRequest => Request, LabelChangesResponse => Response}
+import com.linagora.tmail.james.jmap.model.{LabelChanges, LabelChangesRequest => Request, LabelChangesResponse => Response}
import eu.timepit.refined.auto._
import org.apache.james.jmap.api.change.{State => JavaState}
import org.apache.james.jmap.api.exception.ChangeNotFoundException
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelSetCreatePerformer.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelSetCreatePerformer.scala
index 23e46709b1..9c92ee18c8 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelSetCreatePerformer.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/LabelSetCreatePerformer.scala
@@ -3,17 +3,15 @@ package com.linagora.tmail.james.jmap.method
import com.linagora.tmail.james.jmap.json.LabelSerializer
import com.linagora.tmail.james.jmap.label.LabelRepository
import com.linagora.tmail.james.jmap.method.LabelSetCreatePerformer.{LabelCreationFailure, LabelCreationResult, LabelCreationResults, LabelCreationSuccess}
-import com.linagora.tmail.james.jmap.model.{LabelCreationId, LabelCreationRequest, LabelCreationResponse, LabelSetRequest}
+import com.linagora.tmail.james.jmap.model.{LabelCreationId, LabelCreationParseException, LabelCreationRequest, LabelCreationResponse, LabelSetRequest}
import jakarta.inject.Inject
import org.apache.james.jmap.core.SetError
import org.apache.james.jmap.core.SetError.SetErrorDescription
import org.apache.james.mailbox.MailboxSession
import org.apache.james.metrics.api.MetricFactory
-import play.api.libs.json.{JsError, JsObject, JsSuccess, Json}
+import play.api.libs.json.{JsError, JsObject, JsPath, JsSuccess, Json, JsonValidationError}
import reactor.core.scala.publisher.{SFlux, SMono}
-case class LabelCreationParseException(setError: SetError) extends Exception
-
object LabelSetCreatePerformer {
sealed trait LabelCreationResult {
def labelCreationId: LabelCreationId
@@ -58,11 +56,10 @@ class LabelSetCreatePerformer @Inject()(val labelRepository: LabelRepository,
.map(LabelCreationResults)
private def parseCreate(jsObject: JsObject): Either[LabelCreationParseException, LabelCreationRequest] =
- LabelCreationRequest.validateProperties(jsObject)
- .flatMap(validJsObject => Json.fromJson(validJsObject)(LabelSerializer.labelCreationRequest) match {
+ LabelSerializer.deserializeLabelCreationRequest(jsObject) match {
case JsSuccess(creationRequest, _) => Right(creationRequest)
case JsError(errors) => Left(LabelCreationParseException(standardError(errors)))
- })
+ }
private def createLabel(mailboxSession: MailboxSession,
clientId: LabelCreationId,
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/PublicAssetSetMethod.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/PublicAssetSetMethod.scala
index aa62578fd7..96ce2884a2 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/PublicAssetSetMethod.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/PublicAssetSetMethod.scala
@@ -1,5 +1,6 @@
package com.linagora.tmail.james.jmap.method
+import cats.implicits.toTraverseOps
import com.google.common.base.Preconditions
import com.linagora.tmail.james.jmap.json.PublicAssetSerializer
import com.linagora.tmail.james.jmap.method.CapabilityIdentifier.LINAGORA_PUBLIC_ASSETS
@@ -9,7 +10,8 @@ import jakarta.inject.Inject
import org.apache.james.jmap.api.model.IdentityId
import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, SessionTranslator, UuidState}
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{ClientId, Id, Invocation, Properties, ServerId, SessionTranslator, SetError, UuidState}
import org.apache.james.jmap.method.{InvocationWithContext, MethodRequiringAccountId}
import org.apache.james.jmap.routes.{Blob, BlobNotFoundException, BlobResolvers, ProcessingContext, SessionSupplier}
import org.apache.james.mailbox.MailboxSession
@@ -96,14 +98,26 @@ class PublicAssetSetCreatePerformer @Inject()(val publicAssetRepository: PublicA
.flatMap(publicAsset => SMono(publicAssetSetService.create(mailboxSession.getUser, publicAsset)))
private def parseCreate(jsObject: JsObject): Either[PublicAssetCreationParseException, PublicAssetSetCreationRequest] =
- PublicAssetSetCreationRequest.validateProperties(jsObject)
- .flatMap(validJsObject => PublicAssetSerializer.deserializePublicAssetSetCreationRequest(validJsObject) match {
- case JsSuccess(creationRequest, _) => Right(creationRequest)
- case JsError(errors) => Left(PublicAssetCreationParseException(standardError(errors)))
- })
+ PublicAssetSerializer.deserializePublicAssetSetCreationRequest(jsObject) match {
+ case JsSuccess(creationRequest, _) => Right(creationRequest)
+ case JsError(errors) => Left(PublicAssetCreationParseException(standardError(errors)))
+ }
+
+
+ private def doParseIdentityIds(creationRequest: PublicAssetSetCreationRequest): Either[PublicAssetCreationParseException, List[IdentityId]] =
+ creationRequest.identityIds match {
+ case None => Right(List.empty)
+ case Some(identityIdMap) => identityIdMap.map {
+ case (identityId: IdentityId, bolVal) => if (bolVal) {
+ Right(identityId)
+ } else {
+ scala.Left(PublicAssetCreationParseException(SetError.invalidArguments(SetErrorDescription(s"identityId '$identityId' must be a true"), Some(Properties.toProperties(Set("identityIds"))))))
+ }
+ }.toList.sequence
+ }
private def parseIdentityIds(creationRequest: PublicAssetSetCreationRequest): SMono[Seq[IdentityId]] =
- SMono.fromCallable(() => creationRequest.parseIdentityIds)
+ SMono.fromCallable(() => doParseIdentityIds(creationRequest))
.flatMap(result => result.fold(e => SMono.error(e), ids => SMono.just(ids)))
private def generatePublicAssetCreationRequest(creationRequest: PublicAssetSetCreationRequest,
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/package.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/package.scala
index 50fb655671..a4db2bb65b 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/package.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/method/package.scala
@@ -8,6 +8,7 @@ import play.api.libs.json.{JsError, JsPath, JsResult, JsonValidationError}
import scala.language.implicitConversions
package object method {
+ val JSON_CUSTOM_VALIDATION_ERROR: String = "error.custom.validation"
def standardErrorMessage(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): String =
errors.head match {
@@ -19,8 +20,15 @@ package object method {
case _ => ResponseSerializer.serialize(JsError(errors)).toString()
}
+ private def tryExtractSetErrorMessage(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): Option[SetError] =
+ errors.head match {
+ case (_, Seq(JsonValidationError(Seq(JSON_CUSTOM_VALIDATION_ERROR), setError: SetError))) => Option(setError)
+ case _ => None
+ }
+
def standardError(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): SetError =
- SetError.invalidArguments(SetErrorDescription(standardErrorMessage(errors)))
+ tryExtractSetErrorMessage(errors)
+ .getOrElse(SetError.invalidArguments(SetErrorDescription(standardErrorMessage(errors))))
implicit class AsEitherRequest[T](val jsResult: JsResult[T]) {
def asEitherRequest: Either[IllegalArgumentException, T] =
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala
index 5258342508..ca42885d21 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/FirebaseSubscription.scala
@@ -1,7 +1,6 @@
package com.linagora.tmail.james.jmap.model
import java.time.ZonedDateTime
-import java.util.UUID
import cats.data.Validated
import cats.instances.list._
@@ -11,7 +10,6 @@ import com.linagora.tmail.james.jmap.model.FirebaseSubscriptionPatchObject.updat
import com.linagora.tmail.james.jmap.model.FirebaseSubscriptionUpdateFailure.LOGGER
import eu.timepit.refined.auto._
import org.apache.james.jmap.api.change.TypeStateFactory
-import org.apache.james.jmap.api.model.ExpireTimeInvalidException.TIME_FORMATTER
import org.apache.james.jmap.api.model.TypeName
import org.apache.james.jmap.core.Id.Id
import org.apache.james.jmap.core.Properties.toProperties
@@ -23,104 +21,6 @@ import play.api.libs.json.{JsArray, JsObject, JsPath, JsString, JsValue, JsonVal
import scala.util.{Failure, Success, Try}
-object FirebaseSubscriptionId {
- def generate(): FirebaseSubscriptionId = FirebaseSubscriptionId(UUID.randomUUID)
-
- def liftOrThrow(unparsedId: UnparsedFirebaseSubscriptionId): Either[IllegalArgumentException, FirebaseSubscriptionId] =
- liftOrThrow(unparsedId.id.value)
-
- def liftOrThrow(value: String): Either[IllegalArgumentException, FirebaseSubscriptionId] =
- Try(UUID.fromString(value))
- .map(value1 => FirebaseSubscriptionId(value1))
- .toEither
- .left.map(e => new IllegalArgumentException("FirebaseSubscriptionId is invalid", e))
-}
-
-case class FirebaseSubscriptionId(value: UUID) {
- def serialize: String = value.toString
-
- def asUnparsedFirebaseSubscriptionId: UnparsedFirebaseSubscriptionId =
- UnparsedFirebaseSubscriptionId(Id.validate(serialize).toOption.get)
-}
-
-case class DeviceClientId(value: String) extends AnyVal
-
-case class FirebaseToken(value: String) extends AnyVal
-
-case class FirebaseSubscriptionExpiredTime(value: ZonedDateTime) {
- def isAfter(date: ZonedDateTime): Boolean = value.isAfter(date)
-
- def isBefore(date: ZonedDateTime): Boolean = value.isBefore(date)
-}
-
-case class FirebaseSubscriptionCreationRequest(deviceClientId: DeviceClientId,
- token: FirebaseToken,
- expires: Option[FirebaseSubscriptionExpiredTime] = None,
- types: Seq[TypeName]) {
-
- def validate: Either[IllegalArgumentException, FirebaseSubscriptionCreationRequest] =
- validateTypes
-
- private def validateTypes: Either[IllegalArgumentException, FirebaseSubscriptionCreationRequest] =
- if (types.isEmpty) {
- scala.Left(new IllegalArgumentException("types must not be empty"))
- } else {
- Right(this)
- }
-}
-
-object FirebaseSubscription {
- val EXPIRES_TIME_MAX_DAY: Int = 7
- val allProperties: Properties = Properties("id", "deviceClientId", "expires", "types")
- val idProperty: Properties = Properties("id")
-
- def from(creationRequest: FirebaseSubscriptionCreationRequest,
- expireTime: FirebaseSubscriptionExpiredTime): FirebaseSubscription =
- FirebaseSubscription(id = FirebaseSubscriptionId.generate(),
- deviceClientId = creationRequest.deviceClientId,
- token = creationRequest.token,
- expires = expireTime,
- types = creationRequest.types)
-}
-
-case class FirebaseSubscription(id: FirebaseSubscriptionId,
- deviceClientId: DeviceClientId,
- token: FirebaseToken,
- expires: FirebaseSubscriptionExpiredTime,
- types: Seq[TypeName]) {
- def withTypes(types: Seq[TypeName]): FirebaseSubscription = copy(types = types)
-
- def withExpires(expires: FirebaseSubscriptionExpiredTime): FirebaseSubscription = copy(expires = expires)
-}
-
-case class FirebaseSubscriptionNotFoundException(id: FirebaseSubscriptionId) extends RuntimeException
-
-case class ExpireTimeInvalidException(expires: ZonedDateTime, message: String) extends IllegalStateException(s"`${expires.format(TIME_FORMATTER)}` $message")
-
-case class DeviceClientIdInvalidException(deviceClientId: DeviceClientId, message: String) extends IllegalArgumentException(s"`${deviceClientId.value}` $message")
-
-case class TokenInvalidException(message: String) extends IllegalArgumentException(message)
-
-case class MissingOrInvalidFirebaseCredentialException(message: String) extends IllegalArgumentException(message)
-
-case class UnparsedFirebaseSubscriptionId(id: Id)
-
-case class FirebaseSubscriptionIds(list: List[UnparsedFirebaseSubscriptionId])
-
-case class FirebaseSubscriptionGetRequest(ids: Option[FirebaseSubscriptionIds],
- properties: Option[Properties]) extends WithoutAccountId {
-
- def validateProperties: Either[IllegalArgumentException, Properties] =
- properties match {
- case None => Right(FirebaseSubscription.allProperties)
- case Some(value) =>
- value -- FirebaseSubscription.allProperties match {
- case invalidProperties if invalidProperties.isEmpty() => Right(value ++ FirebaseSubscription.idProperty)
- case invalidProperties: Properties => Left(new IllegalArgumentException(s"The following properties [${invalidProperties.format()}] do not exist."))
- }
- }
-}
-
object FirebaseSubscriptionGetResponse {
def from(list: Seq[FirebaseSubscription], requestIds: Option[FirebaseSubscriptionIds]): FirebaseSubscriptionGetResponse =
requestIds match {
@@ -159,9 +59,9 @@ object FirebaseSubscriptionCreationParseException {
case class FirebaseSubscriptionCreationParseException(setError: SetError) extends Exception
object FirebaseSubscriptionCreation {
- private val serverSetProperty: Set[String] = Set("id")
- private val assignableProperties: Set[String] = Set("deviceClientId", "token", "expires", "types")
- private val knownProperties: Set[String] = assignableProperties ++ serverSetProperty
+ val serverSetProperty: Set[String] = Set("id")
+ val assignableProperties: Set[String] = Set("deviceClientId", "token", "expires", "types")
+ val knownProperties: Set[String] = assignableProperties ++ serverSetProperty
def validateProperties(jsObject: JsObject): Either[FirebaseSubscriptionCreationParseException, JsObject] =
(jsObject.keys.intersect(serverSetProperty), jsObject.keys.diff(knownProperties)) match {
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala
index 1f04d11ccf..81b4f1d354 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/Label.scala
@@ -1,112 +1,7 @@
package com.linagora.tmail.james.jmap.model
-import java.util.UUID
-
-import com.linagora.tmail.james.jmap.method.LabelCreationParseException
-import eu.timepit.refined
-import eu.timepit.refined.auto._
-import eu.timepit.refined.collection.NonEmpty
-import eu.timepit.refined.refineV
-import eu.timepit.refined.string.MatchesRegex
-import eu.timepit.refined.types.string.NonEmptyString
-import org.apache.james.jmap.core.Id.Id
-import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.core.{AccountId, Id, Properties, SetError, UuidState}
-import org.apache.james.jmap.mail.Keyword
+import org.apache.james.jmap.core.{AccountId, Properties, UuidState}
import org.apache.james.jmap.method.WithAccountId
-import play.api.libs.json.JsObject
-
-object LabelId {
- def fromKeyword(keyword: Keyword): LabelId =
- LabelId(Id.validate(keyword.flagName).toOption.get)
-
- def generate(): LabelId =
- LabelId(Id.validate(UUID.randomUUID().toString).toOption.get)
-}
-
-case class LabelId(id: Id) {
- def toKeyword: Keyword =
- Keyword.of(id.value).get
-
- def asUnparsedLabelId: UnparsedLabelId =
- UnparsedLabelId(id)
-
- def serialize: String = id.value
-}
-
-object KeywordUtil {
- def generate(): Keyword =
- Keyword.of(UUID.randomUUID().toString).get
-}
-
-case class DisplayName(value: String)
-
-object Color {
- private type ColorRegex = MatchesRegex["^#[a-fA-F0-9]{6}$"]
-
- def validate(string: String): Either[IllegalArgumentException, Color] =
- refined.refineV[ColorRegex](string) match {
- case Left(_) => scala.Left(new IllegalArgumentException(s"The string should be a valid hexadecimal color value following this pattern #[a-fA-F0-9]{6}"))
- case Right(value) => scala.Right(Color(value))
- }
-}
-
-case class Color(value: String)
-
-object LabelCreationRequest {
- private val serverSetProperty = Set("id", "keyword")
- private val assignableProperties = Set("displayName", "color")
- private val knownProperties = assignableProperties ++ serverSetProperty
-
- def validateProperties(jsObject: JsObject): Either[LabelCreationParseException, JsObject] =
- (jsObject.keys.intersect(serverSetProperty), jsObject.keys.diff(knownProperties)) match {
- case (_, unknownProperties) if unknownProperties.nonEmpty =>
- Left(LabelCreationParseException(SetError.invalidArguments(
- SetErrorDescription("Some unknown properties were specified"),
- Some(toProperties(unknownProperties.toSet)))))
- case (specifiedServerSetProperties, _) if specifiedServerSetProperties.nonEmpty =>
- Left(LabelCreationParseException(SetError.invalidArguments(
- SetErrorDescription("Some server-set properties were specified"),
- Some(toProperties(specifiedServerSetProperties.toSet)))))
- case _ => scala.Right(jsObject)
- }
-
- private def toProperties(strings: Set[String]): Properties = Properties(strings
- .flatMap(string => {
- val refinedValue: Either[String, NonEmptyString] = refineV[NonEmpty](string)
- refinedValue.fold(_ => None, Some(_))
- }))
-}
-
-case class LabelCreationRequest(displayName: DisplayName, color: Option[Color]) {
- def toLabel: Label = {
- val keyword: Keyword = KeywordUtil.generate()
-
- Label(id = LabelId.fromKeyword(keyword),
- displayName = displayName,
- keyword = keyword,
- color = color)
- }
-}
-
-object Label {
- val allProperties: Properties = Properties("id", "displayName", "keyword", "color")
- val idProperty: Properties = Properties("id")
-}
-
-case class Label(id: LabelId, displayName: DisplayName, keyword: Keyword, color: Option[Color]) {
- def update(newDisplayName: Option[DisplayName], newColor: Option[Color]): Label =
- copy(displayName = newDisplayName.getOrElse(displayName),
- color = newColor.orElse(color))
-}
-
-case class LabelNotFoundException(id: LabelId) extends RuntimeException
-
-case class UnparsedLabelId(id: Id) {
- def asLabelId: LabelId = LabelId(id)
-}
-
-case class LabelIds(list: List[UnparsedLabelId])
case class LabelGetRequest(accountId: AccountId,
ids: Option[LabelIds],
@@ -138,4 +33,4 @@ object LabelGetResponse {
case class LabelGetResponse(accountId: AccountId,
state: UuidState,
list: Seq[Label],
- notFound: Seq[UnparsedLabelId] = Seq())
+ notFound: Seq[UnparsedLabelId] = Seq())
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala
index 065bc9e03d..74e6803c99 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/model/LabelChange.scala
@@ -1,6 +1,5 @@
package com.linagora.tmail.james.jmap.model
-import com.linagora.tmail.james.jmap.label.LabelChanges
import org.apache.james.jmap.api.change.Limit
import org.apache.james.jmap.core.{AccountId, UuidState}
import org.apache.james.jmap.method.WithAccountId
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepository.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepository.scala
similarity index 75%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepository.scala
rename to tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepository.scala
index fa427ad4f8..16af9c68ee 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetRepository.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepository.scala
@@ -5,7 +5,7 @@ import java.net.URI
import java.time.Clock
import com.google.common.collect.{HashBasedTable, ImmutableList, Table, Tables}
-import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration
+import com.linagora.tmail.james.jmap.PublicAssetTotalSizeLimit
import jakarta.inject.{Inject, Named}
import org.apache.james.blob.api.{BlobId, BlobStore, BucketName}
import org.apache.james.core.Username
@@ -16,35 +16,8 @@ import reactor.core.scala.publisher.{SFlux, SMono}
import scala.jdk.CollectionConverters._
-trait PublicAssetRepository {
- def create(username: Username, creationRequest: PublicAssetCreationRequest): Publisher[PublicAssetStorage]
-
- def update(username: Username, id: PublicAssetId, identityIds: Set[IdentityId]): Publisher[Void]
-
- def remove(username: Username, id: PublicAssetId): Publisher[Void]
-
- def revoke(username: Username): Publisher[Void]
-
- def get(username: Username, ids: Set[PublicAssetId]): Publisher[PublicAssetStorage]
-
- def get(username: Username, id: PublicAssetId): Publisher[PublicAssetStorage] = get(username, Set(id))
-
- def list(username: Username): Publisher[PublicAssetStorage]
-
- def listPublicAssetMetaDataOrderByIdAsc(username: Username): Publisher[PublicAssetMetadata]
-
- def listAllBlobIds(): Publisher[BlobId]
-
- def updateIdentityIds(username: Username, id: PublicAssetId, identityIdsToAdd: Seq[IdentityId], identityIdsToRemove: Seq[IdentityId]): Publisher[Void] =
- SMono(get(username, id))
- .map(publicAsset => (publicAsset.identityIds.toSet ++ identityIdsToAdd.toSet) -- identityIdsToRemove.toSet)
- .flatMap(identityIds => SMono(update(username, id, identityIds)))
-
- def getTotalSize(username: Username): Publisher[Long]
-}
-
class MemoryPublicAssetRepository @Inject()(val blobStore: BlobStore,
- val configuration: JMAPExtensionConfiguration,
+ val publicAssetTotalSizeLimit: PublicAssetTotalSizeLimit,
@Named("publicAssetUriPrefix") publicAssetUriPrefix: URI) extends PublicAssetRepository {
private val tableStore: Table[Username, PublicAssetId, PublicAssetMetadata] = Tables.synchronizedTable(HashBasedTable.create())
@@ -52,9 +25,9 @@ class MemoryPublicAssetRepository @Inject()(val blobStore: BlobStore,
override def create(username: Username, creationRequest: PublicAssetCreationRequest): Publisher[PublicAssetStorage] =
SMono(getTotalSize(username))
- .filter(totalSize => (totalSize + creationRequest.size.value) <= configuration.publicAssetTotalSizeLimit.asLong())
+ .filter(totalSize => (totalSize + creationRequest.size.value) <= publicAssetTotalSizeLimit.asLong())
.flatMap(_ => SMono(createAsset(username, creationRequest)))
- .switchIfEmpty(SMono.error(PublicAssetQuotaLimitExceededException(configuration.publicAssetTotalSizeLimit.asLong())))
+ .switchIfEmpty(SMono.error(PublicAssetQuotaLimitExceededException(publicAssetTotalSizeLimit.asLong())))
private def createAsset(username: Username, creationRequest: PublicAssetCreationRequest): SMono[PublicAssetStorage] =
SMono.fromCallable(() => creationRequest.content.apply().readAllBytes())
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetRequest.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetRequest.scala
index 515336cf2f..7f7e9e612a 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetRequest.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetSetRequest.scala
@@ -2,7 +2,6 @@ package com.linagora.tmail.james.jmap.publicAsset
import java.util.UUID
-import cats.implicits._
import com.linagora.tmail.james.jmap.json.PublicAssetSerializer.PublicAssetSetUpdateReads
import com.linagora.tmail.james.jmap.method.PublicAssetSetMethod.LOGGER
import com.linagora.tmail.james.jmap.method.standardErrorMessage
@@ -10,11 +9,10 @@ import com.linagora.tmail.james.jmap.publicAsset.ImageContentType.ImageContentTy
import org.apache.james.jmap.api.model.IdentityId
import org.apache.james.jmap.core.Id.Id
import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.core.{AccountId, Properties, SetError, UuidState}
-import org.apache.james.jmap.mail.{IdentityIds, BlobId => JmapBlobId}
-import org.apache.james.jmap.method.{WithAccountId, standardError}
+import org.apache.james.jmap.core.{AccountId, SetError, UuidState}
+import org.apache.james.jmap.method.WithAccountId
import org.apache.james.jmap.routes.BlobNotFoundException
-import play.api.libs.json.{JsArray, JsError, JsObject, JsString, JsSuccess, JsValue}
+import play.api.libs.json.{JsError, JsObject, JsSuccess}
import scala.util.Try
@@ -25,33 +23,6 @@ case class PublicAssetSetRequest(accountId: AccountId,
case class PublicAssetCreationId(id: Id)
-object PublicAssetSetCreationRequest {
- val knownProperties: Set[String] = Set("blobId", "identityIds")
-
- def validateProperties(jsObject: JsObject): Either[PublicAssetCreationParseException, JsObject] = {
- jsObject.fields.find(mapEntry => !knownProperties.contains(mapEntry._1))
- .map(e => Left(PublicAssetCreationParseException(SetError.invalidArguments(
- SetErrorDescription("Some unknown properties were specified"),
- Some(Properties.toProperties(Set(e._1)))))))
- .getOrElse(Right(jsObject))
- }
-}
-
-case class PublicAssetSetCreationRequest(blobId: JmapBlobId, identityIds: Option[Map[IdentityId, Boolean]] = None) {
-
- def parseIdentityIds: Either[PublicAssetCreationParseException, List[IdentityId]] =
- identityIds match {
- case None => Right(List.empty)
- case Some(identityIdMap) => identityIdMap.map {
- case (identityId: IdentityId, bolVal) => if (bolVal) {
- Right(identityId)
- } else {
- scala.Left(PublicAssetCreationParseException(SetError.invalidArguments(SetErrorDescription(s"identityId '$identityId' must be a true"), Some(Properties.toProperties(Set("identityIds"))))))
- }
- }.toList.sequence
- }
-}
-
case class UnparsedPublicAssetId(id: String) {
def tryAsPublicAssetId: Either[IllegalArgumentException, PublicAssetId] =
@@ -102,10 +73,6 @@ case class PublicAssetInvalidIdentityIdException(identityId: String) extends Pub
override val message: String = s"Invalid identityId: $identityId"
}
-case class PublicAssetIdentityIdNotFoundException(identityIds: Seq[IdentityId]) extends PublicAssetException {
- override val message: String = s"IdentityId not found: ${identityIds.map(_.id.toString).mkString(", ")}"
-}
-
sealed trait PublicAssetCreationResult {
def publicAssetCreationId: PublicAssetCreationId
}
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepository.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/MemoryJmapSettingsRepository.scala
similarity index 89%
rename from tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepository.scala
rename to tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/MemoryJmapSettingsRepository.scala
index 14d6170c9e..313bee5ae1 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/JmapSettingsRepository.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/settings/MemoryJmapSettingsRepository.scala
@@ -14,18 +14,6 @@ import reactor.core.scala.publisher.SMono
import scala.collection.mutable
-trait JmapSettingsRepository {
- def get(username: Username): Publisher[JmapSettings]
-
- def getLatestState(username: Username): Publisher[UuidState]
-
- def reset(username: Username, settings: JmapSettingsUpsertRequest): Publisher[SettingsStateUpdate]
-
- def updatePartial(username: Username, settingsPatch: JmapSettingsPatch): Publisher[SettingsStateUpdate]
-
- def delete(username: Username): Publisher[Void]
-}
-
case class MemoryJmapSettingsRepository @Inject()() extends JmapSettingsRepository {
import scala.jdk.CollectionConverters._
diff --git a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketManager.scala b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketManager.scala
index 6af3ef9452..aac027b056 100644
--- a/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketManager.scala
+++ b/tmail-backend/jmap/extensions/src/main/scala/com/linagora/tmail/james/jmap/ticket/TicketManager.scala
@@ -2,7 +2,6 @@ package com.linagora.tmail.james.jmap.ticket
import java.net.InetAddress
import java.time.{Clock, ZonedDateTime}
-import java.util.UUID
import com.linagora.tmail.james.jmap.JMAPExtensionConfiguration
import jakarta.inject.Inject
@@ -11,23 +10,6 @@ import org.apache.james.jmap.core.UTCDate
import reactor.core.scala.publisher.SMono
import scala.collection.mutable
-import scala.util.Try
-
-object TicketValue {
- def parse(string: String): Either[IllegalArgumentException, TicketValue] = Try(UUID.fromString(string))
- .map(TicketValue(_))
- .fold(e => Left(new IllegalArgumentException("TicketValue must be backed by a UUID", e)), Right(_))
-
- def generate: TicketValue = TicketValue(UUID.randomUUID())
-}
-
-case class ForbiddenException() extends RuntimeException
-case class TicketValue(value: UUID)
-case class Ticket(clientAddress: InetAddress,
- value: TicketValue,
- generatedOn: UTCDate,
- validUntil: UTCDate,
- username: Username)
object TicketManager {
private val validity: java.time.Duration = java.time.Duration.ofMinutes(1)
@@ -73,14 +55,6 @@ class TicketManager @Inject() (clock: Clock, ticketStore: TicketStore, jmapExten
.flatMap(ticketStore.delete)
}
-trait TicketStore {
- def persist(ticket: Ticket): SMono[Unit]
-
- def retrieve(value: TicketValue): SMono[Ticket]
-
- def delete(ticketValue: TicketValue): SMono[Unit]
-}
-
class MemoryTicketStore extends TicketStore {
private val map: mutable.Map[TicketValue, Ticket] = mutable.Map()
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStepTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStepTest.scala
index 9e30d29534..5f1bb78974 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStepTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUserDeletionTaskStepTest.scala
@@ -1,6 +1,6 @@
package com.linagora.tmail.james.jmap.contact
-import com.linagora.tmail.james.jmap.contact.ContactUsernameChangeTaskStepTest.{ALICE, ALICE_ACCOUNT_ID, ALICE_CONTACT, ANDRE_CONTACT, BOB_ACCOUNT_ID, MARIE_CONTACT}
+import ContactUsernameChangeTaskStepTest.{ALICE, ALICE_ACCOUNT_ID, ALICE_CONTACT, ANDRE_CONTACT, BOB_ACCOUNT_ID, MARIE_CONTACT}
import org.apache.james.core.Domain
import org.assertj.core.api.Assertions.{assertThat, assertThatCode}
import org.junit.jupiter.api.{BeforeEach, Test}
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStepTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStepTest.scala
index 32508d54e2..f247bf433d 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStepTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/ContactUsernameChangeTaskStepTest.scala
@@ -1,6 +1,6 @@
package com.linagora.tmail.james.jmap.contact
-import com.linagora.tmail.james.jmap.contact.ContactUsernameChangeTaskStepTest.{ALICE, ALICE_ACCOUNT_ID, ANDRE_CONTACT, BOB, BOB_ACCOUNT_ID, MARIE_CONTACT}
+import ContactUsernameChangeTaskStepTest.{ALICE, ALICE_ACCOUNT_ID, ANDRE_CONTACT, BOB, BOB_ACCOUNT_ID, MARIE_CONTACT}
import org.apache.james.core.{MailAddress, Username}
import org.apache.james.jmap.api.model.AccountId
import org.assertj.core.api.Assertions.assertThat
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListenerIntegrationTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListenerIntegrationTest.scala
index 90cdbba8e4..9ed93606bc 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListenerIntegrationTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/contact/EmailAddressContactListenerIntegrationTest.scala
@@ -1,6 +1,6 @@
package com.linagora.tmail.james.jmap.contact
-import com.linagora.tmail.james.jmap.contact.EmailAddressContactListenerIntegrationTest.{ACCOUNT_ID, CONTACT, CONTACT_ADDED_EVENT}
+import EmailAddressContactListenerIntegrationTest.{ACCOUNT_ID, CONTACT, CONTACT_ADDED_EVENT}
import org.apache.james.core.MailAddress
import org.apache.james.events.EventBusTestFixture.{EVENT_ID, NO_KEYS, USERNAME}
import org.apache.james.events.delivery.InVmEventDelivery
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/LabelSerializerTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/LabelSerializerTest.scala
new file mode 100644
index 0000000000..240b0d1530
--- /dev/null
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/LabelSerializerTest.scala
@@ -0,0 +1,66 @@
+package com.linagora.tmail.james.jmap.json
+
+import com.linagora.tmail.james.jmap.label.LabelRepositoryContract.RED
+import com.linagora.tmail.james.jmap.model.{DisplayName, LabelCreationRequest}
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import play.api.libs.json.{JsResult, Json}
+
+class LabelSerializerTest {
+
+ @Test
+ def deserializeCreationRequestShouldSuccess(): Unit = {
+ val jsInput = Json.parse(
+ """
+ {
+ "displayName":"Label 1",
+ "color":"#FF0000"
+ }
+ """)
+
+ val deserializeResult: JsResult[LabelCreationRequest] = LabelSerializer.deserializeLabelCreationRequest(jsInput)
+
+ assertThat(deserializeResult.isSuccess)
+ .isTrue
+ assertThat(deserializeResult.get)
+ .usingRecursiveComparison()
+ .isEqualTo(LabelCreationRequest(
+ displayName = DisplayName("Label 1"),
+ color = Some(RED)
+ ))
+ }
+
+ @Test
+ def givenObjectContainsUnknownPropertyDeserializeCreationRequestShouldFail(): Unit = {
+ val jsInput = Json.parse(
+ """
+ {
+ "displayName":"Label 1",
+ "color":"#FF0000",
+ "unknown": "BAD"
+ }
+ """)
+
+ val deserializeResult: JsResult[LabelCreationRequest] = LabelSerializer.deserializeLabelCreationRequest(jsInput)
+
+ assertThat(deserializeResult.isError)
+ .isTrue
+ }
+
+ @Test
+ def givenObjectContainsServerPropertyDeserializeCreationRequestShouldFail(): Unit = {
+ val jsInput = Json.parse(
+ """
+ {
+ "displayName":"Label 1",
+ "color":"#FF0000",
+ "id": "BAD"
+ }
+ """)
+
+ val deserializeResult: JsResult[LabelCreationRequest] = LabelSerializer.deserializeLabelCreationRequest(jsInput)
+
+ assertThat(deserializeResult.isError)
+ .isTrue
+ }
+}
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializerTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializerTest.scala
index c2fec7f5f4..4079b35260 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializerTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/json/PublicAssetSerializerTest.scala
@@ -135,6 +135,20 @@ class PublicAssetSerializerTest {
.isTrue
}
+ @Test
+ def deserializeCreationRequestShouldFailWhenUnknownPropertySpecified(): Unit = {
+ val jsInput: JsValue = Json.parse(
+ """{
+ | "blobId": "1234",
+ | "unknown": "something"
+ |}""".stripMargin)
+
+ val deserializeResult: JsResult[PublicAssetSetCreationRequest] = PublicAssetSerializer.deserializePublicAssetSetCreationRequest(jsInput)
+
+ assertThat(deserializeResult.isError)
+ .isTrue
+ }
+
@Test
def serializePublicAssetSetResponseShouldSucceed(): Unit = {
val response = PublicAssetSetResponse(
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/MemoryLabelChangeRepositoryTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/MemoryLabelChangeRepositoryTest.scala
new file mode 100644
index 0000000000..22663df8bc
--- /dev/null
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/label/MemoryLabelChangeRepositoryTest.scala
@@ -0,0 +1,25 @@
+package com.linagora.tmail.james.jmap.label
+
+import java.time.ZonedDateTime
+
+import com.linagora.tmail.james.jmap.label.LabelChangeRepositoryContract.DATE
+import org.apache.james.jmap.api.change.State
+import org.apache.james.utils.UpdatableTickingClock
+import org.junit.jupiter.api.BeforeEach
+
+class MemoryLabelChangeRepositoryTest extends LabelChangeRepositoryContract {
+ var repository: MemoryLabelChangeRepository = _
+ var updatableTickingClock: UpdatableTickingClock = _
+
+ override def testee: LabelChangeRepository = repository
+
+ override def stateFactory: State.Factory = State.Factory.DEFAULT
+
+ override def setClock(newTime: ZonedDateTime): Unit = updatableTickingClock.setInstant(newTime.toInstant)
+
+ @BeforeEach
+ def setup(): Unit = {
+ updatableTickingClock = new UpdatableTickingClock(DATE.toInstant)
+ repository = MemoryLabelChangeRepository(updatableTickingClock)
+ }
+}
\ No newline at end of file
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepositoryTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepositoryTest.scala
index 42a0bd69ec..31e6b306c9 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepositoryTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetRepositoryTest.scala
@@ -15,8 +15,9 @@ class MemoryPublicAssetRepositoryTest extends PublicAssetRepositoryContract {
@BeforeEach
def setup(): Unit = {
+
val blobStore = new DeDuplicationBlobStore(new MemoryBlobStoreDAO, BucketName.DEFAULT, blobIdFactory)
- memoryPublicAssetRepository = new MemoryPublicAssetRepository(blobStore, JMAPExtensionConfiguration(), PUBLIC_ASSET_URI_PREFIX)
+ memoryPublicAssetRepository = new MemoryPublicAssetRepository(blobStore, JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT, PUBLIC_ASSET_URI_PREFIX)
}
override def teste: PublicAssetRepository = memoryPublicAssetRepository
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetServiceTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetServiceTest.scala
index f4124767d7..f6e18926df 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetServiceTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/MemoryPublicAssetServiceTest.scala
@@ -15,10 +15,9 @@ class MemoryPublicAssetServiceTest extends PublicAssetServiceContract {
@BeforeEach
def setup(): Unit = {
- val jmapConfiguration = JMAPExtensionConfiguration()
val blobStore = new DeDuplicationBlobStore(new MemoryBlobStoreDAO, BucketName.DEFAULT, blobIdFactory)
- memoryPublicAssetRepository = new MemoryPublicAssetRepository(blobStore, jmapConfiguration, PUBLIC_ASSET_URI_PREFIX)
- publicAssetSetService = new PublicAssetSetService(identityRepository, memoryPublicAssetRepository, jmapConfiguration)
+ memoryPublicAssetRepository = new MemoryPublicAssetRepository(blobStore, JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT, PUBLIC_ASSET_URI_PREFIX)
+ publicAssetSetService = new PublicAssetSetService(identityRepository, memoryPublicAssetRepository, JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT)
}
override def testee: PublicAssetSetService = publicAssetSetService
diff --git a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetDeletionTaskStepTest.scala b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetDeletionTaskStepTest.scala
index 1237cac3f0..fc371562e9 100644
--- a/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetDeletionTaskStepTest.scala
+++ b/tmail-backend/jmap/extensions/src/test/scala/com/linagora/tmail/james/jmap/publicAsset/PublicAssetDeletionTaskStepTest.scala
@@ -26,7 +26,7 @@ class PublicAssetDeletionTaskStepTest {
publicAssetRepository = new MemoryPublicAssetRepository(new DeDuplicationBlobStore(new MemoryBlobStoreDAO,
BucketName.DEFAULT,
new PlainBlobId.Factory()),
- JMAPExtensionConfiguration(),
+ JMAPExtensionConfiguration.PUBLIC_ASSET_TOTAL_SIZE_LIMIT_DEFAULT,
PUBLIC_ASSET_URI_PREFIX)
publicAssetDeletionTaskStep = new PublicAssetDeletionTaskStep(publicAssetRepository);
}
diff --git a/tmail-backend/mailets/pom.xml b/tmail-backend/mailets/pom.xml
index a4f338e4d8..2462294688 100644
--- a/tmail-backend/mailets/pom.xml
+++ b/tmail-backend/mailets/pom.xml
@@ -14,24 +14,6 @@
Mailets for Twake Mail
-
- ${project.groupId}
- jmap-extensions
-
-
- ${project.groupId}
- team-mailboxes
-
-
- ${project.groupId}
- team-mailboxes
- test-jar
- test
-
-
- ${project.groupId}
- tmail-rate-limiter-api
-
${james.groupId}
apache-james-backends-redis
@@ -109,23 +91,23 @@
${james.groupId}
- james-server-rate-limiter-redis
- test
+ james-server-mailets
${james.groupId}
- james-server-rate-limiter-redis
+ james-server-queue-api
test
- test-jar
${james.groupId}
- james-server-queue-api
+ james-server-rate-limiter-redis
test
${james.groupId}
- james-server-mailets
+ james-server-rate-limiter-redis
+ test
+ test-jar
${james.groupId}
@@ -143,20 +125,42 @@
test
- org.mockito
- mockito-core
- test
+ ${project.groupId}
+ jmap-extensions-api
- org.testcontainers
- testcontainers
+ ${project.groupId}
+ team-mailboxes
+
+
+ ${project.groupId}
+ team-mailboxes
+ test-jar
test
+
+ ${project.groupId}
+ tmail-rate-limiter-api
+
+
+ com.sparkjava
+ spark-core
+
net.javacrumbs.json-unit
json-unit-assertj
test
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.testcontainers
+ testcontainers
+ test
+
diff --git a/tmail-backend/pom.xml b/tmail-backend/pom.xml
index 7d0437c6db..a19d268d92 100644
--- a/tmail-backend/pom.xml
+++ b/tmail-backend/pom.xml
@@ -48,6 +48,7 @@
imap-extensions
jmap/extensions
+ jmap/extensions-api
jmap/extensions-cassandra
jmap/extensions-opensearch
jmap/extensions-rabbitmq
@@ -142,6 +143,17 @@
${project.version}
test-jar
+
+ ${project.groupId}
+ jmap-extensions-api
+ ${project.version}
+
+
+ ${project.groupId}
+ jmap-extensions-api
+ ${project.version}
+ test-jar
+
${project.groupId}
jmap-extensions-opensearch
diff --git a/tmail-backend/tmail-third-party/openpaas/pom.xml b/tmail-backend/tmail-third-party/openpaas/pom.xml
index 9d63363100..a3f1cf7f07 100644
--- a/tmail-backend/tmail-third-party/openpaas/pom.xml
+++ b/tmail-backend/tmail-third-party/openpaas/pom.xml
@@ -15,10 +15,6 @@
OpenPaaS integration for Twake Mail
-
- ${project.groupId}
- jmap-extensions
-
${james.groupId}
apache-james-backends-rabbitmq
@@ -47,6 +43,10 @@
testing-base
test
+
+ ${project.groupId}
+ jmap-extensions-api
+
com.fasterxml.jackson.core
jackson-annotations
@@ -59,6 +59,10 @@
com.fasterxml.jackson.core
jackson-databind
+
+ com.sparkjava
+ spark-core
+
io.projectreactor
reactor-core
@@ -74,7 +78,6 @@
-
diff --git a/tmail-backend/webadmin/webadmin-email-address-contact/pom.xml b/tmail-backend/webadmin/webadmin-email-address-contact/pom.xml
index 38694b7d7d..ee9124bff9 100644
--- a/tmail-backend/webadmin/webadmin-email-address-contact/pom.xml
+++ b/tmail-backend/webadmin/webadmin-email-address-contact/pom.xml
@@ -14,10 +14,6 @@
Twake Mail :: Webadmin :: Email Address Contacts
-
- ${project.groupId}
- jmap-extensions
-
${james.groupId}
james-server-data-api
@@ -47,6 +43,10 @@
metrics-tests
test
+
+ ${project.groupId}
+ jmap-extensions-api
+
net.javacrumbs.json-unit
json-unit-assertj