You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by GitBox <gi...@apache.org> on 2021/11/22 06:59:25 UTC

[GitHub] [james-project] vttranlina commented on a change in pull request #754: JAMES-3534 Implement Identity/set update (custom identities)

vttranlina commented on a change in pull request #754:
URL: https://github.com/apache/james-project/pull/754#discussion_r753993618



##########
File path: server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentitySetUpdatePerformer.scala
##########
@@ -0,0 +1,146 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.method
+
+import org.apache.james.jmap.api.identity.{IdentityNotFoundException, IdentityRepository, IdentityUpdateRequest}
+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.{Properties, SetError}
+import org.apache.james.jmap.json.IdentitySerializer
+import org.apache.james.jmap.mail.{IdentitySetRequest, InvalidPropertyException}
+import org.apache.james.jmap.method.IdentitySetUpdatePerformer.{IdentitySetUpdateResults, IdentityUpdate, IdentityUpdateParseException, UpdateFailure, UpdateResult, UpdateSuccess}
+import org.apache.james.mailbox.MailboxSession
+import play.api.libs.json.{JsObject, JsPath, JsValue, JsonValidationError}
+import reactor.core.scala.publisher.{SFlux, SMono}
+
+import java.util.UUID
+import javax.inject.Inject
+import scala.util.Try
+
+object IdentitySetUpdatePerformer {
+  sealed trait UpdateResult
+
+  case class UpdateSuccess(id: IdentityId) extends UpdateResult
+
+  case class UpdateFailure(id: UnparsedIdentitySetId, exception: Throwable) extends UpdateResult {
+    def asSetError: SetError = exception match {
+      case e: IdentityUpdateParseException => e.setError
+      case e: IdentityNotFoundException => SetError.notFound(SetErrorDescription(e.getMessage))
+      case e: InvalidPropertyException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
+      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(e.getMessage), None)
+      case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
+    }
+  }
+
+  object IdentitySetUpdateResponse {
+    def empty: IdentitySetUpdateResponse = IdentitySetUpdateResponse(JsObject(Map[String, JsValue]()))
+  }
+
+  case class IdentitySetUpdateResponse(value: JsObject)
+
+  case class IdentitySetUpdateResults(results: Seq[UpdateResult]) {
+    def updated: Map[IdentityId, IdentitySetUpdateResponse] =
+      results.flatMap(result => result match {
+        case success: UpdateSuccess => Some((success.id, IdentitySetUpdateResponse.empty))
+        case _ => None
+      }).toMap
+
+    def notUpdated: Map[UnparsedIdentitySetId, SetError] = results.flatMap(result => result match {
+      case failure: UpdateFailure => Some(failure.id, failure.asSetError)
+      case _ => None
+    }).toMap
+  }
+
+  case class UnparsedIdentitySetId(id: Id) {
+    def serialise: String = id.value
+
+    def parse: Either[IllegalArgumentException, IdentityId] = Try(UUID.fromString(id.value))
+      .toEither
+      .left.map({
+      case e: IllegalArgumentException => e
+      case e => new IllegalArgumentException(e)
+    }).map(uuid => IdentityId(uuid))
+  }
+
+  object IdentityUpdateParseException {
+    def from(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): IdentityUpdateParseException = {
+      val setError: SetError = errors.head match {
+        case (path, Seq()) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in Identity object is not valid"))
+        case (path, Seq(JsonValidationError(Seq("error.path.missing")))) =>
+          SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in Identity object"))
+        case (path, Seq(JsonValidationError(Seq(message)))) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in Identity object is not valid: $message"))
+        case (path, _) => SetError.invalidArguments(SetErrorDescription(s"Unknown error on property '$path'"))
+      }
+      IdentityUpdateParseException(setError)
+    }
+  }
+
+  case class IdentityUpdateParseException(setError: SetError) extends IllegalArgumentException
+
+  object IdentityUpdate {
+    private val serverSetProperty: Set[String] = Set("id", "mayDelete", "email")
+    private val assignableProperties: Set[String] = Set("name", "replyTo", "bcc", "textSignature", "htmlSignature")
+    private val knownProperties: Set[String] = assignableProperties ++ serverSetProperty
+
+    def validateProperties(jsObject: JsObject): Either[IdentityUpdateParseException, JsObject] =
+      (jsObject.keys.intersect(serverSetProperty), jsObject.keys.diff(knownProperties)) match {
+        case (_, unknownProperties) if unknownProperties.nonEmpty =>
+          Left(IdentityUpdateParseException(SetError.invalidArguments(
+            SetErrorDescription("Some unknown properties were specified"),
+            Some(Properties.toProperties(unknownProperties.toSet)))))
+        case (specifiedServerSetProperties, _) if specifiedServerSetProperties.nonEmpty =>
+          Left(IdentityUpdateParseException(SetError.invalidArguments(
+            SetErrorDescription("Some server-set properties were specified"),
+            Some(Properties.toProperties(specifiedServerSetProperties.toSet)))))
+        case _ => scala.Right(jsObject)
+      }
+  }
+}
+
+class IdentitySetUpdatePerformer @Inject()(identityRepository: IdentityRepository) {
+  def update(request: IdentitySetRequest, mailboxSession: MailboxSession): SMono[IdentitySetUpdateResults] =
+    SFlux.fromIterable(request.update.getOrElse(Map()))
+      .concatMap {
+        case (unparsedId, json) =>
+          val either: Either[Exception, SMono[UpdateResult]] = for {
+            identityId <- unparsedId.parse
+            updateRequest <- parseRequest(json)
+          } yield {
+            update(identityId, updateRequest, mailboxSession)
+              .onErrorResume((error: Throwable) => SMono.just[UpdateResult](UpdateFailure(unparsedId, error)))

Review comment:
       the new version of IDE suggests it, maybe it is more modern?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



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