Struggling with generic(s) repository methods - scala

I have a Language model, table and repository. So far this works:
package repositories
import javax.inject.Inject
import Helper
import model.{Language, LanguageModel}
import play.api.Logger
import play.api.cache.SyncCacheApi
import slick.jdbc.JdbcProfile
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
class LanguageRepository #Inject()(cache: SyncCacheApi, jdbcProfile: JdbcProfile, implicit val executionContext: ExecutionContext)
{
private val model = new LanguageModel(jdbcProfile)
import jdbcProfile.api._
def all(userName: String): Future[Seq[Language]] =
{
cache.get[Future[Seq[Language]]](buildCacheKey(userName)) match
{
case Some(x) => {Logger.info("[LanguageRepository](all) Found something in cache"); x}
case None => {
Logger.info("[LanguageRepository](all) Nothing useful to be found in cache, calling database now")
val result = retrieve(userName)
result.onComplete{
case Success(value) => if(!value.isEmpty) cache.set(buildCacheKey(userName), result)
case Failure(e) => ()
}
result
}
}
}
private def retrieve(userName: String): Future[Seq[Language]] =
{
// TODO extract this to a repositoryTrait and implement fallbacks etc
val db = Database.forURL(Helper.getDbUrl(), driver = Helper.getDbDriver())
db.run(model.all.result)
}
private def buildCacheKey(userName: String): String = s"$userName.languages"
}
Now I am struggling with the today past me left current me.
So I created this trait and wanted to let it be extended by LanguageRepository to get rid of that generic retrieve method that should be the same for all repositories/models. But sadly no luck so far:
trait Repository
{
type Entity
val model: Base
val profile: JdbcProfile
import profile.api._
protected def retrieve(userName: String): Future[Seq[Entity]] =
{
val db = Database.forURL(Helper.getDbUrl(), driver = Helper.getDbDriver())
db.run(model.all.result)
}
}
This is base:
trait Base
{
val dbProfile: JdbcProfile
import dbProfile.api._
type Entity
type EntityTable <: Table[Entity]
lazy val all = TableQuery[EntityTable]
}
Here I get one error >> class type required but Base.this.EntityTable found
class LanguageModel(databaseProfile: JdbcProfile) extends Base
{
override val dbProfile: JdbcProfile = databaseProfile
import dbProfile.api._
...
override type EntityTable = LanguageTable
}
Repository itself is not compiling either, because the types don't match. There are multiple problems and I am not sure where to start to solve them.

Your base table definition won't work like that. You need class types, maybe you should generics instead. Also, instead of creating multiple abstractions, start with only one abstraction and evolve from there. Try something along these lines:
class Repository[A, B <: Table[A]](t: TableQuery[B]) {
val model = t
//protected def retrieve ..
}
class LanguageModel(databaseProfile: JdbcProfile) extends Repository[Language, LanguageTable](TableQuery[LanguageTable]) {
//...
}
Get everything to compile first, then start adding the abstraction one class at a time.

Related

What is the correct way to register generic type to spray-json-support in akka-http Scala?

So, I have this class:
case class Something[T](data: Option[T] = None)
And i register it like the instruction said in https://github.com/spray/spray-json and in https://doc.akka.io/docs/akka-http/current/common/json-support.html. Like this:
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json.DefaultJsonProtocol
trait InternalJsonFormat extends SprayJsonSupport with DefaultJsonProtocol {
import spray.json._
implicit def somethingFormat[A :JsonFormat] = jsonFormat1(Something.apply[A])
}
And last i used complete from akka http directive. Like this:
import akka.http.scaladsl.server.Directives._
object ResponseIt extends InternalJsonFormat {
def apply[T](rawData: T) = {
val theResponse = Something(data = Some(rawData))
complete(theResponse)
}
}
And then i get an error in complete(theResponse). It said
Type mismatch, expected: ToResponseMarshallable, actual: Something[T]
===========================================================
I have try to edit the last code for debugging purpose, like this:
object ResponseIt extends InternalJsonFormat {
import spray.json._
def apply[T](rawData: T) = {
val theResponse = Something(data = Some(rawData))
val trying = theResponse.toJson
complete(theResponse)
}
}
and get new error in val trying = theResponse.toJson. like this:
No implicits found for parameter writer: JsonWriter[Something[T]]
So, i really confused what is wrong in my code?. Is there any correct way to use the spray json support in akka http?
Thanks in advance
You see, there is no evidence for existence of JsonFormat for your T here:
def apply[T](rawData: T) = {
// ^--- here
val theResponse = Something(data = Some(rawData))
val trying = theResponse.toJson
complete(theResponse)
}
One can rewrite this method to provide JsonFormat for generic T:
def apply[T](rawData: T)(implicit formatter: JsonFormat[T])

