ReactiveMongo and Play framework: error ConnectionNotInitialized - mongodb

I'm trying to follow the tutorial of "reactiveMongo" with play framework and scala, but I get the following error:
[ConnectionNotInitialized: MongoError['Connection is missing metadata (like protocol version, etc.) The connection pool is probably being initialized.']]
My application.conf haves:
play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoModule"
mongodb.uri ="mongodb://localhost:27017/cambio"
My build.sbt haves:
libraryDependencies += "org.reactivemongo" %% "play2-reactivemongo" % "0.12.5-play26"
My mongo is start.
My controller is:
package controllers
import javax.inject.Inject
import scala.concurrent.Future
import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
// Reactive Mongo imports
import reactivemongo.api.Cursor
import reactivemongo.api.ReadPreference
import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}
// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._
/*
* Example using ReactiveMongo + Play JSON library.
*
* There are two approaches demonstrated in this controller:
* - using JsObjects directly
* - using case classes that can be turned into JSON using Reads and Writes.
*
* This controller uses JsObjects directly.
*
* Instead of using the default Collection implementation (which interacts with
* BSON structures + BSONReader/BSONWriter), we use a specialized
* implementation that works with JsObject + Reads/Writes.
*
* Of course, you can still use the default Collection implementation
* (BSONCollection.) See ReactiveMongo examples to learn how to use it.
*/
class UserController #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
/*
* Get a JSONCollection (a Collection implementation that is designed to work
* with JsObject, Reads and Writes.)
* Note that the `collection` is not a `val`, but a `def`. We do _not_ store
* the collection reference to avoid potential problems in development with
* Play hot-reloading.
*/
def collection: JSONCollection = db.collection[JSONCollection]("broker")
def index = Action { Ok("works") }
def create(name: String, age: Int) = Action.async {
val json = Json.obj(
"name" -> name,
"age" -> age,
"created" -> new java.util.Date().getTime())
collection.insert(json).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
def createFromJson = Action.async(parse.json) { request =>
import play.api.libs.json.Reads._
/*
* request.body is a JsValue.
* There is an implicit Writes that turns this JsValue as a JsObject,
* so you can call insert() with this JsValue.
* (insert() takes a JsObject as parameter, or anything that can be
* turned into a JsObject using a Writes.)
*/
val transformer: Reads[JsObject] =
Reads.jsPickBranch[JsString](__ \ "firstName") and
Reads.jsPickBranch[JsString](__ \ "lastName") and
Reads.jsPickBranch[JsNumber](__ \ "age") reduce
request.body.transform(transformer).map { result =>
collection.insert(result).map { lastError =>
Logger.debug(s"Successfully inserted with LastError: $lastError")
Created
}
}.getOrElse(Future.successful(BadRequest("invalid json")))
}
def findByName(name: String) = Action.async {
// let's do our query
val cursor: Cursor[JsObject] = collection.
// find all people with name `name`
find(Json.obj("name" -> name)).
// sort them by creation date
sort(Json.obj("created" -> -1)).
// perform the query and get a cursor of JsObject
cursor[JsObject](ReadPreference.primary)
// gather all the JsObjects in a list
val futurePersonsList: Future[List[JsObject]] = cursor.collect[List]()
// transform the list into a JsArray
val futurePersonsJsonArray: Future[JsArray] =
futurePersonsList.map { persons => Json.arr(persons) }
// everything's ok! Let's reply with the array
futurePersonsJsonArray.map { broker =>
Ok(broker)
}
}
}
My routes is:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# An example controller showing a sample home page
GET / controllers.HomeController.index
GET /crear/:name/:age controllers.UserController.create(name: String, age: Int)
GET /buscar/:name controllers.UserController.findByName(name: String)
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)

Related

Play Framework Dependency Injection error

This is my code:
package controllers
import javax.inject.Inject
import scala.concurrent.Future
import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
// Reactive Mongo imports
import reactivemongo.api.Cursor
import reactivemongo.api.ReadPreference
import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}
// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._
/*
* Example using ReactiveMongo + Play JSON library.
*
* There are two approaches demonstrated in this controller:
* - using JsObjects directly
* - using case classes that can be turned into JSON using Reads and Writes.
*
* This controller uses JsObjects directly.
*
* Instead of using the default Collection implementation (which interacts with
* BSON structures + BSONReader/BSONWriter), we use a specialized
* implementation that works with JsObject + Reads/Writes.
*
* Of course, you can still use the default Collection implementation
* (BSONCollection.) See ReactiveMongo examples to learn how to use it.
*/
class Application #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
def collection: Future[JSONCollection] =
database.map(_.collection[JSONCollection]("Persons"))
def index = Action { Ok("works") }
def create(name: String, age: Int) = Action.async {
val json = Json.obj(
"name" -> name,
"age" -> age,
"created" -> new java.util.Date().getTime())
collection.flatMap(_.insert(json)).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
}
I´ve an error that said can´t find database value (database is a member of ReactiveMongoApi Trait) so I guess there is something wrong with dependency injection. I added also this line to the build sbt:
adding routesGenerator := InjectedRoutesGenerator
Any ideas? thank you!
You need to refer to the injected API. That is, replace database with reactiveMongoApi.database:
class Application #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
def collection: Future[JSONCollection] =
reactiveMongoApi.database.map(_.collection[JSONCollection]("Persons"))
...
}

