Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding play 3.0 support. Fixes #564 #565

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 74 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ jobs:
- store_test_results:
path: test-reports

jdk11_scala213_play29:
docker:
- image: cchantep/circleci-openjdk:11-jdk

working_directory: ~/repo

environment:
RELEASE_SUFFIX: play29
PLAY_VERSION: 2.9.1
SCALA_VERSION: 2.13.11

steps:
- checkout
- setup_sbt
- setup_integration
- build_n_tests
- collect_test_reports
- store_test_results:
path: test-reports

jdk11_scala32_play29:
docker:
- image: cchantep/circleci-openjdk:11-jdk
Expand All @@ -204,7 +224,7 @@ jobs:
environment:
RELEASE_SUFFIX: play29
PLAY_VERSION: 2.9.1
SCALA_VERSION: 3.3.3
SCALA_VERSION: 3.2.2

steps:
- checkout
Expand All @@ -215,6 +235,46 @@ jobs:
- store_test_results:
path: test-reports

jdk11_scala213_play30:
docker:
- image: cchantep/circleci-openjdk:11-jdk

working_directory: ~/repo

environment:
RELEASE_SUFFIX: play30
PLAY_VERSION: 3.0.2
SCALA_VERSION: 2.13.11

steps:
- checkout
- setup_sbt
- setup_integration
- build_n_tests
- collect_test_reports
- store_test_results:
path: test-reports

jdk11_scala32_play30:
docker:
- image: cchantep/circleci-openjdk:11-jdk

working_directory: ~/repo

environment:
RELEASE_SUFFIX: play30
PLAY_VERSION: 3.0.2
SCALA_VERSION: 3.2.2

steps:
- checkout
- setup_sbt
- setup_integration
- build_n_tests
- collect_test_reports
- store_test_results:
path: test-reports

publish_snapshots:
docker:
- image: cchantep/circleci-openjdk:8-jdk
Expand Down Expand Up @@ -247,12 +307,17 @@ jobs:
- publish_snapshots:
release_suffix: play28
play_version: 2.8.1
scala_versions: 2.12.17 2.13.11 3.3.3
scala_versions: 2.12.17 2.13.11 3.2.2

- publish_snapshots:
release_suffix: play29
play_version: 2.9.1
scala_versions: 2.13.11 3.3.3
scala_versions: 2.13.11 3.2.2

- publish_snapshots:
release_suffix: play30
play_version: 3.0.2
scala_versions: 2.13.11 3.2.2

- save_cache:
paths:
Expand Down Expand Up @@ -280,7 +345,10 @@ workflows:
- jdk8_scala212_play26
- jdk10_scala213_play27
- jdk11_scala213_play28
- jdk11_scala213_play29
- jdk11_scala32_play29
- jdk11_scala213_play30
- jdk11_scala32_play30

- publish_snapshots:
filters:
Expand All @@ -292,6 +360,9 @@ workflows:
- jdk10_scala213_play27
- jdk11_scala213_play28
- jdk11_scala32_play29
- jdk11_scala213_play29
- jdk11_scala32_play30
- jdk11_scala213_play30

- trigger_dependent_builds:
filters:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ReactiveMongo for Play Framework