Reusing Slick's DB driver code in data access layer

I'm trying to wrap my head around data access with Slick 3.0. After consulting various github examples, I've came with following design.
A singleton Slick object where the DataSource and Driver instances are injected
class Slick(dataSource: DataSource, val driver: JdbcDriver) {
val db = driver.api.Database.forDataSource(dataSource)
}
A trait per DB table where the mappings are defined
The trait is mixed in the upper layer where the queries are constructed.
trait RecipeTable {
protected val slick: Slick
// the ugly import that have to be added when Slick API is used
import slick.driver.api._
type RecipeRow = (Option[Long], String)
class RecipeTable(tag: Tag) extends Table[RecipeRow](tag, "recipe") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = (id, name)
}
protected val recipes = TableQuery[RecipeTable]
}
Now there's obvious drawback that in every *Table trait and also in every place where that is mixed in I need to duplicate import slick.driver.api._ in order to have all Slick's stuff in scope.
This something I'd like to avoid. Ideally the import will be defined only once and reused in downstream components.
Could you please suggest the a design that addresses such a duplication?
I was mainly inspired by this example, however the imports are duplicated there as well.
That "ugly" import is actually a good thing about slick's design. But your way of slick usage can be improved as following,
Create a trait which will provide JdbcDriver
package demo.slick.dbl
trait SlickDriverComponent {
val driver: JdbcDriver
}
trait SlickDBComponent extends SlickDriverComponent {
val db: driver.api.Database
}
Now define your DAO traits as traits dependant on this trait,
package demo.slick.dao
import demo.slick.dbl.SlickDBComponent
trait RecipeDAO { self: SlickDBComponent =>
import driver.api._
type RecipeRow = (Option[Long], String)
class RecipeTable(tag: Tag) extends Table[RecipeRow](tag, "recipe") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = (id, name)
}
val recipes = TableQuery[RecipeTable]
def get5Future = db.run(recipes.take(5).result)
}
When it comes to actually connecting with DB and doing things,
package demo.slick.dbl
trait MySqlDriverProvider extends SlickDriverComponent {
val driver = slick.driver.MySQLDriver
}
object MySqlDBConnection extends MySqlDriverProvider {
val connection = driver.api.Database.forConfig("mysql")
}
trait MySqlDBProvider extends SlickDBComponent {
val driver = slick.driver.MySQLDriver
val db: Database = MySqlDBConnection.connection
}
trait PostgresDriverProvider extends SlickDriverComponent {
val driver = slick.driver.PostgresDriver
}
object PostgresDBConnection extends PostgresDriverProvider {
val connection = driver.api.Database.forConfig("postgres")
}
trait PostgresDBProvider extends SlickDBComponent {
val driver = slick.driver.PostgresDriver
val db: Database = PostgresDBConnection.connection
}
Now finally define your DAO objects as follows,
package demo.slick.dao
import demo.slick.dbl.MySqlDBProvider
object MySqlRecipeDAO extends RecipeDAO with MySqlDBProvider
object PostgresRecipeDAO extends RecipeDAO with PostgresDBProvider
Now, you can use these as follows,
pakcage demo.slick
import scala.util.{Failure, Success, Try}
import scala.concurrent.ExecutionContext.Implicits.global
import demo.slick.RecipeDAO
object App extends Application {
val recipesFuture = MysqlRecipeDAO.get5Future
recipesFuture.onComplete({
case Success(seq) => println("Success :: found :: " + seq)
case Failure(ex) => println("Failure :: failed :: " + ex.getMessage)
})
}
Now... as we all know that different databases have different sets of functionalities and hence the "things" available to you will depend upon the driver being used.
So that need to ugly import every time is so that you can write your DAO traits once and then be able to use them with whatever database specific driver implementation you want.

Spray won't convert my case class to json and expect a spray.httpx.marshalling.ToResponseMarshallable