ReactiveMongo with Play Silhouette JWT Authenticator

I am currently building a REST Api using Play 2.5 (Scala) using Play Silhouette 4.0 as my authentication library.
I have a need to invalidate JWT per user therefore I will need to persist the authenticator in the database (mongodb). I am using ReactiveMongo 0.12.1.
Following the code examples of Silhouette's CacheAuthenticatorRepository and MongoAuthInfoDAO for ReactiveMongo, I have created an authenticator repository just for the JWT Authenticator like so:
package modules
import javax.inject.Inject
import com.mohiva.play.silhouette.api.StorableAuthenticator
import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository
import modules._
import com.mohiva.play.silhouette.api.util._
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import scala.reflect.ClassTag
import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo }
import com.mohiva.play.silhouette.persistence.exceptions.MongoException
import play.api.Configuration
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json.{ Format, JsObject, Json }
import play.modules.reactivemongo.ReactiveMongoApi
import play.modules.reactivemongo.json._
import reactivemongo.api.commands.WriteResult
import reactivemongo.play.json.collection.JSONCollection
import play.api.libs.json.Reads
/**
* Implementation of the authenticator repository which uses the reactive mongo to persist the authenticator.
*
* #param cacheLayer The cache layer implementation.
* #tparam T The type of the authenticator to store.
*/
class MongoAuthenticatorRepository[JWTAuthenticator] #Inject() (reactiveMongoApi: ReactiveMongoApi)
extends AuthenticatorRepository[JWTAuthenticator] {
def collection: Future[JSONCollection] = reactiveMongoApi.database.map(_.collection("jwt.auth.repo"))
/**
* Finds the authenticator for the given ID.
*
* #param id The authenticator ID.
* #return The found authenticator or None if no authenticator could be found for the given ID.
*/
override def find(id: String): Future[Option[JWTAuthenticator]] = {
//cacheLayer.find[JWTAuthenticator](id)
val query = Json.obj("id" -> id)
collection.flatMap(_.find(query).one[JWTAuthenticator])
}
/**
* Adds a new authenticator.
*
* #param authenticator JWTAuthenticatorhe authenticator to add.
* #return JWTAuthenticatorhe added authenticator.
*/
override def add(authenticator: JWTAuthenticator): Future[JWTAuthenticator] = {
//cacheLayer.save[JWTAuthenticator](authenticator.id, authenticator, Duration.Inf)
val obj = Json.obj("id" -> authenticator.id , "authenticator" -> authenticator, "duration" -> Duration.Inf)
collection.flatMap(_.insert(obj))
}
/**
* Updates an already existing authenticator.
*
* #param authenticator JWTAuthenticatorhe authenticator to update.
* #return JWTAuthenticatorhe updated authenticator.
*/
override def update(authenticator: JWTAuthenticator): Future[JWTAuthenticator] = {
//cacheLayer.save[JWTAuthenticator](authenticator.id, authenticator, Duration.Inf)
val obj = Json.obj("id" -> authenticator.id , "authenticator" -> authenticator, "duration" -> Duration.Inf)
collection.flatMap(_.update(Json.obj("id" -> authenticator.id), obj, upsert = true))
}
/**
* Removes the authenticator for the given ID.
*
* #param id JWTAuthenticatorhe authenticator ID.
* #return An empty future.
*/
override def remove(id: String): Future[Unit] = {
//cacheLayer.remove(id)
val query = Json.obj("id" -> id)
collection.flatMap(_.remove(query))
}
}
I then implemented it by modifying my silhouette.module file like so:
#Provides def provideAuthenticatorService(authenticatorEncoder: AuthenticatorEncoder, idGenerator: IDGenerator, configuration: Configuration, clock: Clock): AuthenticatorService[JWTAuthenticator] = {
val config = new JWTAuthenticatorSettings(
fieldName = configuration.underlying.getString("silhouette.authenticator.fieldName"),
//requestParts = Some(configuration.underlying.getAs[Seq[RequestPart.Value]]("silhouette.authenticator.requestParts").get),
issuerClaim = configuration.underlying.getString("silhouette.authenticator.issuerClaim"),
sharedSecret = configuration.underlying.getString("silhouette.authenticator.sharedSecret"),
authenticatorExpiry = configuration.underlying.getAs[FiniteDuration]("silhouette.authenticator.authenticatorExpiry").get,
authenticatorIdleTimeout = Some(configuration.underlying.getAs[FiniteDuration]("silhouette.authenticator.authenticatorIdleTimeout").get)
)
implicit lazy val format = Json.format[JWTAuthenticator]
val repo = new MongoAuthenticatorRepository[JWTAuthenticator]
new JWTAuthenticatorService(config, repo, authenticatorEncoder, idGenerator, clock)
}
I keep having problems compiling the code and the errors shown are:
"No Json deserializer found for type JWTAuthenticator. Try to implement an implicit Reads or Format for this type."
and
"Imported `MongoAuthenticatorRepository' is permanently hidden by definition of class MongoAuthenticatorRepository in package modules"
I tried to implement a custom JWTAuthenticator class as well and setting implicit formats within the companion object. However the errors still persists.
I am new to Scala as well as Play Framework, would appreciate any pointers or tips on resolving the above issue! Thanks!!

