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
The following commit(s) were added to refs/heads/master by this push:
new 949491c JAMES-3609 Allow to inject custom BlobResolver
new d93a431 Merge pull request #528 from chibenwa/JAMES-3609
949491c is described below
commit 949491c8f4cf93e8450d075af0cd75cced666806
Author: Benoit Tellier <[email protected]>
AuthorDate: Mon Jul 5 15:41:15 2021 +0700
JAMES-3609 Allow to inject custom BlobResolver
---
.../james/jmap/rfc8621/RFC8621MethodsModule.java | 9 ++++
.../jmap-rfc-8621-integration-tests-common/pom.xml | 4 ++
.../rfc8621/contract/CustomMethodContract.scala | 59 ++++++++++++++++++++--
.../apache/james/jmap/routes/DownloadRoutes.scala | 27 +++++-----
4 files changed, 83 insertions(+), 16 deletions(-)
diff --git
a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
index c82bdff..bce2e8e 100644
---
a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
+++
b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
@@ -64,9 +64,13 @@ import org.apache.james.jmap.method.ThreadGetMethod;
import org.apache.james.jmap.method.VacationResponseGetMethod;
import org.apache.james.jmap.method.VacationResponseSetMethod;
import org.apache.james.jmap.method.ZoneIdProvider;
+import org.apache.james.jmap.routes.AttachmentBlobResolver;
+import org.apache.james.jmap.routes.BlobResolver;
import org.apache.james.jmap.routes.DownloadRoutes;
import org.apache.james.jmap.routes.EventSourceRoutes;
import org.apache.james.jmap.routes.JMAPApiRoutes;
+import org.apache.james.jmap.routes.MessageBlobResolver;
+import org.apache.james.jmap.routes.MessagePartBlobResolver;
import org.apache.james.jmap.routes.SessionRoutes;
import org.apache.james.jmap.routes.UploadRoutes;
import org.apache.james.jmap.routes.WebSocketRoutes;
@@ -135,6 +139,11 @@ public class RFC8621MethodsModule extends AbstractModule {
typeNameMultibinder.addBinding().toInstance(EmailSubmissionTypeName$.MODULE$);
typeNameMultibinder.addBinding().toInstance(EmailDeliveryTypeName$.MODULE$);
typeNameMultibinder.addBinding().toInstance(VacationResponseTypeName$.MODULE$);
+
+ Multibinder<BlobResolver> blobResolverMultibinder =
Multibinder.newSetBinder(binder(), BlobResolver.class);
+ blobResolverMultibinder.addBinding().to(MessageBlobResolver.class);
+ blobResolverMultibinder.addBinding().to(MessagePartBlobResolver.class);
+ blobResolverMultibinder.addBinding().to(AttachmentBlobResolver.class);
}
@ProvidesIntoSet
diff --git
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
index 4463737..ae1eeb0 100644
---
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
+++
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
@@ -36,6 +36,10 @@
<dependencies>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>blob-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-common</artifactId>
</dependency>
<dependency>
diff --git
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
index b480150..a152b5e 100644
---
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
+++
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
@@ -19,12 +19,19 @@
package org.apache.james.jmap.rfc8621.contract
+import java.io.{ByteArrayInputStream, InputStream}
+import java.net.URI
+import java.nio.charset.StandardCharsets
+
import com.google.inject.AbstractModule
import com.google.inject.multibindings.Multibinder
import eu.timepit.refined.auto._
+import eu.timepit.refined.numeric.NonNegative
+import eu.timepit.refined.refineV
import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
import io.restassured.RestAssured._
import io.restassured.http.ContentType.JSON
+import javax.inject.{Inject, Named}
import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
import org.apache.http.HttpStatus.SC_OK
import org.apache.james.GuiceJamesServer
@@ -38,11 +45,17 @@ import
org.apache.james.jmap.core.ResponseObject.SESSION_STATE
import org.apache.james.jmap.core.{Capability, CapabilityProperties, State}
import org.apache.james.jmap.draft.JmapGuiceProbe
import org.apache.james.jmap.http.UserCredential
+import org.apache.james.jmap.mail
+import org.apache.james.jmap.mail.Email.Size
import org.apache.james.jmap.method.{InvocationWithContext, Method}
import org.apache.james.jmap.rfc8621.contract.CustomMethodContract.CUSTOM
+import org.apache.james.jmap.rfc8621.contract.DownloadContract.accountId
import org.apache.james.jmap.rfc8621.contract.Fixture._
+import org.apache.james.jmap.routes.{Applicable, Blob, BlobResolutionResult,
BlobResolver, NonApplicable}
import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.model.ContentType
import org.apache.james.utils.{DataProbeImpl, GuiceProbe}
+import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.{BeforeEach, Test}
import org.reactivestreams.Publisher
import play.api.libs.json.{JsObject, Json}
@@ -57,9 +70,7 @@ import sttp.monad.syntax.MonadErrorOps
import sttp.ws.WebSocketFrame
import sttp.ws.WebSocketFrame.Text
-import java.net.URI
-import javax.inject.{Inject, Named}
-import scala.util.Try
+import scala.util.{Success, Try}
object CustomMethodContract {
val CUSTOM: CapabilityIdentifier = "urn:apache:james:params:jmap:custom"
@@ -179,9 +190,33 @@ class CustomMethodModule extends AbstractModule {
Multibinder.newSetBinder(binder(), classOf[GuiceProbe])
.addBinding()
.to(classOf[JmapEventBusProbe])
+ Multibinder.newSetBinder(binder(), classOf[BlobResolver])
+ .addBinding()
+ .to(classOf[CustomBlobResolver])
}
}
+case object CustomBlob extends Blob {
+ private val payload: Array[Byte] = "zomeuh".getBytes(StandardCharsets.UTF_8)
+
+ override def blobId: mail.BlobId =
org.apache.james.jmap.mail.BlobId("gabouh")
+
+ override def contentType: ContentType = ContentType.of("application/bytes")
+
+ override def size: Try[Size] =
Success(refineV[NonNegative](payload.length.toLong).toOption.get)
+
+ override def content: InputStream = new ByteArrayInputStream(payload)
+}
+
+class CustomBlobResolver extends BlobResolver {
+ override def resolve(blobId: org.apache.james.jmap.mail.BlobId,
mailboxSession: MailboxSession): BlobResolutionResult =
+ if (blobId.equals(CustomBlob.blobId)) {
+ Applicable(SMono.just(CustomBlob))
+ } else {
+ NonApplicable
+ }
+}
+
class CustomMethod extends Method {
override val methodName = MethodName("Custom/echo")
@@ -463,6 +498,24 @@ trait CustomMethodContract {
|}""".stripMargin)
}
+ @Test
+ def shouldAcceptCustomBlobResolver(): Unit = {
+ val response = `given`
+ .basePath("")
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .when
+ .get(s"/download/$accountId/gabouh")
+ .`then`
+ .statusCode(SC_OK)
+ .contentType("application/bytes")
+ .extract
+ .body
+ .asString
+
+ assertThat(new
ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8)))
+ .hasContent("zomeuh")
+ }
+
private def authenticatedRequest(server: GuiceJamesServer):
RequestT[Identity, Either[String, String], Any] = {
val port = server.getProbe(classOf[JmapGuiceProbe])
.getJmapPort
diff --git
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
index 866d962..d1a90db 100644
---
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
+++
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
@@ -68,7 +68,7 @@ object DownloadRoutes {
sealed trait BlobResolutionResult {
def asOption: Option[SMono[Blob]]
}
-case class NonApplicable() extends BlobResolutionResult {
+case object NonApplicable extends BlobResolutionResult {
override def asOption: Option[SMono[Blob]] = None
}
case class Applicable(blob: SMono[Blob]) extends BlobResolutionResult {
@@ -127,7 +127,7 @@ class MessageBlobResolver @Inject()(val messageIdFactory:
MessageId.Factory,
val messageIdManager: MessageIdManager)
extends BlobResolver {
override def resolve(blobId: BlobId, mailboxSession: MailboxSession):
BlobResolutionResult = {
Try(messageIdFactory.fromString(blobId.value.value)) match {
- case Failure(_) => NonApplicable()
+ case Failure(_) => NonApplicable
case Success(messageId) => Applicable(SMono.fromPublisher(
messageIdManager.getMessagesReactive(List(messageId).asJava,
FetchGroup.FULL_CONTENT, mailboxSession))
.map[Blob](MessageBlob(blobId, _))
@@ -143,10 +143,9 @@ class AttachmentBlobResolver @Inject()(val
attachmentManager: AttachmentManager)
Try(attachmentManager.getAttachment(attachmentId, mailboxSession))
match {
case Success(attachmentMetadata) => Applicable(
SMono.fromCallable(() => AttachmentBlob(attachmentMetadata,
attachmentManager.load(attachmentMetadata, mailboxSession))))
- case Failure(_) =>
Applicable(SMono.error(BlobNotFoundException(blobId)))
+ case Failure(_) => NonApplicable
}
-
- case _ => NonApplicable()
+ case _ => NonApplicable
}
}
@@ -166,7 +165,7 @@ class MessagePartBlobResolver @Inject()(val
messageIdFactory: MessageId.Factory,
override def resolve(blobId: BlobId, mailboxSession: MailboxSession):
BlobResolutionResult = {
asMessageAndPartId(blobId) match {
- case Failure(_) => NonApplicable()
+ case Failure(_) => NonApplicable
case Success((messageId, partId)) =>
Applicable(SMono.fromPublisher(
messageIdManager.getMessagesReactive(List(messageId).asJava,
FetchGroup.FULL_CONTENT, mailboxSession))
@@ -186,14 +185,16 @@ class MessagePartBlobResolver @Inject()(val
messageIdFactory: MessageId.Factory,
}
}
-class BlobResolvers @Inject()(val messageBlobResolver: MessageBlobResolver,
- val messagePartBlobResolver:
MessagePartBlobResolver,
- val attachmentBlobResolver:
AttachmentBlobResolver) {
+class BlobResolvers(blobResolvers: Set[BlobResolver]) {
+
+ @Inject
+ def this(blobResolvers: java.util.Set[BlobResolver]) {
+ this(blobResolvers.asScala.toSet)
+ }
+
def resolve(blobId: BlobId, mailboxSession: MailboxSession): SMono[Blob] =
- messageBlobResolver
- .resolve(blobId, mailboxSession).asOption
- .orElse(messagePartBlobResolver.resolve(blobId, mailboxSession).asOption)
- .orElse(attachmentBlobResolver.resolve(blobId, mailboxSession).asOption)
+ blobResolvers.flatMap(resolver => resolver.resolve(blobId,
mailboxSession).asOption)
+ .headOption
.getOrElse(SMono.error(BlobNotFoundException(blobId)))
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]