I'm trying to reprocude this or this, but I keep getting an error I am not able to fix...
First of all, here are my dependencies:
compile 'io.spray:spray-can_2.11:1.3.1'
compile 'io.spray:spray-routing_2.11:1.3.1',
compile 'io.spray:spray-json_2.11:1.2.6'
Now what I'm trying to do is:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import spray.json._
import DefaultJsonProtocol._
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
With:
case object MasterJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport {
import spray.json._
case class Answer(code: Int, content: String)
implicit val anwserFormat: JsonFormat[Answer] = jsonFormat2(Answer)
}
Now I get this error:
Error:(42, 19) type mismatch;
found : MasterJsonProtocol.Answer
required: spray.httpx.marshalling.ToResponseMarshallable
Answer(1, "test")
^
I tried a lot of things but can't manage to make it works.
I tried with
Answer(1, "test").toJson
Answer(1, "test").toJson.asJsObject
Finally what I did was
complete {
Answer(1, "test").toJson.compactPrint
}
This works but it is sent to the client as Content-Type: text/plain when I need application/json.
Anyone see what the problem is here?
Edit: I added a sample project on github https://github.com/ydemartino/spray-test
Move your model outside of the json protocol and make it a regular object (not a case object)
case class Answer(code: Int, content: String)
object MasterJsonProtocol extends DefaultJsonProtocol {
implicit val anwserFormat = jsonFormat2(Answer)
}
Edit
Also clean up your imports:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
I created a pull request to fix your problem: https://github.com/ydemartino/spray-test/pull/1
The json protocol object has to be declared before it can be used implicitly. I'm not wholly sure why the compiler can't figure it out, but moving the object declaration to the top fixed it.
For your actual project make sure to declare packages in each file then use those packages to in the import statements.
In my case the name of the unresolvable implicit format instance conflicted with a local definition, so it got shadowed. The compiler was graciously silent about that. Only discovered that by accident after hours of head-banging.

How to implement an implicit Write for an Object in Play

I have a model
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
case class College(collegeCode: String, collegeName: String)
object College {
lazy val database = Database.forDataSource(DB.getDataSource())
val CollegeTable = new Table[College]("College"){
def collegeCode = column[String]("CollegeCode", O.PrimaryKey)
def collegeName = column[String]("CollegeName")
def * = collegeCode ~ collegeName <> (College.apply _, College.unapply _)
implicit val CollegeReads = Json.reads[College]
implicit val CollegeWrites = Json.writes[College]
}
def getAll: Seq[College] = {
database withSession {
val q = Query(CollegeTable)
q.list
}
}
In my controller i'm attempting to render the data as JSON.
Ok(Json.toJson(College.getAll))
When viewing the page I receive this error:
No Json deserializer found for type Seq[models.College]. Try to implement an implicit Writes or Format for this type.
I thought that defining the implicit read/write in the model would take care of this. It's not until I do something like this:
Ok(Json.toJson(College.getAll.map { c=>
(c.collegeCode, c.collegeName)
} toMap))
in the controller before JSON is actually rendered. What am I doing wrong in the implicit read/write implementation?
The problem is that the controller cannot see the implicits. They are hidden inside the block for defining CollegeTable.
Is there any particular reason you need the implicits in this location? If not then you could put the implicits in your controller.
If you want your model to have the implicits beside it, then move the implicits up a level and import them in the controller.
object College {
implicit val CollegeReads = Json.reads[College]
implicit val CollegeWrites = Json.writes[College]
}
class Controller extends Controller {
import College.CollegeReads
import College.CollegeWrites
...
}

Problem in serialization "scala.math.BigDecimal does not have a no-arg default constructor"

I'm new to scala programming and java so here is my problem:
I have an object to be serialized with a BigDecimal Property
import java.util.Date
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter
import reflect.BeanProperty
class TestObject {
#XmlJavaTypeAdapter(classOf[BigDecimalAdapter])
var test: BigDecimal = 0.00
}
I receive this error:
scala.math.BigDecimal does not have a no-arg default constructor
XmlAdapter:
import javax.xml.bind.annotation.adapters.XmlAdapter
class BigDecimalAdapter extends XmlAdapter[String, BigDecimal] {
def unmarshal(str: String) = BigDecimal(str)
def marshal(bD: BigDecimal) = bD.toString()
}
SOAPServer:
import javax.jws.soap.SOAPBinding
import javax.jws.{WebParam, WebMethod, WebService}
import javax.xml.ws.Endpoint
#WebService(targetNamespace="test", name="testws", portName="test", serviceName="wsTest")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
class Server {
#WebMethod(action = "test")
def test(#WebParam(name = "testParam") testParam:TestObject): TestObject = {
return testParam
}
}
object SoapServer { // defined Companion Object for our class
def main(args: Array[String]) { // main method to make this a runnable application
val endpoint = Endpoint.publish("http://192.168.189.132:8080/wsTest", new Server())
System.out.println("Binded to port 8080. Waiting for requests...")
}
}
I think you probably want to be using java.math.BigDecimal, as opposed to scala.math.BigDecimal. Use the fully-qualified path name:
import java.math.{BigDecimal => JDec}
var test: JDec = new JDec(0)
It seems like the jaxb framework is expecting a no-arg constructor; I'm not familiar enogh with it to understand why