Play Framework Anorm withConnection Error

I have the following case class and class in Play Framework 2.5. Since the DB object has been deprecated I'm trying to use the new dependency inject methodology but not understanding why my code does not compile. The old method works fine, the new method has compilation error.
missing parameter type on the implicit connection =>
package models
import javax.inject.{Inject, Singleton}
import play.db._
import anorm.SqlParser._
import anorm._
import play.api.db.DB
import play.api.Play.current
case class Department(id: Int,
name: String)
#Singleton
class DepartmentDao #Inject() (dBApi: DBApi) {
private val db = dBApi.getDatabase("default")
val simple = {
get[Int]("department.id") ~
get[String]("department.name") map {
case id ~ name => Department(id, name)
}
}
def getDepartments = DB.withConnection { implicit connection =>
SQL("SELECT * FROM DEPARTMENT")
}
def getDepartmentsNew = db.withConnection { implicit connection =>
SQL("SELECT * FROM DEPARMTMENT")
}
}
UPDATE:
I've placed what the code looked like before was I was originally trying to get it working. Unless I am missing something this code should run. It is identical to code I've seen in multiple sample projects.
#Singleton
class DepartmentDao #Inject() (dBApi: DBApi) {
private val db = dBApi.getDatabase("default")
val simple = {
get[Int]("department.id") ~
get[String]("department.name") map {
case id ~ name => Department(id, name)
}
}
def getDepartments: Seq[Department] = db.withConnection { implicit connection =>
SQL("SELECT * FROM DEPARMTMENT").as(simple.*)
}
}

Instantiate a val from a given #inject class

