You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/02/03 02:51:09 UTC

[james-project] 03/12: JAMES-3491 JMAP WebSocket transport JSON serialization

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

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

commit fe23fea43f3dd556b186e320f041f9e32aa06a2d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Jan 28 10:47:54 2021 +0700

    JAMES-3491 JMAP WebSocket transport JSON serialization
---
 .../james/jmap/json/ResponseSerializer.scala       | 58 ++++++++++++++++++++--
 1 file changed, 53 insertions(+), 5 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
index 8bdbe4a..edace41 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
@@ -19,9 +19,6 @@
 
 package org.apache.james.jmap.json
 
-import java.io.InputStream
-import java.net.URL
-
 import eu.timepit.refined.refineV
 import io.netty.handler.codec.http.HttpResponseStatus
 import org.apache.james.core.Username
@@ -34,8 +31,11 @@ import org.apache.james.jmap.core.{Account, Invocation, Session, _}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
 
+import java.io.InputStream
+import java.net.URL
 import scala.collection.{Seq => LegacySeq}
 import scala.language.implicitConversions
+import scala.util.Try
 
 object ResponseSerializer {
   // CreateIds
@@ -68,7 +68,7 @@ object ResponseSerializer {
 
   private implicit val stateWrites: Writes[State] = Json.valueWrites[State]
   // ResponseObject
-  private implicit val responseObjectFormat: Format[ResponseObject] = Json.format[ResponseObject]
+  private implicit val responseObjectFormat: OFormat[ResponseObject] = Json.format[ResponseObject]
 
   private implicit val maxSizeUploadWrites: Writes[MaxSizeUpload] = Json.valueWrites[MaxSizeUpload]
   private implicit val maxConcurrentUploadWrites: Writes[MaxConcurrentUpload] = Json.valueWrites[MaxConcurrentUpload]
@@ -163,7 +163,45 @@ object ResponseSerializer {
 
   private implicit val jsErrorWrites: Writes[JsError] = Json.writes[JsError]
 
-  private implicit val problemDetailsWrites: Writes[ProblemDetails] = Json.writes[ProblemDetails]
+  private implicit val problemDetailsWrites: OWrites[ProblemDetails] = Json.writes[ProblemDetails]
+
+  private implicit val requestIdFormat: Format[RequestId] = Json.valueFormat[RequestId]
+  private implicit val webSocketRequestReads: Reads[WebSocketRequest] = {
+    case jsObject: JsObject =>
+      for {
+        requestId <- jsObject.value.get("requestId")
+          .map(requestIdJson => requestIdFormat.reads(requestIdJson).map(Some(_)))
+          .getOrElse(JsSuccess(None))
+        request <- requestObjectRead.reads(jsObject)
+      } yield {
+        WebSocketRequest(requestId, request)
+      }
+    case _ => JsError("Expecting a JsObject to represent a webSocket inbound request")
+  }
+  private implicit val webSocketInboundReads: Reads[WebSocketInboundMessage] = {
+    case json: JsObject =>
+      json.value.get("@type") match {
+        case Some(JsString("Request")) => webSocketRequestReads.reads(json)
+        case Some(JsString(unknownType)) => JsError(s"Unknown @type filed on a webSocket inbound message: $unknownType")
+        case Some(invalidType) => JsError(s"Invalid @type filed on a webSocket inbound message: expecting a JsString, got $invalidType")
+        case None => JsError(s"Missing @type filed on a webSocket inbound message")
+      }
+    case _ => JsError("Expecting a JsObject to represent a webSocket inbound message")
+  }
+  private implicit val webSocketResponseWrites: Writes[WebSocketResponse] = response => {
+    val apiResponseJson: JsObject = responseObjectFormat.writes(response.responseObject)
+    JsObject(Map(
+      "@type" -> JsString("Response"),
+      "requestId" -> response.requestId.map(_.value).map(JsString).getOrElse(JsNull))
+      ++ apiResponseJson.value)
+  }
+  private implicit val webSocketErrorWrites: Writes[WebSocketError] = error => {
+    val errorJson: JsObject = problemDetailsWrites.writes(error.problemDetails)
+    JsObject(Map(
+      "@type" -> JsString("RequestError"),
+      "requestId" -> error.requestId.map(_.value).map(JsString).getOrElse(JsNull))
+      ++ errorJson.value)
+  }
 
   def serialize(session: Session): JsValue = Json.toJson(session)
 
@@ -175,8 +213,18 @@ object ResponseSerializer {
 
   def serialize(errors: JsError): JsValue = Json.toJson(errors)
 
+  def serialize(response: WebSocketOutboundMessage): JsValue = {
+    case response: WebSocketResponse => Json.toJson(response)
+    case error: WebSocketError => Json.toJson(error)
+  }
+
+  def serialize(errors: WebSocketError): JsValue = Json.toJson(errors)
+
   def deserializeRequestObject(input: String): JsResult[RequestObject] = Json.parse(input).validate[RequestObject]
 
+  def deserializeWebSocketInboundMessage(input: String): JsResult[WebSocketInboundMessage] = Try(Json.parse(input).validate[WebSocketInboundMessage])
+    .fold(e => JsError(e.getMessage), result => result)
+
   def deserializeRequestObject(input: InputStream): JsResult[RequestObject] = Json.parse(input).validate[RequestObject]
 
   def deserializeResponseObject(input: String): JsResult[ResponseObject] = Json.parse(input).validate[ResponseObject]


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