DBAction causes play server not responding - scala

I was doing some simple testing with play-slick. I first used DB.withSession {implicit s:Session => ...} in Global.scala, and it works very well. Then I tried to use DBAction{} in my controller, and things start to fall apart.
package controllers
import play.api.mvc._
import play.api.db.slick._
import play.api.Play.current
import models._
object Main extends Controller {
def index = DBAction { implicit s =>
Articles.insert(Article(None,"title1", "hellothere", Some(timeStamp), Some(timeStamp), None))
Ok(views.html.index.render("OK, It works"))
}
}
The model looks like this (I'm skipping the DAO trait, case class, and the cake-pattern):
object Articles extends DAO {
/* Insert a new Article */
def insert(article: Article)(implicit s: Session) {
Articles.insert(article)
}
}
I tried to run this program, and all I got was the forever server pending status. Everything else works fine, my Global.scala created all tables correctly, and if I remove that Articles.insert() clause, I get my view page. So I would say something must be wrong with this part: DBAction { implicit s=> ...}. I somehow feel like DBAction couldn't find the implicit session, and it keeps looking...which causes this forever pending response.
Well, I searched for it and still don't know what to do. Does anyone know what's wrong here and how should I fix it?

DBAction puts the session down a layer. So in your code above "s" is actually getting set to the request. You could call s.dbSession and get it that way.
def index = DBAction { request =>
implicit val session = request.dbSession
Articles.insert(Article(None,"title1", "hellothere", Some(timeStamp), Some(timeStamp), None))
Ok(views.html.index.render("OK, It works"))
}
I had a similar issue and that is how I resolved it. There has to be a better way to get the session though. If someone else has a better suggestion I would like to know as well.

Related

ScalaTest: Marking a scenario test as an expected failure

When working through testing with FeatureSpec and using scenario, I have part of my code that sends a "failure" case class (i.e. ApplesNotAllowed) and is used via a scalaz disjunction.
Quick example:
val result = if (order.value != 10) {
-\/(ApplesNotSupported(orderNumber))
} else {
\/-Option.empty[Orange]
}
In my test suite, I can easily test for the case where I receive an Option.empty[Orange] by expecting that it shouldBe None. However, I do run into a problem when I want to test for the ApplesNotSupported case class being returned. How would I expect this "failure" that will acknowledge the test as passing since we want the "failure" to happen? Whenever I try to run my scenario, I get the error:
Validation is not success ApplesNotSupported(orderNumber)
Tried using interrupt does not solve the issue since it is not an exception, nor do we want to deliberately fail the test. Could it be an issue with using scalaz and ScalaTest?
I am fairly new to functional programming and programming in Scala overall, so any resources or suggestions help as I do have trouble searching for documentation.
You may want to look in to using disjunction matchers when testing disjunctions:
https://github.com/typelevel/scalaz-scalatest/blob/master/src/main/scala/DisjunctionMatchers.scala
This is the best way to test based on the result of a disjunction.
You can match based on the disjunction itself:
must be_\/-
must be_-\/
Or go a step further and match on the result of the disjunction and the value it contains:
must be_\/-(value)
must be_-\/(value)
I don't personally use ScalaTest, but it looks like it should be the same as it is in specs2.
What about wrapping it in Try and do something like this:
import org.scalatest.{FunSuite, Matchers}
import org.specs2.mock.Mockito
import scala.util.{Failure, Success, Try}
/**
* Created by ssharma on 4/18/17.
*/
class UnitTest extends FunSuite with Matchers{
class A {
def t1(x: Int): Try[Int] = Try(t(x))
private def t(x: Int): Int = {
x match {
case 1 => 1
case _ => throw new RuntimeException
}
}
}
test("A") {
val b = new A
assert(b.t1(1)isSuccess)
}
test("A fail") {
val b = new A
assert(b.t1(2)isFailure)
}
}
I am not familiar with either scalaz or disjunctions but it sounds like the intercept functionality in scalatest's assertions (http://www.scalatest.org/user_guide/using_assertions) may help.
You define the type of exception you are expecting with intercept and you run the code that will throw the exception within the function. You can assign the result of intercept to a val which then you can compare to the expected value using assert or assertResult from scalatest.
Hope this can help! :)

Play 2.5 and ReactiveMongo 0.11 compilation error: value find is not a member of scala.concurrent.Future