How do I instantiate an object from this class? I have another class where I would like to use the MongoUtils class as defined below. This class is from reactivemongo
package controllers
import javax.inject.Inject
import scala.concurrent.Future
import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
// Reactive Mongo imports
import reactivemongo.api.Cursor
import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}
// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._
/*
* Example using ReactiveMongo + Play JSON library.
*
* There are two approaches demonstrated in this controller:
* - using JsObjects directly
* - using case classes that can be turned into JSON using Reads and Writes.
*
* This controller uses JsObjects directly.
*
* Instead of using the default Collection implementation (which interacts with
* BSON structures + BSONReader/BSONWriter), we use a specialized
* implementation that works with JsObject + Reads/Writes.
*
* Of course, you can still use the default Collection implementation
* (BSONCollection.) See ReactiveMongo examples to learn how to use it.
*/
class MongoUtils #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
/*
* Get a JSONCollection (a Collection implementation that is designed to work
* with JsObject, Reads and Writes.)
* Note that the `collection` is not a `val`, but a `def`. We do _not_ store
* the collection reference to avoid potential problems in development with
* Play hot-reloading.
*/
def collection: JSONCollection = db.collection[JSONCollection]("persons")
def index = Action { Ok("works") }
def create(name: String, age: Int) = Action.async {
val json = Json.obj(
"name" -> name,
"age" -> age,
"created" -> new java.util.Date().getTime())
collection.insert(json).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
def createFromJson = Action.async(parse.json) { request =>
import play.api.libs.json.Reads._
/*
* request.body is a JsValue.
* There is an implicit Writes that turns this JsValue as a JsObject,
* so you can call insert() with this JsValue.
* (insert() takes a JsObject as parameter, or anything that can be
* turned into a JsObject using a Writes.)
*/
val transformer: Reads[JsObject] =
Reads.jsPickBranch[JsString](__ \ "firstName") and
Reads.jsPickBranch[JsString](__ \ "lastName") and
Reads.jsPickBranch[JsNumber](__ \ "age") reduce
request.body.transform(transformer).map { result =>
collection.insert(result).map { lastError =>
Logger.debug(s"Successfully inserted with LastError: $lastError")
Created
}
}.getOrElse(Future.successful(BadRequest("invalid json")))
}
def findByName(name: String) = Action.async {
// let's do our query
val cursor: Cursor[JsObject] = collection.
// find all people with name `name`
find(Json.obj("name" -> name)).
// sort them by creation date
sort(Json.obj("created" -> -1)).
// perform the query and get a cursor of JsObject
cursor[JsObject]()
// gather all the JsObjects in a list
val futurePersonsList: Future[List[JsObject]] = cursor.collect[List]()
// transform the list into a JsArray
val futurePersonsJsonArray: Future[JsArray] =
futurePersonsList.map { persons => Json.arr(persons) }
// everything's ok! Let's reply with the array
futurePersonsJsonArray.map { persons =>
Ok(persons)
}
}
}
So far I have tried val mongoutil = new MongoUtils(reactiveMongoApi = play.modules.reactivemongo)
but apparently it does not work.
You can use Play’s dependency injection mechanism to resolve instance of ReactiveMongoApi which is the interface to MongoDB. The steps could be:
Add the lines below to application.conf
play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoModule"
play.modules.enabled += "modules.CommonModule"
modules.CommonModule is the place you define your bindings, so that Guice, a DI framework integrated with Play 2.4, knows which implementation to be used for a particular interface. For example,
package modules
import com.google.inject.AbstractModule
import dao.impl.UserDaoMongo
import dao.UserDao
import play.api.{Configuration, Environment}
import services.{UserService, UserServiceImpl}
case class CommonModule(environment: Environment, configuration: Configuration) extends AbstractModule {
def configure() {
bindDAOs()
bindServices()
}
private def bindDAOs(): Unit = {
bind(classOf[UserDao]).to(classOf[UserDaoMongo])
}
def bindServices(): Unit = {
bind(classOf[UserService]).to(classOf[UserServiceImpl])
}
}
In addition, it is a good practice to make controllers singleton. For example:
import javax.inject.{Singleton, Inject}
#Singleton
class UserController #Inject()(userService: UserService) extends Controller {
Please read this to know how to use Reactivemongo with Play framework. On the other hand, this is a complete example: https://github.com/luongbalinh/play-mongo
If you're using this class from a controller, you should annotate your controller as follows to have play inject an instance into it:
class MyController #Inject() (mongoUtils: MongoUtils) extends Controller {}
If you are not using the injected controllers, you can access it via the injector:
Play.current.injector.instanceOf(classOf[MongoUtils])
Read more about dependency injection in play here: https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection

Getting Swagger to work in Play2 Scala

I'm attempting to get Swagger working in a Play2 application. I've include the dependancy in build.sbt. "com.wordnik" %% "swagger-play2" % "1.3.1"
I've defined a series of routes.
GET /api-docs controllers.ApiHelpController.getResources
GET /api-docs/building controllers.ApiHelpController.getResource(path = "/building")
GET /building controllers.Application.building
I also have a model that pulls data from Slick and has annotations.
package models
import scala.slick.driver.SQLServerDriver.simple._
import play.api.libs.json._
import play.api.db.DB
import play.api.Play.current
import Database.threadLocalSession
#XmlRootElement(name = "Building")
case class Building(#XmlElement(name = "BuildingCode") BuildingCode: String,
#XmlElement(name = "BuildingDescription") BuildingDescription: String)
object Building {
lazy val database = Database.forDataSource(DB.getDataSource())
implicit val BuildingReads = Json.reads[Building]
implicit val BuildingWrites = Json.writes[Building]
val BuildingTable = new Table[Building]("building"){
def BuildingCode = column[String]("BuildingCode", O.PrimaryKey)
def BuildingDescription = column[String]("BuildingDescription")
def * = BuildingCode ~ BuildingDescription <> (Building.apply _, Building.unapply _)
}
def getAll: Seq[Building] = {
database withSession {
val q = Query(BuildingTable)
q.list
}
}
}
Here is what my controller looks like.
object Application extends Controller {
def building = Action {
Ok(Json.toJson(Building.getAll))
}
}
When I navigate to /api-docs the page renders.
{"apiVersion":"beta","swaggerVersion":"1.2"}
Then if I navigate to /api-docs/building nothing is rendered. Finally when I navigate to /building I receive the payload. I'm not sure what else I have to do for Swagger to generate it's data. Can someone point me in the right direction?
Can you post your controller implementation? The error is most likely due to missing/improper annotations.
Also, check out https://github.com/wordnik/swagger-core/tree/master/samples/scala-play2 for a good sample play2-swagger app.