This is a module for the [Play Framework](https://www.playframework.com) 2.5, 2.6 and 2.7, enabling support for [ReactiveMongo](http://reactivemongo.org) – a reactive, asynchronous and non-blocking Scala driver for MongoDB.
This is a module for the [Play Framework](https://www.playframework.com) 2.5, 2.6, 2.7, 2.8, 2.9 and 3.0, enabling support for [ReactiveMongo](http://reactivemongo.org) – a reactive, asynchronous and non-blocking Scala driver for MongoDB.

[![Maven](https://img.shields.io/maven-central/v/org.reactivemongo/play2-reactivemongo_2.12.svg)](http://search.maven.org/#search%7Cga%7C1%7Cplay2-reactivemongo) [![Javadocs](https://javadoc.io/badge/org.reactivemongo/play2-reactivemongo_2.12.svg)](https://javadoc.io/doc/org.reactivemongo/play2-reactivemongo_2.12)

Expand All @@ -15,9 +15,9 @@ The documentation is available online.

## Build manually

ReactiveMongo for Play 2 can be built from this source repository.
ReactiveMongo for Play can be built from this source repository.

sbt publish-local
sbt publishLocal

To run the tests, use:

Expand Down
29 changes: 23 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,17 @@ val playDependencies = Def.setting[Seq[ModuleID]] {
}
}

val groupId = {
if (ver startsWith "2.") {
"com.typesafe.play"
} else {
"org.playframework"
}
}

val baseDeps = base.map {
case (name, scope) =>
("com.typesafe.play" %% name % ver % scope) cross x
(groupId %% name % ver % scope) cross x
}

baseDeps
Expand Down Expand Up @@ -64,6 +72,7 @@ lazy val reactivemongo = Project("Play2-ReactiveMongo", file(".")).settings(
val dep = ("org.reactivemongo" %% "reactivemongo" % dv)
.cross(CrossVersion.binary)
.exclude("com.typesafe.akka", "*")
.exclude("org.apache.pekko", "*")
. // provided by Play
exclude("com.typesafe.play", "*")

Expand Down Expand Up @@ -97,14 +106,22 @@ lazy val reactivemongo = Project("Play2-ReactiveMongo", file(".")).settings(
.cross(CrossVersion.binary)
.exclude("org.reactivemongo", "*") // Avoid mixing shaded w/ nonshaded

val akkaStream =
("org.reactivemongo" %% "reactivemongo-akkastream" % buildVer)
.cross(CrossVersion.binary)
.exclude("com.typesafe.akka", "*") // provided by Play
val reactiveMongoStreaming = {
if (Common.playVer.value startsWith "2.") {
("org.reactivemongo" %% "reactivemongo-akkastream" % buildVer)
.cross(CrossVersion.binary)
.exclude("com.typesafe.akka", "*") // provided by Play
} else {
("org.reactivemongo" %% "reactivemongo-pekkostream" % buildVer)
.cross(CrossVersion.binary)
.exclude("org.apache.pekko", "*") // provided by Play
Copy link
Member

@cchantep cchantep Apr 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you specify your comment ?

}
}


driverDeps ++ Seq(
jsonCompat,
akkaStream,
reactiveMongoStreaming,
"junit" % "junit" % "4.13.2" % Test,
"ch.qos.logback" % "logback-classic" % "1.2.13" % Test
) ++ additionalDeps ++ playDependencies.value ++ specs2Dependencies ++ silencer
Expand Down
11 changes: 7 additions & 4 deletions project/Common.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ object Common extends AutoPlugin {
driverVersion := {
val ver = (ThisBuild / version).value
val suffix = {
if (useShaded.value) "" // default ~> no suffix
else "noshaded"
(useShaded.value, playVer.value startsWith "2.") match {
case (false, false) => "noshaded.pekko"
case (true, false) => "pekko"
case (false, true) => "noshaded"
case (true, true) => ""
}
}

if (suffix.isEmpty) {
ver
} else {
Expand Down Expand Up @@ -51,7 +54,7 @@ object Common extends AutoPlugin {
crossScalaVersions := Seq(
"2.11.12",
scalaVersion.value,
"2.13.10",
"2.13.11",
"3.2.2"
),
crossVersion := CrossVersion.binary,
Expand Down
6 changes: 5 additions & 1 deletion project/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -e

S2_12="2.12.17"
S2_11="2.11.12"
S2_13="2.13.10"
S2_13="2.13.11"
S3_2="3.2.2"

unset REACTIVEMONGO_SHADED
Expand All @@ -28,3 +28,7 @@ sbt $SBT_OPTS ++${S2_12} makePom packageBin packageSrc packageDoc \
export RELEASE_SUFFIX=play29 PLAY_VERSION=2.9.1
sbt $SBT_OPTS ++${S2_13} makePom packageBin packageSrc packageDoc \
++${S3_2} makePom packageBin packageSrc packageDoc

export RELEASE_SUFFIX=play30 PLAY_VERSION=3.0.2
sbt $SBT_OPTS ++${S2_13} makePom packageBin packageSrc packageDoc \
++${S3_2} makePom packageBin packageSrc packageDoc
125 changes: 125 additions & 0 deletions src/main/play-2.6/modules/reactivemongo/MongoController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2012-2013 Stephane Godbillon (@sgodbillon)
*
* Licensed 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 play.modules.reactivemongo

import scala.concurrent.{ ExecutionContext, Future }

import play.api.http.{ HttpChunk, HttpEntity }
import play.api.mvc.{ BodyParser, MultipartFormData, ResponseHeader, Result }

import reactivemongo.api.bson.{ BSONDocument, BSONValue }
import reactivemongo.api.bson.collection.BSONSerializationPack

import akka.stream.Materializer

object MongoController {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was no need for that, how is it related with support for Play 3?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MongoController Trait references akka dependencies in the signature of functions and needed to be changed to pekko.
I though that the best way of changing from akka to pekko was to split the trait into a version specific file, resulting in the duplication of the file for every version under play 3.0.

type GridFS = reactivemongo.api.gridfs.GridFS[BSONSerializationPack.type]

type GridFSBodyParser[T <: BSONValue] = BodyParser[
MultipartFormData[reactivemongo.api.gridfs.ReadFile[T, BSONDocument]]
]

type FileToSave[T <: BSONValue] =
reactivemongo.api.gridfs.FileToSave[T, BSONDocument]

/** `Content-Disposition: attachment` */
private[reactivemongo] val CONTENT_DISPOSITION_ATTACHMENT = "attachment"

/** `Content-Disposition: inline` */
private[reactivemongo] val CONTENT_DISPOSITION_INLINE = "inline"

}

/** A mixin for controllers that will provide MongoDB actions. */
trait MongoController extends PlaySupport.Controller {
self: ReactiveMongoComponents =>

import reactivemongo.api.Cursor
import reactivemongo.akkastream.GridFSStreams
import MongoController._

/**
* Returns the current MongoConnection instance
* (the connection pool manager).
*/
protected final def connection = reactiveMongoApi.connection

/** Returns the default database (as specified in `application.conf`). */
protected final def database = reactiveMongoApi.database

/**
* Returns a future Result that serves the first matched file,
* or a `NotFound` result.
*/
protected final def serve[Id <: BSONValue](
gfs: GridFS
)(foundFile: Cursor[gfs.ReadFile[Id]],
dispositionMode: String = CONTENT_DISPOSITION_ATTACHMENT
)(implicit
materializer: Materializer
): Future[Result] = {
implicit def ec: ExecutionContext = materializer.executionContext

foundFile.headOption.collect { case Some(file) => file }.map { file =>
def filename = file.filename.getOrElse("file.bin")
def contentType = file.contentType.getOrElse("application/octet-stream")

def chunks = GridFSStreams(gfs).source(file).map(HttpChunk.Chunk(_))

Result(
header = ResponseHeader(OK),
body = HttpEntity.Chunked(chunks, Some(contentType))
).as(contentType)
.withHeaders(
CONTENT_LENGTH -> file.length.toString,
CONTENT_DISPOSITION -> (s"""$dispositionMode; filename="$filename"; filename*="UTF-8''""" + java.net.URLEncoder
.encode(filename, "UTF-8")
.replace("+", "%20") + '"')
)

}.recover { case _ => NotFound }
}

protected final def gridFSBodyParser(
gfs: Future[GridFS]
)(implicit
materializer: Materializer
): GridFSBodyParser[BSONValue] = {
implicit def ec: ExecutionContext = materializer.executionContext
import play.api.libs.streams.Accumulator

parse.multipartFormData { (in: Any) =>
in match {
case PlaySupport.FileInfo(partName, filename, contentType) =>
Accumulator.flatten(gfs.map { gridFS =>
val fileRef = gridFS.fileToSave( // see Api.scala
filename = Some(filename),
contentType = contentType
)

val sink = GridFSStreams(gridFS).sinkWithMD5(fileRef)

Accumulator(sink).map { ref =>
MultipartFormData.FilePart(partName, filename, contentType, ref)
}
})

case info =>
sys.error(s"Unsupported: $info")
}
}
}
}