I have created a simple app with Play!2.5 where data from a form is written to the database for which I use ReactiveMongo 0.11.
Currently I am trying to get the index page to display all the entries in the DB.
My code:
def index = Action.async{
val cursor: Cursor[User] = collectionF.find(Json.obj()).cursor[User]
val futureUserList :Future[List[User]] = cursor.collect[List]()
val futureUserListJsonArray :Future[JsArray] = futureUserList.map{user =>
Json.arr(user)
}
futureUserListJsonArray.map{ user=>
Ok(user)
}
}
I receive the following compilation error:
value find is not a member of scala.concurrent.Future[reactivemongo.play.json.collection.JSONCollection]
Where the find refers to the collectionF.find() on the second line.
This is what I have imported with regards to concurrency:
import scala.concurrent.Future
import play.api.libs.concurrent.Execution.Implicits.defaultContext
Thanks
As cchantep already pointed out in a comment, you are trying to call find on a Future, not on ReactiveMongo's JSONCollection, and you need a way to access the JSONCollection instead.
However, I'm wondering how you ended up using a Future[JSONCollection] in the first place. It's not only that I can't imagine a viable reason to do that, it's also cumbersome to use that construct afterwards. Yes, you could use e.g. map to get access to the JSONCollection, but this means that the returned Cursor is also a Future (which subsequently forces you to flatMap any operation you are doing with the Cursor).
val cursor: Future[Cursor[User]] = collectionF.map(_.find(...).cursor[User])
val users = cursor.flatMap(...)
Have a look at the documentation of ReactiveMongo's play plugin, especially at the examples at the bottom of the page:
class Application #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
// no Future here:
def collection: JSONCollection = db.collection[JSONCollection]("users")
}

ReactiveMongo w/ Play Scala

New to play,scala, and reactivemongo and the documentation is not very noob friendly.
I see the Bulk Insert section at See Bulk Insert
but I don't know why they aren't showing it contained in a method?
I am expecting a request with JSON data containing multiple objects in it. How do I set up a bulk insert that handles multiple inserts with errors that can be returned.
For example by single insert method is as follows:
def createFromJson = Action(parse.json) {
request =>
try {
val person = request.body.validate[Person].get
val mongoResult = Await.result(collection.insert(person),Duration.apply(20,"seconds"))
if(mongoResult.hasErrors) throw new Exception(mongoResult.errmsg.getOrElse("something unknown"))
Created(Json.toJson(person))
}
catch {
case e: Exception => BadRequest(e.getMessage)
}
}
Here is a full example how you can do it:
class ExampleController #Inject()(database: DefaultDB) extends Controller {
case class Person(firstName: String, lastName: String)
val personCollection: BSONCollection = database.collection("persons")
implicit val PersonJsonReader: Reads[Person] = Json.reads[Person]
implicit val PersonSeqJsonReader: Reads[Seq[Person]] = Reads.seq(PersonJsonReader)
implicit val PersonJsonWriter: Writes[Person] = Json.writes[Person]
implicit val PersonSeqJsonWriter: Writes[Seq[Person]] = Writes.seq(PersonJsonWriter)
implicit val PersonBsonWriter = Macros.writer[Person]
def insertMultiple = Action.async(parse.json) { implicit request =>
val validationResult: JsResult[Seq[Person]] = request.body.validate[Seq[Person]]
validationResult.fold(
invalidValidationResult => Future.successful(BadRequest),
// [1]
validValidationResult => {
val bulkDocs = validValidationResult.
map(implicitly[personCollection.ImplicitlyDocumentProducer](_))
personCollection.bulkInsert(ordered = true)(bulkDocs: _*).map {
case insertResult if insertResult.ok =>
Created(Json.toJson(validationResult.get))
case insertResult =>
InternalServerError
}
}
)
}
}
The meat of it all sits in the lines after [1]. validValidationResult is a variable of type Seq[Person] and contains valid data at this point. Thats what we want to insert into the database.
To do that we need to prepare the documents by mapping each document through the ImplicitlyDocumentProducer of your target collection (here personCollection). Thats leaves you with bulkDocs of type Seq[personCollection.ImplicitlyDocumentProducer]. You can just use bulkInsert() with that:
personCollection.bulkInsert(ordered = true)(bulkDocs: _*)
We use _* here to splat the Seq since bulkInsert() expects varargs and not a Seq. See this thread for more info about it. And thats basically it already.
The remaing code is handling play results and validating the received request body to make sure it contains valid data.
Here are a few general tips to work with play/reactivemongo/scala/futures:
Avoid Await.result. You basically never need it in production code. The idea behind futures is to perform non-blocking operations. Making them blocking again with Await.result defeats the purpose. It can be useful for debugging or test code, but even then there are usually better ways to go about things. Scala futures (unlike java ones) are very powerful and you can do a lot with them, see e.g. flatMap/map/filter/foreach/.. in the Future scaladoc. The above code for instance makes use of exactly that. It uses Action.async instead of Action at the controller method. This means it has to return a Future[Result] instead of a Result. Which is great because ReactiveMongo returns a bunch of Futures for all operations. So all you have to do is execute bulkInsert, which returns a Future and use map() to map the returned Future[MultiBulkWriteResult] to a Future[Result]. This results in no blocking and play can work with the returned future just fine.
Of course the above example can be improved a bit, I tried to keep it simple.
For instance you should return proper error messages when returning BadRequest (request body validation failed) or InternalServerError (database write failed). You can get more info about the errors from invalidValidationResult and insertResult. And you could use Formats instead of that many Reads/Writes (and also use them for ReactiveMongo). Check the play json documentation as well as the reactive mongo doc for more info on that.
Although the previous answer is correct.
We can reduce the boilerplate using JSONCollection
package controllers
import javax.inject._
import play.api.libs.json._
import play.api.mvc._
import play.modules.reactivemongo._
import reactivemongo.play.json.collection.{JSONCollection, _}
import utils.Errors
import scala.concurrent.{ExecutionContext, Future}
case class Person(name: String, age: Int)
object Person {
implicit val formatter = Json.format[Person]
}
#Singleton
class PersonBulkController #Inject()(val reactiveMongoApi: ReactiveMongoApi)(implicit exec: ExecutionContext) extends Controller with MongoController with ReactiveMongoComponents {
val persons: JSONCollection = db.collection[JSONCollection]("person")
def createBulkFromJson = Action.async(parse.json) { request =>
Json.fromJson[Seq[Person]](request.body) match {
case JsSuccess(newPersons, _) =>
val documents = newPersons.map(implicitly[persons.ImplicitlyDocumentProducer](_))
persons.bulkInsert(ordered = true)(documents: _*).map{ multiResult =>
Created(s"Created ${multiResult.n} persons")
}
case JsError(errors) =>
Future.successful(BadRequest("Could not build an array of persons from the json provided. " + errors))
}
}
}
In build.sbt
libraryDependencies ++= Seq(
"org.reactivemongo" %% "play2-reactivemongo" % "0.11.12"
)
Tested with play 2.5.1 although it should compile in previous versions of play.
FYI, as previous answers said, there are two ways to manipulate JSON data: use ReactiveMongo module + Play JSON library, or use ReactiveMongo's BSON library.
The documentation of ReactiveMongo module for Play Framework is available online. You can find code examples there.

