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 <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
