I'm doing tests with reactivemongo
In my controller I have this:
package controllers
import models._
import models.JsonFormats._
import play.modules.reactivemongo.MongoController
import scala.concurrent.Future
import reactivemongo.api.Cursor
import org.slf4j.{LoggerFactory, Logger}
import javax.inject.Singleton
import play.api.mvc._
import reactivemongo.api.collections.default.BSONCollection
import reactivemongo.bson._
#Singleton
class Users extends Controller with MongoController {
private final val logger: Logger = LoggerFactory.getLogger(classOf[Users])
val collection = db[BSONCollection]("users")
// list all articles and sort them
def list = Action.async { implicit request =>
// get a sort document (see getSort method for more information)
val sort = getSort(request)
// build a selection document with an empty query and a sort subdocument ('$orderby')
val query = BSONDocument(
"$orderby" -> sort,
"$query" -> BSONDocument())
val activeSort = request.queryString.get("sort").flatMap(_.headOption).getOrElse("none")
// the cursor of documents
val found = collection.find(query).cursor[User]
// build (asynchronously) a list containing all the articles
found.collect[List]().map { users =>
Ok(views.html.admin.list(users, activeSort))
}.recover {
case e =>
e.printStackTrace()
BadRequest(e.getMessage())
}
}
...........
}
and in my model i have this:
package models
import reactivemongo.bson._
case class User(
nickName: String,
email: String,
password: String,
active: Boolean
)
object JsonFormats {
import play.api.libs.json.Json
// Generates Writes and Reads for Feed and User thanks to Json Macros
implicit val userFormat = Json.format[User]
}
When I compile the project returns the following error:
could not find implicit value for parameter reader: reactivemongo.bson.BSONDocumentReader[models.User]
in this line is the problem:
val found = collection.find(query).cursor[User]
Can anyone tell me where I'm wrong or what I'm missing please?
You have no implicit handler defined to map your model class to a BSONDocument. You can implement it yourself, or, just like you did for the JsonFormats, you could use the macros provided by ReactiveMongo.
object BsonFormats {
import reactivemongo.bson.Macros
implicit val userFormat = Macros.handler[User]
}
Alternatively, instead of the BSONCollection, you could use the JSONCollection provided by Play-ReactiveMongo to perform your mapping using the JSON format that you have already defined.
For me, I still get the error even after I have declared the implicits for both bson and json format. What I need to do is just import this:
import reactivemongo.api.commands.bson.BSONCountCommandImplicits._
Related
I need to get json from https://api.opendota.com/api/leagues and rebuild it keeping only leagues name and leagues id, save to local file and send this json as a response to client by Get method
package dev.zio.quickstart.dota
import zhttp.http.\*
import zio.\*
import zio.json.\*
import zio.\_
import zio.http.Client
An http app that:
Accepts a `Request` and returns a `Response`
May fail with type of `Throwable`
Uses a `DotaRepo` as the environment
object DotaApp:
def apply(): Http\[DotaRepo, Throwable, Request, Response\] =
Http.collectZIO\[Request\] {
// GET /Leagues
case Method.GET -\> !! / "Leagues" =\>
val url = "https://api.opendota.com/api/leagues"
val program = for {
res \<- Client.request(url)
data \<- res.body.asString
} yield data.map(response =\> Response.json(response.toJson))
}
package dev.zio.quickstart.dota
import java.util.UUID
import zio.json.\*
case class League(name: String, leagueid: Int)
object League:
given JsonEncoder\[League\] =
DeriveJsonEncoder.gen\[League\]
given JsonDecoder\[League\] =
DeriveJsonDecoder.gen\[League\]\`
package dev.zio.quickstart.dota
import zio.\*
trait DotaRepo:
def leagues: Task\[List\[League\]\]
def liveMatches: Task\[List\[LiveMatch\]\]
object DotaRepo:
def leagues: ZIO\[DotaRepo, Throwable, List\[League\]\] =
ZIO.serviceWithZIO\[DotaRepo\](\_.leagues)
def liveMatches: ZIO[DotaRepo, Throwable, List[LiveMatch]] =
ZIO.serviceWithZIO[DotaRepo](_.liveMatches)
package dev.zio.quickstart.dota
import zio.\*
import scala.collection.mutable
case class InmemoryLeague(listBuffer: Ref\[mutable.ListBuffer\[League\]\]) extends DotaRepo:
def getLeagues: UIO\[List\[League\]\] =
listBuffer.get.map(\_.toList)
object InmemoryLeague {
def layer: ZLayer\[Any, Nothing, InmemoryLeague\] =
ZLayer.fromZIO(
Ref.make(mutable.ListBuffer.empty\[League\]).map(new InmemoryLeague(\_))
)
}
I've tried using these examples https://github.com/zio/zio-http/tree/main/zio-http-example/src/main/scala/example
Can't get in to syntax
I have the following:
Account.scala
package modules.accounts
import java.time.Instant
import reactivemongo.api.bson._
case class Account(id: String, name: String)
object Account {
type ID = String
implicit val accountHandler: BSONDocumentHandler[Account] = Macros.handler[Account]
// implicit def accountWriter: BSONDocumentWriter[Account] = Macros.writer[Account]
// implicit def accountReader: BSONDocumentReader[Account] = Macros.reader[Account]
}
AccountRepo.scala
package modules.accounts
import java.time.Instant
import reactivemongo.api.collections.bson.BSONCollection
import scala.concurrent.ExecutionContext
final class AccountRepo(
val coll: BSONCollection
)(implicit ec: ExecutionContext) {
import Account.{ accountHandler, ID }
def insertTest() = {
val doc = Account(s"account123", "accountName") //, Instant.now)
coll.insert.one(doc)
}
}
The error I am getting is:
could not find implicit value for parameter writer: AccountRepo.this.coll.pack.Writer[modules.accounts.Account]
[error] coll.insert.one(doc)
From what I understand the implicit handler that is generated by the macro should be enough and create the Writer. What am I doing wrong?
Reference: http://reactivemongo.org/releases/1.0/documentation/bson/typeclasses.html
The code is mismixing different versions.
The macro generated handler is using the new BSON API, as it can be seen with the import reactivemongo.api.bson, whereas the collection is using an old driver, as it can be seen as it uses reactivemongo.api.collections.bson instead of reactivemongo.api.bson.collection.
It's recommended to have a look at the documentation, and not mixing incompatible versions of related libraries.
I'm not really sure where this error is coming from. I'm trying to migrate from an old .find() method where I could pass Pattern: query: BSONDocument to find and call .one[T] on it. This worked perfectly fine, but I'd like to follow the recommendations.
However, I have a feeling that my desire to create a generic database access object is causing some of this headache... take a look below.
The object that i'm trying to read from:
package Models
import reactivemongo.api.bson.{BSONDocumentHandler, Macros}
case class Person(name: String)
object Person {
implicit val handler: BSONDocumentHandler[Person] = Macros.handler[Person]
}
The class containing the method I'm trying to execute (ignore the Await, etc.)
package Data
import reactivemongo.api.AsyncDriver
import reactivemongo.api.bson._
import reactivemongo.api.bson.collection.BSONCollection
import reactivemongo.api.commands.WriteResult
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.reflect.ClassTag
object DataService extends DataService
trait DataService {
implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global
private val driver = new AsyncDriver
private val connection = Await.result(driver.connect(List("localhost:32768")), 10.seconds)
private val database = Await.result(connection.database("database"), 10.seconds)
private def getCollection[T]()(implicit tag: ClassTag[T]): BSONCollection = database.collection(tag.runtimeClass.getSimpleName)
def Get[T]()(implicit handler: BSONDocumentHandler[T], tag: ClassTag[T]): Future[Option[T]] = {
val collection = getCollection[T]
collection.find(BSONDocument("name" -> "hello"), Option.empty).one[T]
}
}
How I'm trying to get it (ignore the Await, this is just for test purposes):
val personName = Await.result(dataService.Get[Person](), 10.seconds).map(_.name).getOrElse("Not found")
The error that I'm getting:
Error:(32, 19) ambiguous implicit values:
both object BSONDocumentIdentity in trait BSONIdentityHandlers of type reactivemongo.api.bson.package.BSONDocumentIdentity.type
and value handler of type reactivemongo.api.bson.BSONDocumentHandler[T]
match expected type collection.pack.Writer[J]
collection.find(BSONDocument("name" -> "hello"), Option.empty).cursor[T]
Thank you all for your help.
Try to add type to your option in your second argument find method, for example:
def Get[T]()(implicit handler: BSONDocumentHandler[T], tag: ClassTag[T]): Future[Option[T]] = {
val collection = getCollection[T]
collection.find(BSONDocument("name" -> "hello"), Option.empty[BSONDocument]).one[T]
}
I am playing around with Slick on top of a Twitter Finatra application. Finally I thought I made it but now, when I want to process a result, I always get an recursion error. I looked around but I did not find anything helpful for this particular problem. The code I have is actually quite simple:
Map the Database class to a custom Type:
package com.configurationManagement.library
package object Types {
type SlickDatabase = slick.driver.MySQLDriver.api.Database
}
Model:
package com.configurationManagement.app.domain
import slick.lifted.Tag
import slick.driver.MySQLDriver.api._
import slick.profile.SqlProfile.ColumnOption.NotNull
case class ConfigurationTemplate(id: Option[Int], name: String)
class ConfigurationTemplates(tag: Tag) extends Table[ConfigurationTemplate](tag: Tag, "configuration_template") {
def id = column[Int]("id", O.AutoInc, O.PrimaryKey)
def name = column[String]("name", NotNull)
def uniqueNameIndex = index("unique_name", name, unique = true)
def * = (id.?, name) <> (ConfigurationTemplate.tupled, ConfigurationTemplate.unapply)
}
Controller:
package com.configurationManagement.app.controller
import com.google.inject.{Inject, Singleton}
import com.configurationManagement.app.domain.ConfigurationTemplates
import com.configurationManagement.app.dto.request.RequestConfigurationTemplateDto
import com.configurationManagement.library.Types._
import com.twitter.finatra.http.Controller
import com.twitter.inject.Logging
import slick.driver.MySQLDriver.api._
#Singleton
class ConfigurationTemplateController #Inject()(database: SlickDatabase)
extends Controller with Logging with FutureConverter {
post("/configurations/templates") { dto: RequestConfigurationTemplateDto =>
val templates = TableQuery[ConfigurationTemplates]
val query = templates.filter(_.id === 1)
response.ok(query.map(_.name))
}
}
And here comes the error
Infinite recursion (StackOverflowError) (through reference chain: com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast
Obvisously this line causes the error:
query.map(_.name)
Two things I see, first you need to add .result to the query to transform it into a FixedSqlStreamingAction, second you need a database to run that query on:
private[this] val database: slick.driver.MySQLDriver.backend.DatabaseDef = Database.forDataSource(...)
database.run(templates.filter(_.id === 1).map(_.name).result)
Which returns a Future[Seq[String]], this probably is the expected type from response.ok.
I have the following route setup, but when my map is returned in the first complete block I get an error:
could not find implicit value for evidence parameter of type spray.httpx.marshalling.Marshaller[scala.collection.immutable.Map[String,String]]
import spray.routing.HttpService
import akka.actor.Actor
import spray.http.HttpRequest
import spray.routing.RequestContext
import spray.json.DefaultJsonProtocol._
class UserServiceActor extends Actor with RestUserService {
def actorRefFactory = context
def receive = runRoute(linkRoute)
}
trait RestUserService extends HttpService {
val userService = new LinkUserService
def linkRoute =
pathPrefix("user" / Segment) {
userId =>
path("link") {
parameters('service ! "YT") {
complete {
Map("status"-> "OK", "auth_url" -> "http://mydomain.com/auth")
}
}
}
}
}
According to this test I should be able to convert a Map to json when DefaultJsonProtocol._ is imported but even that's failing:
val map:Map[String, String] = Map("hi"->"bye")
map.toJson
Cannot find JsonWriter or JsonFormat type class for scala.collection.mutable.Map[String,String]
Not sure what's wrong :(
Someone on the spray mailing list pointed out that the Map being created was a mutable one, spray-json won't marshal that. I changed it to be scala.collection.immutable.Map and also added the following imports:
import spray.httpx.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._
And now everything works great.