Where to put my database access methods when using a DAO with Slick 2.0?

(This question is based on a very similar previous request for help. With the introduction of a DAO and multiple database drivers, the same problem requires a different approach, and I hope warrants a new SO question.)
I have a class and Slick Table defined like this:
import play.api.db.slick.Profile
case class Foo(title: String, description: String, id: Int = 0)
trait FooComponent extends Profile { this: Profile =>
import profile.simple._
class FooTable(tag: Tag) extends Table[Foo](tag, "FOO") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def title = column[String]("TITLE", O.NotNull)
def description = column[String]("DESCRIPTION")
def * = (title, description, id) <> (Foo.tupled, Foo.unapply)
}
}
And a data access object:
class DAO(override val profile: JdbcProfile) extends FooComponent with Profile {
val foos = TableQuery[FooTable]
}
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
This is pretty awesome, because now I can add something like the following to my application.conf:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.test.driver=org.postgresql.Driver
db.test.user="testuser"
db.test.password=""
db.test.url="jdbc:postgresql:testdb"
... and if I do the following in a Controller:
import models.current.dao._
import models.current.dao.profile.simple._
I have access to my foos TableQuery, and it automagically gets the driver and database url given for db.default in application.conf.
In a similar, but not-quite-as-nice way, I can do the following in my test Specification:
"test Foos" in new WithApplication() {
val dao = new DAO(play.api.db.slick.DB("test").driver)
import dao._ //import all our database Tables
import dao.profile.simple._ //import specific database methods
play.api.db.slick.DB("test").withSession { implicit s: Session =>
println(s.conn.getMetaData.getURL)
println(foos.list)
}
However, what if I want to define a method which can act on a TableQuery[Foo]? Something like this:
def findByTitle(title: String) = foos.filter(_.id === id).list
Problem
What's the correct way of writing the findByTitle method, and where should I put it so that I can:
Call it in a way such that it won't collide with a method of the same name which acts on TableQuery[Bar]. Coming from OO, I feel like I want to do something like foos.findByTitle("someFoo"), but if there's a better way of doing this functional-style, I'm open to suggestions.
Call it from an application Controller such that the query will work with my db.default h2 driver, and from my test Specification so that it will work with my db.test postgres driver.
As an aside, if I can put this in my DAO:
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
and then import models.dao.current._ anywhere I want to use this DAO, how can I extend the same form to the following:
object test {
val dao = new DAO(play.api.db.slick.DB("test").driver)
}
If I try to do this, the compiler complains about not having an implicit Application in scope.
I think you need to read up in implicit conversion and implicit parameters in Scala. There are online Scala books available.
When you get an error message about a missing implicit it either means you ran into a failing type-check provided by a library preventing you from doing something wrong, but that's not the case here. Or you simply forgot to make the implicit available. There are two ways to make an implicit available. Either import it into the scope where you get the error message. Or basically defer the lookup to the callsite of your method. Not sure which one is the right one for play. You either need to import the implicit Application from play, or you need turn val dao into a method and request an implicit application in an implicit argument list def dao(implicit app: Application) = .... You can alternatively turn test into a class and request it there.
If you use the play slick plugin it will need a started play application to be able to call code that uses the DB access from that plugin, you can make sure to start a play app in your tests using WithApplication as described in the docs: http://www.playframework.com/documentation/2.3.x/ScalaFunctionalTestingWithSpecs2

How to test Zentasks sample app from Play 2.0

I play with Play 2.0, Scala version. Currently, I analyze Zentasks sample app.
One of the part of this app is authentication mechanism mostly covered in Secured trait. I'm wondering how I can test secured actions, ex. index from Projects controller.
For not-secured action, I'd probably do something like
val result = controllers.Projects.index(FakeRequest())
to run an action and get its result.
What should I do in case of the secured action?
Disclaimer: I'm totally new to both Scala and Play, so all hints are very valuable. Thanks!
There is a fix for the integrated approach to this in Playframewrk v2.1 I have a backport of the fix on the 2.0.x branch
Until it gets merged and released, here is what I did (it works on Play 2.0.3+):
I defined my own Helpers object in a libs package like so.
package libs
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.test._
object Helpers {
def routeAndCall[T](request: FakeRequest[T]): Option[Result] = {
routeAndCall(this.getClass.getClassLoader.loadClass("Routes").asInstanceOf[Class[play.core.Router.Routes]], request)
}
/**
* Use the Router to determine the Action to call for this request and executes it.
*/
def routeAndCall[T, ROUTER <: play.core.Router.Routes](router: Class[ROUTER], request: FakeRequest[T]): Option[play.api.mvc.Result] = {
val routes = router.getClassLoader.loadClass(router.getName + "$").getDeclaredField("MODULE$").get(null).asInstanceOf[play.core.Router.Routes]
routes.routes.lift(request).map {
case a: Action[_] =>
val action = a.asInstanceOf[Action[T]]
val parsedBody: Option[Either[play.api.mvc.Result, T]] = action.parser(request).fold(
(a, in) => Promise.pure(Some(a)),
k => Promise.pure(None),
(msg, in) => Promise.pure(None)
).await.get
parsedBody.map{resultOrT =>
resultOrT.right.toOption.map{innerBody =>
action(FakeRequest(request.method, request.uri, request.headers, innerBody))
}.getOrElse(resultOrT.left.get)
}.getOrElse(action(request))
}
}
}
Then in my test I import my Helpers and the whole play Helpers context, except for routeAndCall :
import libs.Helpers._
import play.api.test.Helpers.{routeAndCall => _,_}
I then use an Around to setup my app (I need the provide an application.secret as I store the authenticated user name in the session which is based on a signed cookie)
def appWithSecret():Map[String,String]={
Map(("application.secret","the answer is 42 !"))
}
object emptyApp extends Around {
def around[T <% Result](t: => T) = {
running(FakeApplication(additionalConfiguration = inMemoryMongoDatabase("emptyApp")++appWithSecret())) {
User(new ObjectId, "Jane Doe", "foobar#example.com", "id1").save()
t // execute t inside a http session
}
}
}
This allows me to write the following tests:
"respond to the index Action" in emptyApp {
val request: FakeRequest[AnyContent] = FakeRequest(GET, "/expenses").withSession(("email", "foobar#example.com"))
val Some(result) = routeAndCall(request)
status(result) must equalTo(OK)
contentType(result) must beSome("application/json")
charset(result) must beSome("utf-8")
contentAsString(result) must contain("Hello Bob")
}
It allows you to exercise the secured code even though it is not a unit test.
ok, I am no great expert either, but here is an idea.
Create a trait InSecure trait extends Secured which overrides the Secured actions and always permits access.
Then you can make an object InSecureProjects extends Projects with InSecture in your test, this should override just the security checks and let you test the actions without any security.
Now, instead of running the tests on Projects, you run them on InSecureProjects. You can do exactly the same for the other secured controllers.
I haven't tested it, so let me know if it works ;)