I migrated my Play version from 2.3 to 2.4. In my previous version I used firstOption to get the latest record from DB. But that functionality is removed in latest version of slick. I searched and found that we can use headOption instead of firstOption. The following is my code :
def findById(id: String)(implicit ec: ExecutionContext): Future[Option[Client]] =
Future {
DB.withConnection { implicit session =>
Clients.filter(c => c.id === id).firstOption
}
}
The imports I have used are as follows :
import play.api.Play.current
import play.api.db.DB
import slick.driver.PostgresDriver.api._
import scala.concurrent.{ExecutionContext, Future}
When I tried to replace firstOption with headOption it shows error 'cannot resolve symbol headOption'. Am I missing any import statements? Is there any alternative way to fix this?
Try
Clients.filter(c => c.id === id).result.headOption
Your object structure is not clear to me but the way I use it is:
val query = tableInstance.filter(_.id === id)
val action = query.result.headOption
db.run(action)
where tableInstance is of slick's TableQuery type
Try to import
scala.collections.convert.WrapAsScala._
This solved the problem on my side.
Related
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 new to scala and same for scalatest.
My aim is to write a Unit test for the following small piece of code:
import java.sql.SQLException
import com.typesafe.scalalogging.LazyLogging
import slick.jdbc.MySQLProfile.api._
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future}
object DbCreator extends LazyLogging {
implicit val ex: ExecutionContextExecutor = ExecutionContext.global
def createDatabaseIfNotExist(): Future[String] = {
Database
.forURL(url = "some host", user = "user", password = "pass", driver = "driver")
.run(sqlu"CREATE DATABASE ...").map(_ => "created")
.recover {
case e: Throwable => {
logger.error("Error!", e)
throw new SQLException(e.getMessage)
}
}
}
}
I've previously used python, which has the patch concept.
So my idea was to patch the Database class being imported and used in createDatabaseIfNotExist, so I can verify the different scenarios.
Unfortunately, I could not find equivalent/similar concept in scalatest.
Did I miss it?
Is my approach wrong? If so, how would u suggest me write a UT for the createDatabaseIfNotExist method?
Is the current DbCreator implementation not testable enough?
Many thanks for all help!
I see several options:
scalamock or some java mocking library
acolyte JDBC driver (has scala DSL)
embedded mysql
I have read this SO post: Play 2.4 - Slick 3.0.0 - DELETE not working
but it seems not to work in my case.
I manage to insert or get some data from my PostgreSQL (9.1) database in my Play! 2.4 application, but I don't manage to perform some deletions.
Here is the very simple code of my controller:
package controllers
import javax.inject.Inject
import play.api.db.slick.DatabaseConfigProvider
import play.api.mvc._
import slick.driver.JdbcProfile
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import slick.driver.PostgresDriver.api._
import models.Address
class Application #Inject()(dbConfigProvider: DatabaseConfigProvider) extends Controller {
val dbConfig = dbConfigProvider.get[JdbcProfile]
import dbConfig._
import dbConfig.driver.api._
def index = Action.async {
db.run(addresses.filter(_.id === 1L).delete) map { res =>
Ok(Json.toJson(res))
}
}
}
and my model Address:
case class Address(id: Option[Long], city: String)
object Address {
class Addresses(tag: Tag) extends Table[Address](tag, "addresses") {
def id = column[Long]("addressid", O.PrimaryKey, O.AutoInc)
def city = column[String]("city")
def * = (id.?, city) <>
((Address.apply _).tupled, Address.unapply)
}
lazy val addresses = TableQuery[Addresses]
}
The compilation error I get is: value delete is not a member of slick.lifted.Query[models.Address.Addresses,models.Address.Addresses#TableElementType,Seq]
I suspect that it comes from the imports, but I tried a lot of things without any improvement.
I suspect that the problem is that you are importing the implicits needed for the delete function twice, thus generating an ambiguity and causing the compiler to give up on that:
import slick.driver.PostgresDriver.api._
import dbConfig.driver.api._
You don't probably need one of the two anyway, if you are trying to be generic (e.g. because you want to run tests over H2) try removing the PostgresDriver import. If that doesn't work try removing the inner import and leave PostgresDriver as the SO post you linked seemed to suggest that delete was (is?) not in the common API.
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._
I have copied Spray Client's example code into my own project, to have it easily available. I use IntelliJ 13.
Here is the code I have:
package mypackage
import scala.util.Success
import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.event.Logging
import akka.io.IO
import spray.json.{JsonFormat, DefaultJsonProtocol}
import spray.can.Http
import spray.util._
import spray.client.pipelining._
import scala.util.Success
import scala.util.Failure
case class Elevation(location: Location, elevation: Double)
case class Location(lat: Double, lng: Double)
case class GoogleApiResult[T](status: String, results: List[T])
object ElevationJsonProtocol extends DefaultJsonProtocol {
implicit val locationFormat = jsonFormat2(Location)
implicit val elevationFormat = jsonFormat2(Elevation)
implicit def googleApiResultFormat[T :JsonFormat] = jsonFormat2(GoogleApiResult.apply[T])
}
object SprayExample extends App {
// we need an ActorSystem to host our application in
implicit val system = ActorSystem("simple-spray-client")
import system.dispatcher // execution context for futures below
val log = Logging(system, getClass)
log.info("Requesting the elevation of Mt. Everest from Googles Elevation API...")
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
val responseFuture = pipeline {
Get("http://maps.googleapis.com/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false")
}
responseFuture onComplete {
case Success(GoogleApiResult(_, Elevation(_, elevation) :: _)) =>
log.info("The elevation of Mt. Everest is: {} m", elevation)
shutdown()
case Success(somethingUnexpected) =>
log.warning("The Google API call was successful but returned something unexpected: '{}'.", somethingUnexpected)
shutdown()
case Failure(error) =>
log.error(error, "Couldn't get elevation")
shutdown()
}
def shutdown(): Unit = {
IO(Http).ask(Http.CloseAll)(1.second).await
system.shutdown()
}
}
As it stands, this works perfectly and it prints the height of Mt.Everest as expected.
The strange thing happens if I move the file down one level in the package structure, that is I create a mypackage.myinnerpackage and move the file inside it.
IDEA changes my first line of code into package mypackage.myinnerpackage and that's it.
Then I try to run the app and the compilation will fail with the following message:
could not find implicit value for evidence parameter of type spray.httpx.unmarshalling.FromResponseUnmarshaller[courserahelper.sprayexamples.GoogleApiResult[courserahelper.sprayexamples.Elevation]]
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
^
I did not change anything in the code, I effectively just changed the package! Additionally, this code is self contained, it does not rely on any other implicit I declared in any other part of my code....
What am I missing?
Thanks!
(Replaced the comment by this answer which supports proper formatting.)
The code you posted is missing these two imports before the usage of unmarshal:
import ElevationJsonProtocol._
import SprayJsonSupport._
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
which exist in the original code. IntelliJ is sometimes messing with imports so that may be the reason they got lost in the transition.
You need to provide a Json Formatter for your case class.
case class Foo(whatever: Option[String])
object FooProtocol extends DefaultJsonProtocol {
implicit val fooJsonFormat = jsonFormat1(Foo)
}
Then include the following near the implementation...
import SprayJsonSupport._
import co.xxx.FooProtocol._