Skip to content

Commit

Permalink
Merge pull request #20 from DeepnessScience/EXTENSIONS-SPRAY-AND-PLAY…
Browse files Browse the repository at this point in the history
…-JSON

Extensions for Spray and Play JSON
  • Loading branch information
lashchenko committed Oct 6, 2023
2 parents 6105ebd + f08f380 commit 6a4e1f9
Show file tree
Hide file tree
Showing 83 changed files with 3,365 additions and 2,835 deletions.
96 changes: 96 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# https://scalameta.org/scalafmt/docs/installation.html#sbt
version = 3.7.14

# https://scalameta.org/scalafmt/docs/configuration.html#scala-dialects
runner.dialect = scala3

# https://docs.scala-lang.org/style/declarations.html#modifiers
rewrite.rules = [SortModifiers]
rewrite.sortModifiers.order = [
"`final`"
"`implicit`"
"`override`"
"`protected`"
"`private`"
"`lazy`"
]

//rewrite.rules = [RedundantBraces]
//rewrite.redundantBraces.stringInterpolation = true
//rewrite.redundantBraces.defnBodies = "all"
//rewrite.redundantBraces.methodBodies = true
//rewrite.redundantBraces.includeUnitMethods = true
//rewrite.redundantBraces.generalExpressions = true
//rewrite.redundantBraces.ifElseExpressions = true
//newlines.afterCurlyLambdaParams=squash

# https://scalameta.org/scalafmt/docs/configuration.html#imports
rewrite.rules = [Imports]
//rewrite.imports.sort = scalastyle
rewrite.imports.sort = ascii
rewrite.imports.expand = true
rewrite.imports.groups = [
["io.github.greenleafoss\\..*"],
["org.mongodb\\..*"],
["org.bson\\..*"],
["java\\..*"],
["scala\\..*"]
]

# https://scalameta.org/scalafmt/docs/configuration.html#maxcolumn
# 80| 90| 100| 110| 120|
maxColumn = 120

# https://scalameta.org/scalafmt/docs/configuration.html#top-level-presets
preset = IntelliJ

# https://scalameta.org/scalafmt/docs/configuration.html#alignpreset
align.preset=most
align.multiline = true
align.arrowEnumeratorGenerator = false
align.openParenCallSite = false
align.openParenDefnSite = true

optIn.configStyleArguments = true

verticalMultiline.atDefnSite = true
# https://scalameta.org/scalafmt/docs/configuration.html#after-only
# implicit
# override private val ctx: Context,
# private val ops: Ops
newlines.implicitParamListModifierForce = [after]
//newlines.implicitParamListModifierForce = [before, after]

# https://scalameta.org/scalafmt/docs/configuration.html#indentextendsite
indent.extendSite = 2

# https://scalameta.org/scalafmt/docs/configuration.html#indentdefnsite
indent.defnSite = 4

indent.fewerBraces = always



# https://scalameta.org/scalafmt/docs/configuration.html#trailing-commas
rewrite.trailingCommas.style = never

# https://scalameta.org/scalafmt/docs/configuration.html#binpacking
# doesn't apply binpacking to calls with fewer arguments
binPack.literalsMinArgCount = 3

# https://scalameta.org/scalafmt/docs/configuration.html#binpackparentconstructors
binPack.parentConstructors = Never

//newlines.beforeTemplateBodyIfBreakInParentCtors = true

# https://scalameta.org/scalafmt/docs/configuration.html#literal-argument-lists
# List(
# 1,
# 2,
# 3,
# )
binPack.literalArgumentLists = false
binPack.literalsSingleLine = false

docstrings.style = Asterisk
docstrings.style = keep
55 changes: 33 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,54 @@
[![green-leaf-mongo Scala version support](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo/latest-by-scala-version.svg)](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo)

