You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/04/13 02:53:37 UTC
[james-project] 10/13: JAMES-2888: Remove JMAPRoutes,
correct CoreEcho method and test, ordering pom,
remove unused [no review] commit. Just unit test, not integration yet.
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 d72dba483f54fd540fcd1a722d62b982229e8c9c
Author: ducnv <du...@gmail.com>
AuthorDate: Wed Apr 1 08:59:18 2020 +0700
JAMES-2888: Remove JMAPRoutes, correct CoreEcho method and test, ordering pom, remove unused [no review] commit.
Just unit test, not integration yet.
---
pom.xml | 5 +
server/protocols/jmap-rfc-8621/pom.xml | 42 +++---
.../org/apache/james/jmap/json/Serializer.scala | 5 +
.../ResponseObject.scala => method/CoreEcho.scala} | 14 +-
.../ResponseObject.scala => method/Method.scala} | 13 +-
.../apache/james/jmap/model/ResponseObject.scala | 5 +
.../apache/james/jmap/routes/JMAPApiRoutes.scala | 99 +++++++++-----
.../scala/org/apache/james/jmap/json/Fixture.scala | 13 +-
.../jmap/json/RequestObjectSerializationTest.scala | 12 +-
.../json/ResponseObjectSerializationTest.scala | 8 +-
.../apache/james/jmap/method/CoreEchoTest.scala | 3 +-
.../james/jmap/routes/JMAPApiRoutesTest.scala | 152 +++++++++++++++++++++
12 files changed, 299 insertions(+), 72 deletions(-)
diff --git a/pom.xml b/pom.xml
index a30cb9b..d39bd79 100644
--- a/pom.xml
+++ b/pom.xml
@@ -664,6 +664,11 @@
<scope>import</scope>
</dependency>
<dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-scala-extensions_${scala.base}</artifactId>
+ <version>0.5.1</version>
+ </dependency>
+ <dependency>
<groupId>${james.groupId}</groupId>
<artifactId>apache-james-backends-cassandra</artifactId>
<version>${project.version}</version>
diff --git a/server/protocols/jmap-rfc-8621/pom.xml b/server/protocols/jmap-rfc-8621/pom.xml
index 79dfe41..bb6a83b 100644
--- a/server/protocols/jmap-rfc-8621/pom.xml
+++ b/server/protocols/jmap-rfc-8621/pom.xml
@@ -33,6 +33,28 @@
<dependencies>
<dependency>
+ <groupId>com.typesafe.play</groupId>
+ <artifactId>play-json_${scala.base}</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>eu.timepit</groupId>
+ <artifactId>refined_${scala.base}</artifactId>
+ <version>0.9.13</version>
+ </dependency>
+ <dependency>
+ <groupId>io.rest-assured</groupId>
+ <artifactId>rest-assured</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor.netty</groupId>
+ <artifactId>reactor-netty</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-scala-extensions_${scala.base}</artifactId>
+ </dependency>
+ <dependency>
<groupId>${james.groupId}</groupId>
<artifactId>james-core</artifactId>
</dependency>
@@ -56,25 +78,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.typesafe.play</groupId>
- <artifactId>play-json_${scala.base}</artifactId>
- </dependency>
- <dependency>
- <groupId>eu.timepit</groupId>
- <artifactId>refined_${scala.base}</artifactId>
- <version>0.9.13</version>
- </dependency>
- <dependency>
- <groupId>io.projectreactor</groupId>
- <artifactId>reactor-scala-extensions_${scala.base}</artifactId>
- <version>0.5.1</version>
- </dependency>
- <dependency>
- <groupId>io.rest-assured</groupId>
- <artifactId>rest-assured</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
@@ -94,7 +97,6 @@
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
</dependencies>
-
<build>
<plugins>
<plugin>
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
index 2189af9..173a82d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
@@ -19,6 +19,7 @@
package org.apache.james.jmap.json
+import java.io.InputStream
import java.net.URL
import org.apache.james.core.Username
@@ -141,6 +142,10 @@ class Serializer {
Json.parse(input).validate[RequestObject]
}
+ def deserializeRequestObject(input: InputStream): JsResult[RequestObject] = {
+ Json.parse(input).validate[RequestObject]
+ }
+
def deserializeResponseObject(input: String): JsResult[ResponseObject] = {
Json.parse(input).validate[ResponseObject]
}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEcho.scala
similarity index 71%
copy from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
copy to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEcho.scala
index a50d3a2..efeb0e7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEcho.scala
@@ -16,9 +16,17 @@
* specific language governing permissions and limitations *
* under the License. *
* ***************************************************************/
+package org.apache.james.jmap.method
-package org.apache.james.jmap.model
-import org.apache.james.jmap.model.State.State
+import eu.timepit.refined.auto._
+import org.apache.james.jmap.model.Invocation
+import org.apache.james.jmap.model.Invocation.MethodName
+import org.reactivestreams.Publisher
+import reactor.core.scala.publisher.SMono
-case class ResponseObject(sessionState: State, methodResponses: Seq[Invocation])
+class CoreEcho extends Method {
+ override val methodName = MethodName("Core/echo")
+
+ override def process(invocation: Invocation): Publisher[Invocation] = SMono.just(invocation)
+}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
similarity index 78%
copy from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
copy to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
index a50d3a2..7a89b84 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
@@ -16,9 +16,16 @@
* specific language governing permissions and limitations *
* under the License. *
* ***************************************************************/
+package org.apache.james.jmap.method
-package org.apache.james.jmap.model
-import org.apache.james.jmap.model.State.State
+import org.apache.james.jmap.model.Invocation
+import org.apache.james.jmap.model.Invocation.MethodName
+import org.reactivestreams.Publisher
+
+trait Method {
+ val methodName: MethodName
+
+ def process(invocation: Invocation): Publisher[Invocation]
+}
-case class ResponseObject(sessionState: State, methodResponses: Seq[Invocation])
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
index a50d3a2..783801b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
@@ -19,6 +19,11 @@
package org.apache.james.jmap.model
+import eu.timepit.refined.auto._
import org.apache.james.jmap.model.State.State
case class ResponseObject(sessionState: State, methodResponses: Seq[Invocation])
+
+object ResponseObject {
+ val SESSION_STATE: State = "75128aab4b1b"
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
index ddfe2fe..8336487 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
@@ -18,52 +18,87 @@
* ***************************************************************/
package org.apache.james.jmap.routes
+import java.io.InputStream
+import java.nio.charset.StandardCharsets
+import java.util.stream
+import java.util.stream.Stream
+
import eu.timepit.refined.auto._
+import io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE
+import io.netty.handler.codec.http.HttpMethod
+import io.netty.handler.codec.http.HttpResponseStatus.OK
+import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE
+import org.apache.james.jmap.JMAPUrls.JMAP
import org.apache.james.jmap.json.Serializer
import org.apache.james.jmap.method.CoreEcho
import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
import org.apache.james.jmap.model.{Invocation, RequestObject, ResponseObject}
-import org.reactivestreams.Publisher
+import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
+import org.slf4j.{Logger, LoggerFactory}
import play.api.libs.json.{JsError, JsSuccess, Json}
-import reactor.core.scala.publisher.SMono
+import reactor.core.publisher.Mono
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
import reactor.netty.http.server.{HttpServerRequest, HttpServerResponse}
-object JMAPApiRoutes {
- private val ECHO_METHOD = new CoreEcho()
-}
+class JMAPApiRoutes extends JMAPRoutes {
+ override def logger(): Logger = LoggerFactory.getLogger(getClass)
+
+ private val coreEcho = new CoreEcho
-class JMAPApiRoutes {
- private val echoMethod = JMAPApiRoutes.ECHO_METHOD
+ override def routes(): stream.Stream[JMAPRoute] = Stream.of(
+ JMAPRoute.builder
+ .endpoint(new Endpoint(HttpMethod.POST, JMAP))
+ .action(this.post)
+ .corsHeaders,
+ JMAPRoute.builder
+ .endpoint(new Endpoint(HttpMethod.OPTIONS, JMAP))
+ .action(JMAPRoutes.CORS_CONTROL)
+ .corsHeaders())
- def post(httpServerRequest: HttpServerRequest, httpServerResponse: HttpServerResponse): SMono[Void] = {
- SMono.fromPublisher(extractRequestObject(httpServerRequest))
- .flatMap(this.process)
- .doOnError(e => new RuntimeException(e.getMessage))
+ private def post(httpServerRequest: HttpServerRequest, httpServerResponse: HttpServerResponse): Mono[Void] =
+ this.requestAsJsonStream(httpServerRequest)
+ .flatMap(requestObject => this.process(requestObject, httpServerResponse))
+ .onErrorResume(throwable => SMono.fromPublisher(handleInternalError(httpServerResponse, throwable)))
+ .subscribeOn(Schedulers.elastic)
+ .asJava()
.`then`()
+
+ private def requestAsJsonStream(httpServerRequest: HttpServerRequest): SMono[RequestObject] = {
+ SMono.fromPublisher(httpServerRequest
+ .receive()
+ .aggregate()
+ .asInputStream())
+ .flatMap(this.parseRequestObject)
}
- private def process(requestObject: RequestObject): SMono[ResponseObject] = {
- SMono.just(
- requestObject.methodCalls.map((invocation: Invocation) =>
- invocation.methodName match {
- case echoMethod.methodName => echoMethod.process(invocation)
- case _ => SMono.just(new Invocation(
- MethodName("error"),
- Arguments(Json.obj("type" -> "Not implemented")),
- invocation.methodCallId))
- }
+ private def parseRequestObject(inputStream: InputStream): SMono[RequestObject] =
+ new Serializer().deserializeRequestObject(inputStream) match {
+ case JsSuccess(requestObject, _) => SMono.just(requestObject)
+ case JsError(errors) => SMono.raiseError(new RuntimeException(errors.toString()))
+ }
+
+ private def process(requestObject: RequestObject, httpServerResponse: HttpServerResponse): SMono[Void] =
+ requestObject
+ .methodCalls
+ .map(this.processMethodWithMatchName)
+ .foldLeft(SFlux.empty[Invocation]) { (flux: SFlux[Invocation], mono: SMono[Invocation]) => flux.mergeWith(mono) }
+ .collectSeq()
+ .flatMap((invocations: Seq[Invocation]) =>
+ SMono.fromPublisher(httpServerResponse.status(OK)
+ .header(CONTENT_TYPE, JSON_CONTENT_TYPE)
+ .sendString(
+ SMono.fromCallable(() =>
+ new Serializer().serialize(ResponseObject(ResponseObject.SESSION_STATE, invocations)).toString()),
+ StandardCharsets.UTF_8
+ ).`then`())
)
- ).flatMap((invocations: Seq[Invocation]) => SMono.just(ResponseObject(ResponseObject.SESSION_STATE, invocations)))
- }
- private def extractRequestObject(httpServerRequest: HttpServerRequest): Publisher[RequestObject] = {
- httpServerRequest
- .receive()
- .asInputStream()
- .flatMap(inputStream => new Serializer().deserializeRequestObject(inputStream) match {
- case JsSuccess(requestObject, _) => SMono.just(new ResponseObject(ResponseObject.SESSION_STATE, requestObject.methodCalls))
- case JsError(errors) => SMono.raiseError(new RuntimeException(errors.toString()))
- })
+ private def processMethodWithMatchName(invocation: Invocation): SMono[Invocation] = invocation.methodName match {
+ case coreEcho.methodName => SMono.fromPublisher(coreEcho.process(invocation))
+ case _ => SMono.just(new Invocation(
+ MethodName("error"),
+ Arguments(Json.obj("type" -> "Not implemented")),
+ invocation.methodCallId))
}
}
-
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala
index bba26fa..e249535 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala
@@ -23,7 +23,7 @@ import eu.timepit.refined.auto._
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model.Id.Id
import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
-import org.apache.james.jmap.model.{ClientId, CreatedIds, Invocation, ServerId}
+import org.apache.james.jmap.model.{ClientId, CreatedIds, Invocation, ResponseObject, ServerId}
import play.api.libs.json.Json
object Fixture {
@@ -32,12 +32,19 @@ object Fixture {
val coreIdentifier: CapabilityIdentifier = "urn:ietf:params:jmap:core"
val mailIdentifier: CapabilityIdentifier = "urn:ietf:params:jmap:mail"
val invocation1: Invocation = Invocation(
- methodName = MethodName("Core/echo1"),
+ methodName = MethodName("Core/echo"),
arguments = Arguments(Json.obj("arg1" -> "arg1data", "arg2" -> "arg2data")),
methodCallId = MethodCallId("c1"))
val invocation2: Invocation = Invocation(
- methodName = MethodName("Core/echo2"),
+ methodName = MethodName("Core/echo"),
arguments = Arguments(Json.obj("arg3" -> "arg3data", "arg4" -> "arg4data")),
methodCallId = MethodCallId("c2")
)
+ val unsupportedInvocation: Invocation = Invocation(
+ methodName = MethodName("error"),
+ arguments = Arguments(Json.obj("type" -> "Not implemented")),
+ methodCallId = MethodCallId("notsupport"))
+ val responseObject1: ResponseObject = ResponseObject(ResponseObject.SESSION_STATE, Seq(invocation1))
+ val responseObject2: ResponseObject = ResponseObject(ResponseObject.SESSION_STATE, Seq(invocation2))
+ val responseObjectWithUnsupportedMethod: ResponseObject = ResponseObject(ResponseObject.SESSION_STATE, Seq(invocation1, unsupportedInvocation))
}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala
index 173c026..4d1c5bd 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala
@@ -39,7 +39,7 @@ class RequestObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "using": [ "urn:ietf:params:jmap:core"],
| "methodCalls": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ]
@@ -60,7 +60,7 @@ class RequestObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "using": [ "urn:ietf:params:jmap:core"],
| "methodCalls": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ]
@@ -82,11 +82,11 @@ class RequestObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
| "methodCalls": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ],
- | [ "Core/echo2", {
+ | [ "Core/echo", {
| "arg3": "arg3data",
| "arg4": "arg4data"
| }, "c2" ]
@@ -109,7 +109,7 @@ class RequestObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
| "methodCalls": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ]
@@ -132,7 +132,7 @@ class RequestObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
| "methodCalls": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ]
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala
index 6dbbd8e..11c2504 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala
@@ -37,7 +37,7 @@ class ResponseObjectSerializationTest extends AnyWordSpec with Matchers {
"""
|{
| "methodResponses": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ]
@@ -57,11 +57,11 @@ class ResponseObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "sessionState": "75128aab4b1b",
| "methodResponses": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ],
- | [ "Core/echo2", {
+ | [ "Core/echo", {
| "arg3": "arg3data",
| "arg4": "arg4data"
| }, "c2" ]
@@ -82,7 +82,7 @@ class ResponseObjectSerializationTest extends AnyWordSpec with Matchers {
|{
| "sessionState": "75128aab4b1b",
| "methodResponses": [
- | [ "Core/echo1", {
+ | [ "Core/echo", {
| "arg1": "arg1data",
| "arg2": "arg2data"
| }, "c1" ]
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala
index b723321..2d78e5f 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala
@@ -22,6 +22,7 @@ import org.apache.james.jmap.json.Fixture.{invocation1, invocation2}
import org.apache.james.jmap.model.Invocation
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
+import reactor.core.scala.publisher.SMono
class CoreEchoTest extends AnyWordSpec with Matchers {
private val echoMethod: CoreEcho = new CoreEcho()
@@ -38,7 +39,7 @@ class CoreEchoTest extends AnyWordSpec with Matchers {
"success and not return anything else different than the original invocation" in {
val wrongExpected: Invocation = invocation2
val dataResponse = SMono.fromPublisher(echoMethod.process(invocation1)).block()
-
+
dataResponse should not be(wrongExpected)
}
}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
new file mode 100644
index 0000000..55bf125
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
@@ -0,0 +1,152 @@
+/** **************************************************************
+ * 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.routes
+
+import java.nio.charset.StandardCharsets
+
+import com.google.common.collect.ImmutableSet
+import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
+import io.restassured.RestAssured
+import io.restassured.builder.RequestSpecBuilder
+import io.restassured.config.EncoderConfig.encoderConfig
+import io.restassured.config.RestAssuredConfig.newConfig
+import io.restassured.http.ContentType
+import org.apache.http.HttpStatus
+import org.apache.james.jmap.JMAPUrls.JMAP
+import org.apache.james.jmap.json.Fixture._
+import org.apache.james.jmap.json.Serializer
+import org.apache.james.jmap.model.RequestObject
+import org.apache.james.jmap.{JMAPConfiguration, JMAPRoutesHandler, JMAPServer, Version}
+import org.scalatest.BeforeAndAfter
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+class JMAPApiRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
+
+ private val TEST_CONFIGURATION: JMAPConfiguration = JMAPConfiguration.builder().enable().randomPort().build()
+ private val ACCEPT_JMAP_VERSION_HEADER = "application/json; jmapVersion="
+ private val ACCEPT_DRAFT_VERSION_HEADER = ACCEPT_JMAP_VERSION_HEADER + Version.DRAFT.getVersion
+ private val ACCEPT_RFC8621_VERSION_HEADER = ACCEPT_JMAP_VERSION_HEADER + Version.RFC8621.getVersion
+
+ private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes()
+ private val ROUTES_HANDLER: ImmutableSet[JMAPRoutesHandler] = ImmutableSet.of(new JMAPRoutesHandler(Version.RFC8621, JMAP_API_ROUTE))
+
+ private val REQUEST_OBJECT: String =
+ new Serializer().serialize(RequestObject(Seq(coreIdentifier), Seq(invocation1))).toString()
+
+ private val REQUEST_OBJECT_WITH_UNSUPPORTED_METHOD: String =
+ new Serializer().serialize(RequestObject(Seq(coreIdentifier), Seq(invocation1, unsupportedInvocation))).toString()
+
+ private val RESPONSE_OBJECT: String = new Serializer().serialize(responseObject1).toString()
+ private val RESPONSE_OBJECT_WITH_UNSUPPORTED_METHOD: String = new Serializer().serialize(responseObjectWithUnsupportedMethod).toString()
+
+ var jmapServer: JMAPServer = _
+
+ before {
+ jmapServer = new JMAPServer(TEST_CONFIGURATION, ROUTES_HANDLER)
+ jmapServer.start()
+
+ RestAssured.requestSpecification = new RequestSpecBuilder()
+ .setContentType(ContentType.JSON)
+ .setAccept(ContentType.JSON)
+ .setConfig(newConfig.encoderConfig(encoderConfig.defaultContentCharset(StandardCharsets.UTF_8)))
+ .setPort(jmapServer.getPort.getValue)
+ .setBasePath(JMAP)
+ .build
+ }
+
+ after {
+ jmapServer.stop()
+ }
+
+ "RFC-8621 version, GET" should "not supported and return 404 status" in {
+ RestAssured
+ .`given`()
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .when()
+ .get
+ .then
+ .statusCode(HttpStatus.SC_NOT_FOUND)
+ }
+
+ "RFC-8621 version, POST, without body" should "return 200 status" in {
+ RestAssured
+ .`given`()
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .when()
+ .post
+ .then
+ .statusCode(HttpStatus.SC_OK)
+ }
+
+ "RFC-8621 version, POST, methods include supported" should "return OK status" in {
+ val response = RestAssured
+ .`given`()
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(REQUEST_OBJECT)
+ .when()
+ .post()
+ .then
+ .statusCode(HttpStatus.SC_OK)
+ .contentType(ContentType.JSON)
+ .extract()
+ .body()
+ .asString()
+
+ response shouldBe (RESPONSE_OBJECT)
+ }
+
+ "RFC-8621 version, POST, with methods" should "return OK status, ResponseObject depend on method" in {
+
+ val response = RestAssured
+ .`given`()
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(REQUEST_OBJECT_WITH_UNSUPPORTED_METHOD)
+ .when()
+ .post()
+ .then
+ .statusCode(HttpStatus.SC_OK)
+ .contentType(ContentType.JSON)
+ .extract()
+ .body()
+ .asString()
+
+ response shouldBe (RESPONSE_OBJECT_WITH_UNSUPPORTED_METHOD)
+ }
+
+ "Draft version, GET" should "return 404 status" in {
+ RestAssured
+ .`given`()
+ .header(ACCEPT.toString, ACCEPT_DRAFT_VERSION_HEADER)
+ .when()
+ .get
+ .then
+ .statusCode(HttpStatus.SC_NOT_FOUND)
+ }
+
+ "Draft version, POST, without body" should "return 400 status" in {
+ RestAssured
+ .`given`()
+ .header(ACCEPT.toString, ACCEPT_DRAFT_VERSION_HEADER)
+ .when()
+ .post
+ .then
+ .statusCode(HttpStatus.SC_NOT_FOUND)
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org