## Short description
This extension created on top of official [MongoDB Scala Driver](http://mongodb.github.io/mongo-scala-driver), allows to fully utilize [Spray JSON](https://github.com/spray/spray-json) and represents bidirectional serialization for case classes in BSON, as well as flexible DSL for [MongoDB query operators](https://docs.mongodb.com/manual/reference/operator/query/), documents and collections.
This extension created on top of official [MongoDB Scala Driver](https://mongodb.github.io/mongo-scala-driver) and allows to fully utilize [Spray JSON](https://github.com/spray/spray-json) or [Play JSON](https://github.com/playframework/play-json) to represent bidirectional serialization for case classes in BSON, as well as flexible DSL for [MongoDB query operators](https://www.mongodb.com/docs/manual/reference/operator/query/), documents and collections.

## Usage
```scala
// build.sbt
// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo" % "0.1.16.1"

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-core
// `green-leaf-mongo-core` can be used if you want to create your own extension

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-spray
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo-spray" % "3.0"

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-play
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo-play" % "3.0"
```

## JSON and BSON protocols

`GreenLeafJsonProtocol` based on DefaultJsonProtocol from Spray JSON and allows to override predefined JsonFormats to make possible use custom seriallization in BSON format.
This trait also includes a few additional JsonFormats for _ZonedDateTime_, _ObjectId_, _scala Enumeration_ and _UUID_.
`GreenLeafMongoJsonBasicFormats` based on DefaultJsonProtocol from Spray JSON and allows to override predefined JsonFormats to make possible use custom serialization in BSON format.
This trait also includes a few additional JsonFormats for _LocalDate_, _LocalDateTime_, _ZonedDateTime_, _ObjectId_, _scala Enumeration_ and _UUID_.

`GreenLeafBsonProtocol` extends `GreenLeafJsonProtocol` and overrides _Long_, _ZonedDateTime_, _ObjectId_, _scala Enumeration_, _UUID_ and _Regex_ JSON formats to represent them in related BSON formats.
`PlayJsonProtocol` is a related extension for Play JSON library and `PlayJsonProtocol` for Spray JSON library.

`SprayBsonProtocol`/`PlayBsonProtocol` extends related JsonProtocols and overrides _Int_, _Long_, _BigDecimal_, _LocalDate_, _LocalDateTime_, _ZonedDateTime_, _ObjectId_, _scala Enumeration_, _UUID_ and _Regex_ JSON formats to represent them in related BSON (MongoDB Extended JSON V2) formats https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/#mongodb-extended-json-v2-usage.

These base protocols allow to simply (de)serialize this instance to and from both JSON and BSON the same way as in Spray JSON:
```scala
```scala 3
// MODEL
case class Test(id: ObjectId, i: Int, l: Long, b: Boolean, zdt: ZonedDateTime)

// JSON
trait TestJsonProtocol extends GreenLeafJsonProtocol {
implicit def testJf = jsonFormat5(Test)
}
trait TestJsonProtocol extends SprayBsonProtocol:
given testJsonFormat = jsonFormat5(Test)

object TestJsonProtocol extends TestJsonProtocol

// BSON
trait TestBsonProtocol extends TestJsonProtocol with GreenLeafBsonProtocol {
override implicit def testJf = jsonFormat(Test, "_id", "i", "l", "b", "zdt")
}
trait TestBsonProtocol extends SprayBsonProtocol:
given testBsonFormat = jsonFormat(Test, "_id", "i", "l", "b", "zdt")

object TestBsonProtocol extends TestBsonProtocol
```

Once protocols defined, we can make instance of Test case class and use TestJsonProtocol to print related JSON:
```scala
val obj = Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01")

import TestJsonProtocol._
import TestJsonProtocol.given
println(obj.toJson.prettyPrint)
```
Output in this case will be:
Expand All @@ -62,7 +71,7 @@ Changing single line of import `TestJsonProtocol` to `TestBsonProtocol` allows u
```scala
val obj = Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01")

import TestBsonProtocol._
import TestBsonProtocol.given
println(obj.toJson.prettyPrint)
```

Expand All @@ -83,10 +92,10 @@ Output in this case will be:
}
```

Full code of the examples above available in `GreenLeafJsonAndBsonProtocolsTest`.
More examples available in implementation of **JsonProtocolSpec**/**BsonProtocolSpec** in Spray and Play project modules.

## GreenLeafMongoDsl
Import `GreenLeafMongoDsl._` makes it possible to write queries with a syntax that is more close to real queries in MongoDB, as was implemented in [Casbah Query DSL](http://mongodb.github.io/casbah/3.1/reference/query_dsl/).
`GreenLeafMongoFilterOps` makes it possible to write queries with a syntax that is more close to real queries in MongoDB, as was implemented in [Casbah Query DSL](http://mongodb.github.io/casbah/3.1/reference/query_dsl/).

```scala
"size" $all ("S", "M", "L")
Expand All @@ -100,17 +109,19 @@ Import `GreenLeafMongoDsl._` makes it possible to write queries with a syntax th
"size" $nin ("S", "XXL")
$or( "price" $lt 5, "price" $gt 1, "promotion" $eq true )
$and( "price" $lt 5, "price" $gt 1, "stock" $gte 1 )
"price" $not { _ $gte 5.1 }
"price" $not { $gte (5.1) }
$nor( "price" $eq 1.99 , "qty" $lt 20, "sale" $eq true )
"qty" $exists true
"results" $elemMatch $and("product" $eq "xyz", "score" $gte 8)
// ...
```

More examples of queries available in `GreenLeafMongoDslTest`.
More examples of queries available in **GreenLeafMongoFilterOpsSpec**.


## GreenLeafMongoDao
`GreenLeafMongoDao` extends `GreenLeafMongoDsl` and provides simple DSL to transform Mongo's _Observable[Document]_ instances to _Future[Seq[T]]_, _Future[Option[T]]_ and _Future[T]_.
In addition this trait provides many useful generic methods such as _insert_, _getById_, _findById_, _updateById_, _replaceById_ and others.
You can find more details and examples in [EntityWithIdAsFieldDaoTest](https://github.com/GreenLeafOSS/green-leaf-mongo/blob/master/src/test/scala/io/github/greenleafoss/mongo/EntityWithIdAsFieldDaoTest.scala), [EntityWithIdAsObjectDaoTest](https://github.com/GreenLeafOSS/green-leaf-mongo/blob/master/src/test/scala/io/github/greenleafoss/mongo/EntityWithIdAsObjectDaoTest.scala), [EntityWithOptionalFieldsDaoTest](https://github.com/GreenLeafOSS/green-leaf-mongo/blob/master/src/test/scala/io/github/greenleafoss/mongo/EntityWithOptionalFieldsDaoTest.scala) and [EntityWithoutIdDaoTest](https://github.com/GreenLeafOSS/green-leaf-mongo/blob/master/src/test/scala/io/github/greenleafoss/mongo/EntityWithoutIdDaoTest.scala).
`GreenLeafMongoDao` extends `GreenLeafMongoObservableToFutureOps` with `GreenLeafMongoFilterOps` and provides simple DSL to transform Mongo's _Observable[Document]_ instances to _Future[Seq[T]]_, _Future[Option[T]]_ and _Future[T]_.
In addition, this trait provides many useful generic methods such as _insert_, _getById_, _findById_, _updateById_, _replaceById_ and others.
`SprayMongoDao`/`PlayMongoDao` are related implementations for Spray and Play JSON libraries.
You can find more details and examples in the dao tests.

147 changes: 87 additions & 60 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,64 +1,91 @@
// **************************************************
// SETTINGS
// **************************************************

lazy val commonSettings = Seq(
version := "3.0",
description :=
"""
|This extension created on top of official MongoDB Scala Driver.
|It allows to fully utilize Spray JSON and represents bidirectional serialization for case classes in BSON,
|as well as flexible DSL for MongoDB query operators, documents and collections.
|""".stripMargin,
licenses := List("Apache 2" -> new URL("https://www.apache.org/licenses/LICENSE-2.0.txt")),
homepage := Some(url("https://github.com/GreenLeafOSS/green-leaf-mongo")),
organization := "io.github.greenleafoss",
organizationName := "greenleafoss",
organizationHomepage := Some(url("https://github.com/greenleafoss")),
developers := List(
Developer(
id = "lashchenko",
name = "Andrii Lashchenko",
email = "andrew.lashchenko@gmail.com",
url = url("https://github.com/lashchenko")
)
),
scmInfo := Some(
ScmInfo(
url("https://github.com/GreenLeafOSS/green-leaf-mongo"),
"scm:git@github.com:GreenLeafOSS/green-leaf-mongo.git"
)
),
publishTo := {
// https://central.sonatype.org/news/20210223_new-users-on-s01/
val nexus = "https://s01.oss.sonatype.org"
if (isSnapshot.value) Some("snapshots" at nexus + "/content/repositories/snapshots")
else Some("releases" at nexus + "/service/local/staging/deploy/maven2")
},

name := "green-leaf-mongo"

version := "0.1.16.1"

description := "This extension created on top of official MongoDB Scala Driver, allows to fully utilize Spray JSON and represents bidirectional serialization for case classes in BSON, as well as flexible DSL for MongoDB query operators, documents and collections."
licenses := List("Apache 2" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt"))
homepage := Some(url("https://github.com/GreenLeafOSS/green-leaf-mongo"))

organization := "io.github.greenleafoss"
organizationName := "greenleafoss"
organizationHomepage := Some(url("https://github.com/greenleafoss"))

developers := List(
Developer(
id = "lashchenko",
name = "Andrii Lashchenko",
email = "andrew.lashchenko@gmail.com",
url = url("https://github.com/lashchenko")
)
)

scmInfo := Some(
ScmInfo(
url("https://github.com/GreenLeafOSS/green-leaf-mongo"),
"scm:git@github.com:GreenLeafOSS/green-leaf-mongo.git"
)
)

publishTo := {
// https://central.sonatype.org/news/20210223_new-users-on-s01/
val nexus = "https://s01.oss.sonatype.org"
if (isSnapshot.value) Some("snapshots" at nexus + "/content/repositories/snapshots")
else Some("releases" at nexus + "/service/local/staging/deploy/maven2")
}

// https://central.sonatype.org/news/20210223_new-users-on-s01/
sonatypeCredentialHost := "https://s01.oss.sonatype.org"

publishMavenStyle := true

publishConfiguration := publishConfiguration.value.withOverwrite(true)

scalaVersion := "3.3.0"

crossScalaVersions := Seq("2.12.18", "2.13.11")

scalacOptions ++= Seq(
"-deprecation"
sonatypeCredentialHost := "https://s01.oss.sonatype.org",
publishMavenStyle := true,
publishConfiguration := publishConfiguration.value.withOverwrite(true),
publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true),
scalaVersion := "3.3.1",
scalacOptions ++= Seq(
"-deprecation",
"-feature",
"-explain"
),
scalafmtOnCompile := true,
Test / parallelExecution := false,
Test / fork := true,
libraryDependencies += "org.slf4j" % "slf4j-api" % "2.0.7",
libraryDependencies += "org.slf4j" % "slf4j-simple" % "2.0.7" % Test,
libraryDependencies += "de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % "3.5.4" % Test,
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % Test,
libraryDependencies += "org.immutables" % "value" % "2.9.2" % Test
)

Test / parallelExecution := false
Test / fork := true

libraryDependencies += "io.spray" %% "spray-json" % "1.3.6"
libraryDependencies += "org.mongodb.scala" %% "mongo-scala-driver" % "4.10.2" cross CrossVersion.for3Use2_13


libraryDependencies += "org.slf4j" % "slf4j-api" % "2.0.7"
libraryDependencies += "org.slf4j" % "slf4j-simple" % "2.0.7" % Test

libraryDependencies += "de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % "4.8.0" % Test
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.16" % Test
libraryDependencies += "org.immutables" % "value" % "2.9.3" % Test
// **************************************************
// PROJECTS
// **************************************************

lazy val core = (project in file("core"))
.settings(name := "green-leaf-mongo-core")
.settings(commonSettings)
.settings(libraryDependencies += "org.mongodb.scala" %% "mongo-scala-driver" % "4.9.0" cross CrossVersion.for3Use2_13)

lazy val spray = (project in file("spray"))
.settings(name := "green-leaf-mongo-spray")
.settings(commonSettings)
.settings(libraryDependencies += "io.spray" %% "spray-json" % "1.3.6")
.dependsOn(core % "compile->compile;test->test")

lazy val play = (project in file("play"))
.settings(name := "green-leaf-mongo-play")
.settings(commonSettings)
.settings(libraryDependencies += "com.typesafe.play" %% "play-json" % "2.10.1")
.dependsOn(core % "compile->compile;test->test")

lazy val extensions: Seq[ProjectReference] = List[ProjectReference](spray, play)
lazy val aggregated: Seq[ProjectReference] = List[ProjectReference](core) ++ extensions

lazy val root = (project in file("."))
.settings(name := "green-leaf-mongo")
.settings(commonSettings)
// we don't need to publish core + spray + play together
.settings(publish / skip := true)
.aggregate(aggregated*)
.dependsOn(core % "compile->compile;test->test")
.dependsOn(extensions.map(_ % "compile->compile;compile->test")*)
Loading

0 comments on commit 6a4e1f9

Please sign in to comment.