Many changes
Rewrote slick db service using monix task instead of future Added cats IO to controller to make use of above db service Added action methods that support effect wrappers other than future Added play-flyway to auto-manage migrations Moved migration files to appropriate directory as a result of above Added cors filter to work with separately hosted frontend Made logger log at debug
This commit is contained in:
parent
a9c6cfc96c
commit
cb9524eac7
@ -13,6 +13,11 @@ import com.example.user.CarDAO
|
||||
import com.example.Car.slick.SlickCarDAO
|
||||
import com.example.services.LibraryService
|
||||
import com.example.user.slick.services.SlickLibraryService
|
||||
import com.example.services.TaskLibraryService
|
||||
import com.example.user.slick.services.TaskSlickLibraryService
|
||||
import monix.execution.Scheduler
|
||||
import util.{Schedulers, SchedulersImpl}
|
||||
import slick.jdbc.JdbcProfile
|
||||
|
||||
/**
|
||||
* This module handles the bindings for the API to the Slick implementation.
|
||||
@ -23,9 +28,13 @@ class Module(environment: Environment, configuration: Configuration)
|
||||
extends AbstractModule {
|
||||
override def configure(): Unit = {
|
||||
bind(classOf[Database]).toProvider(classOf[DatabaseProvider])
|
||||
bind(classOf[JdbcProfile]).toProvider(classOf[JdbcProfileProvider])
|
||||
bind(classOf[UserDAO]).to(classOf[SlickUserDAO])
|
||||
bind(classOf[CarDAO]).to(classOf[SlickCarDAO])
|
||||
bind(classOf[LibraryService]).to(classOf[SlickLibraryService])
|
||||
bind(classOf[TaskLibraryService]).to(classOf[TaskSlickLibraryService])
|
||||
bind(classOf[Scheduler]).toProvider(classOf[SchedulerProvider])
|
||||
bind(classOf[Schedulers]).toProvider(classOf[AppSchedulersProvider])
|
||||
bind(classOf[DBCloseHook]).asEagerSingleton()
|
||||
}
|
||||
}
|
||||
@ -35,7 +44,27 @@ class DatabaseProvider @Inject() (config: Config) extends Provider[Database] {
|
||||
lazy val get = Database.forConfig("myapp.database", config)
|
||||
}
|
||||
|
||||
/** Closes database connections safely. Important on dev restart. */
|
||||
@Singleton
|
||||
class SchedulerProvider() extends Provider[Scheduler] {
|
||||
lazy val get = Scheduler.io()
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class AppSchedulersProvider() extends Provider[Schedulers] {
|
||||
lazy val get =
|
||||
new SchedulersImpl(
|
||||
dbScheduler = Scheduler.io(),
|
||||
cpuScheduler = Scheduler.global
|
||||
)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class JdbcProfileProvider() extends Provider[JdbcProfile] {
|
||||
lazy val get: JdbcProfile = _root_.slick.jdbc.H2Profile
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class DBCloseHook @Inject() (db: Database, lifecycle: ApplicationLifecycle) {
|
||||
|
||||
lifecycle.addStopHook { () => Future.successful(db.close()) }
|
||||
}
|
||||
|
@ -9,21 +9,28 @@ import scala.concurrent.ExecutionContext
|
||||
import com.example.user.CarDAO
|
||||
import com.example.services.LibraryService
|
||||
import play.api.libs.json.Json
|
||||
import com.example.services.TaskLibraryService
|
||||
|
||||
case class UserForm(name: String)
|
||||
object UserForm {
|
||||
implicit val userFormFormat = Json.format[UserForm]
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class HomeController @Inject() (
|
||||
userDAO: UserDAO,
|
||||
carDAO: CarDAO,
|
||||
libraryService: LibraryService,
|
||||
taskLibraryService: TaskLibraryService,
|
||||
cc: ControllerComponents
|
||||
)(implicit ec: ExecutionContext)
|
||||
extends AbstractController(cc) {
|
||||
|
||||
def index = Action.async { implicit request =>
|
||||
def index = Action.async {
|
||||
userDAO.all.map { users => Ok(views.html.index(users)) }
|
||||
}
|
||||
|
||||
def cars = Action.async { implicit request =>
|
||||
def cars = Action.async {
|
||||
carDAO.all.map { cars => Ok(views.html.cars(cars)) }
|
||||
}
|
||||
|
||||
@ -36,10 +43,16 @@ class HomeController @Inject() (
|
||||
} yield (Ok(Json.toJson(maybeBook)))
|
||||
}
|
||||
|
||||
def authors(bookId: Long) = Action.async { implicit request =>
|
||||
libraryService.getAuthorsForBook(bookId).map( t => {
|
||||
def authors(bookId: Long) = Action.async {
|
||||
libraryService
|
||||
.getAuthorsForBook(bookId)
|
||||
.map(t => {
|
||||
Ok(Json.toJson(t))
|
||||
})
|
||||
}
|
||||
|
||||
def user = Action {
|
||||
Ok(Json.toJson(UserForm("hello")))
|
||||
}
|
||||
|
||||
}
|
||||
|
34
app/controllers/MonixHomeController.scala
Normal file
34
app/controllers/MonixHomeController.scala
Normal file
@ -0,0 +1,34 @@
|
||||
package controllers
|
||||
|
||||
import javax.inject.{Inject, Singleton}
|
||||
import play.api.mvc._
|
||||
import play.api.libs.json.Json
|
||||
import com.example.services.TaskLibraryService
|
||||
import util.IOHttp._
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import cats.effect.IO
|
||||
import scala.concurrent.ExecutionContext
|
||||
import util.Schedulers
|
||||
|
||||
@Singleton
|
||||
class MonixHomeController @Inject() (
|
||||
taskLibraryService: TaskLibraryService,
|
||||
cc: ControllerComponents
|
||||
)(schedulers: Schedulers, ec: ExecutionContext)
|
||||
extends AbstractController(cc)
|
||||
with LazyLogging {
|
||||
|
||||
private implicit val defaultScheduler = schedulers.dbScheduler
|
||||
|
||||
def authors(bookId: Long) = Action.asyncF {
|
||||
for {
|
||||
_ <- IO(logger.debug("Getting Authors"))
|
||||
authors <- taskLibraryService.getAuthorsForBook(bookId).to[IO]
|
||||
_ <- IO(logger.debug("Where am I now?"))
|
||||
_ <- IO.shift(ec)
|
||||
_ <- IO(logger.info("Got Authors"))
|
||||
res <- IO.pure(Ok(Json.toJson(authors)))
|
||||
} yield (res)
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
package service
|
||||
// package service
|
||||
|
||||
import javax.inject._
|
||||
import play.api.db.slick.DatabaseConfigProvider
|
||||
import scala.concurrent.ExecutionContext
|
||||
import play.api.db.slick.HasDatabaseConfigProvider
|
||||
import slick.jdbc.JdbcProfile
|
||||
// import javax.inject._
|
||||
// import play.api.db.slick.DatabaseConfigProvider
|
||||
// import scala.concurrent.ExecutionContext
|
||||
// import play.api.db.slick.HasDatabaseConfigProvider
|
||||
// import slick.jdbc.JdbcProfile
|
||||
|
||||
@Singleton
|
||||
class MyService @Inject() (
|
||||
protected val dbConfigProvider: DatabaseConfigProvider,
|
||||
implicit val ec: ExecutionContext
|
||||
) extends HasDatabaseConfigProvider[JdbcProfile] {
|
||||
// @Singleton
|
||||
// class MyService @Inject() (
|
||||
// protected val dbConfigProvider: DatabaseConfigProvider,
|
||||
// implicit val ec: ExecutionContext
|
||||
// ) extends HasDatabaseConfigProvider[JdbcProfile] {
|
||||
|
||||
import profile.api._
|
||||
db
|
||||
}
|
||||
// import profile.api._
|
||||
// db
|
||||
// }
|
||||
|
18
app/util/Schedulers.scala
Normal file
18
app/util/Schedulers.scala
Normal file
@ -0,0 +1,18 @@
|
||||
package util
|
||||
|
||||
import monix.execution.Scheduler
|
||||
|
||||
/**
|
||||
* Class containing various schedulers for offloading blocking tasks
|
||||
* from play's default thread pool
|
||||
*
|
||||
* @param dbScheduler
|
||||
* @param cpuScheduler
|
||||
*/
|
||||
trait Schedulers {
|
||||
def dbScheduler: Scheduler
|
||||
def cpuScheduler: Scheduler
|
||||
}
|
||||
|
||||
class SchedulersImpl(val dbScheduler: Scheduler, val cpuScheduler: Scheduler)
|
||||
extends Schedulers
|
58
app/util/Util.scala
Normal file
58
app/util/Util.scala
Normal file
@ -0,0 +1,58 @@
|
||||
package util
|
||||
|
||||
import scala.concurrent.Future
|
||||
import cats.effect.IO
|
||||
import cats.implicits._
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import cats.effect.Effect
|
||||
import play.api.mvc._
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
object IOHttp {
|
||||
|
||||
/**
|
||||
* Actions to convert an effect wrapper F such as cats IO
|
||||
* or monix Task into a future implicitly
|
||||
*
|
||||
* @param ab
|
||||
*/
|
||||
implicit class ActionBuilderOps[+R[_], B](ab: ActionBuilder[R, B]) {
|
||||
|
||||
import cats.effect.implicits._
|
||||
|
||||
/**
|
||||
* Action to convert an effect wrapper F such as cats IO
|
||||
* or monix Task into a future implicitly
|
||||
*
|
||||
* @param ab
|
||||
*/
|
||||
def asyncFR[F[_]: Effect](cb: R[B] => F[Result]): Action[B] = ab.async {
|
||||
c => cb(c).toIO.unsafeToFuture()
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to convert an effect wrapper F such as cats IO
|
||||
* or monix Task into a future implicitly
|
||||
*
|
||||
* @param ab
|
||||
*/
|
||||
def asyncF[F[_]: Effect](cb: => F[Result]): Action[AnyContent] =
|
||||
ab.async { cb.toIO.unsafeToFuture() }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object RepoUtil {
|
||||
def fromFuture[IOEffect[A], A](
|
||||
f: => Future[A]
|
||||
)(implicit ec: ExecutionContext): IO[A] =
|
||||
IO.delay(f) >>= (f =>
|
||||
IO.async[A] { cb =>
|
||||
f.onComplete {
|
||||
case Success(a) => cb(Right(a))
|
||||
case Failure(ex) => cb(Left(ex))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
30
build.sbt
30
build.sbt
@ -20,9 +20,8 @@ resolvers in ThisBuild += Resolver.sonatypeRepo("snapshots")
|
||||
|
||||
libraryDependencies in ThisBuild ++= Seq(
|
||||
"javax.inject" % "javax.inject" % "1",
|
||||
// "joda-time" % "joda-time" % "2.10.2",
|
||||
// "org.joda" % "joda-convert" % "2.2.1",
|
||||
"com.google.inject" % "guice" % "4.2.3"
|
||||
"com.google.inject" % "guice" % "4.2.3",
|
||||
"org.flywaydb" %% "flyway-play" % "6.0.0"
|
||||
)
|
||||
|
||||
scalaVersion in ThisBuild := "2.13.1"
|
||||
@ -35,13 +34,13 @@ scalacOptions in ThisBuild ++= Seq(
|
||||
"-Xlint",
|
||||
"-Ywarn-numeric-widen"
|
||||
)
|
||||
javacOptions in ThisBuild ++= Seq("-source", "1.8", "-target", "1.8")
|
||||
javacOptions in ThisBuild ++= Seq("-source", "11", "-target", "11")
|
||||
|
||||
lazy val flyway = (project in file("modules/flyway"))
|
||||
.enablePlugins(FlywayPlugin)
|
||||
.settings(
|
||||
libraryDependencies += "org.flywaydb" % "flyway-core" % FlywayVersion,
|
||||
flywayLocations := Seq("classpath:db/migration"),
|
||||
flywayLocations := Seq("classpath:conf/db/migration/default"),
|
||||
flywayUrl := databaseUrl,
|
||||
flywayUser := databaseUser,
|
||||
flywayPassword := databasePassword,
|
||||
@ -51,7 +50,12 @@ lazy val flyway = (project in file("modules/flyway"))
|
||||
lazy val api = (project in file("modules/api")).settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"com.typesafe.play" %% "play-json" % "2.8.1",
|
||||
),
|
||||
"org.typelevel" %% "cats-core" % "2.1.1",
|
||||
"org.typelevel" %% "cats-effect" % "2.1.4",
|
||||
"io.monix" %% "monix" % "3.2.2",
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3"
|
||||
)
|
||||
)
|
||||
|
||||
lazy val slick = (project in file("modules/slick"))
|
||||
@ -62,8 +66,12 @@ lazy val slick = (project in file("modules/slick"))
|
||||
"com.typesafe.slick" %% "slick" % "3.3.2",
|
||||
"com.typesafe.slick" %% "slick-hikaricp" % "3.3.2",
|
||||
"io.bfil" %% "automapper" % "0.7.0",
|
||||
"io.scalaland" %% "chimney" % "0.5.2"
|
||||
// "com.github.tototoshi" %% "slick-joda-mapper" % "2.4.1"
|
||||
"io.scalaland" %% "chimney" % "0.5.2",
|
||||
"org.typelevel" %% "cats-core" % "2.1.1",
|
||||
"org.typelevel" %% "cats-effect" % "2.1.4",
|
||||
"io.monix" %% "monix" % "3.2.2",
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3"
|
||||
),
|
||||
slickCodegenDatabaseUrl := databaseUrl,
|
||||
slickCodegenDatabaseUser := databaseUser,
|
||||
@ -74,9 +82,6 @@ lazy val slick = (project in file("modules/slick"))
|
||||
slickCodegenExcludedTables := Seq("schema_version"),
|
||||
slickCodegenCodeGenerator := { (model: m.Model) =>
|
||||
new SourceCodeGenerator(model) {
|
||||
// override def code =
|
||||
// "import com.github.tototoshi.slick.H2JodaSupport._\n" + "import org.joda.time.DateTime\n" + super.code
|
||||
|
||||
override def Table = new Table(_) {
|
||||
override def Column = new Column(_) {
|
||||
override def rawType = model.tpe match {
|
||||
@ -104,7 +109,6 @@ lazy val root = (project in file("."))
|
||||
"com.h2database" % "h2" % "1.4.199",
|
||||
ws % Test,
|
||||
"org.flywaydb" % "flyway-core" % FlywayVersion % Test,
|
||||
"com.typesafe.play" %% "play-slick" % "5.0.0",
|
||||
"org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test,
|
||||
"com.typesafe.play" %% "play-json" % "2.8.1",
|
||||
"io.bfil" %% "automapper" % "0.7.0"
|
||||
@ -113,4 +117,4 @@ lazy val root = (project in file("."))
|
||||
)
|
||||
.aggregate(slick)
|
||||
.dependsOn(slick)
|
||||
// fork := true
|
||||
.dependsOn(flyway)
|
||||
|
@ -6,3 +6,17 @@ myapp = {
|
||||
maxConnections=20
|
||||
}
|
||||
}
|
||||
|
||||
play.filters.enabled += "play.filters.cors.CORSFilter"
|
||||
play.modules.enabled += "org.flywaydb.play.PlayModule"
|
||||
|
||||
db.default.driver=${myapp.database.driver}
|
||||
db.default.url=${myapp.database.url}
|
||||
db.default.username=${myapp.database.user}
|
||||
db.default.password=${myapp.database.password}
|
||||
|
||||
play.filters.cors {
|
||||
allowedHttpMethods = ["GET", "POST", "OPTION"]
|
||||
allowedHttpHeaders = ["Accept"]
|
||||
preflightMaxAge = 3 days
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
|
||||
<pattern>%date{HH:mm:ss.SSS} %thread %coloredLevel %logger{15} - %message%n%xException{10}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
@ -23,15 +23,17 @@
|
||||
<appender-ref ref="STDOUT" />
|
||||
</appender>
|
||||
|
||||
<logger name="play" level="INFO"/>
|
||||
<logger name="application" level="INFO"/>
|
||||
<logger name="play" level="DEBUG" />
|
||||
<logger name="application" level="DEBUG" />
|
||||
|
||||
<!-- Useful debugging settings in slick -->
|
||||
<logger name="slick.jdbc.JdbcBackend.statement" level="INFO" />
|
||||
<logger name="slick.jdbc.JdbcBackend.benchmark" level="INFO" />
|
||||
<logger name="com.zaxxer.hikari" level="WARN"/>
|
||||
<logger name="com.zaxxer.hikari" level="INFO" />
|
||||
|
||||
<root level="WARN">
|
||||
<logger name="controllers" level="DEBUG" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="ASYNCFILE" />
|
||||
<appender-ref ref="ASYNCSTDOUT" />
|
||||
</root>
|
||||
|
@ -6,7 +6,8 @@
|
||||
GET / controllers.HomeController.index
|
||||
GET /cars controllers.HomeController.cars
|
||||
GET /book controllers.HomeController.book
|
||||
GET /authors/:bookId controllers.HomeController.authors(bookId: Long)
|
||||
GET /authors/:bookId controllers.MonixHomeController.authors(bookId: Long)
|
||||
GET /user controllers.HomeController.user
|
||||
|
||||
# Map static resources from the /public folder to the /assets URL path
|
||||
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.example.services
|
||||
|
||||
import monix.eval.Task
|
||||
import com.example.models.Book
|
||||
import com.example.models.Author
|
||||
import com.example.models.NewAuthor
|
||||
import com.example.models.NewBook
|
||||
|
||||
trait TaskLibraryService {
|
||||
// Simple function that returns a book
|
||||
def findBookById(id: Long): Task[Option[Book]]
|
||||
|
||||
// Simple function that returns a list of books with it's author
|
||||
def findBooksWithAuthor: Task[Seq[(Book, Author)]]
|
||||
|
||||
// Insert a book and an author composing two DBIOs in a transaction
|
||||
def insertBookAndAuthor(book: NewBook, author: NewAuthor): Task[(Long, Long)]
|
||||
|
||||
def getAuthorsForBook(id: Long): Task[Seq[(Author, Book)]]
|
||||
}
|
@ -24,6 +24,7 @@ myapp = {
|
||||
validationTimeout = 5000
|
||||
|
||||
connectionPool = disabled
|
||||
keepAlive = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,11 @@ import com.example.models._
|
||||
import io.scalaland.chimney.dsl._
|
||||
import com.example.user.slick.Tables
|
||||
import javax.inject.Singleton
|
||||
// import slick.jdbc.H2Profile.api._
|
||||
// import scala.concurrent.ExecutionContext
|
||||
import javax.inject._
|
||||
|
||||
@Singleton
|
||||
class SlickLibraryDbio extends Tables {
|
||||
|
||||
override val profile: JdbcProfile = _root_.slick.jdbc.H2Profile
|
||||
class SlickLibraryDbio @Inject() (override val profile: JdbcProfile)
|
||||
extends Tables {
|
||||
|
||||
import profile.api._
|
||||
|
||||
|
@ -16,10 +16,13 @@ class SlickLibraryService @Inject() (
|
||||
libraryDbio: SlickLibraryDbio
|
||||
)(implicit ec: ExecutionContext)
|
||||
extends LibraryService {
|
||||
import libraryDbio.profile.api._
|
||||
|
||||
override def getAuthorsForBook(id: Long): Future[Seq[(Author, Book)]] = {
|
||||
db.run {
|
||||
libraryDbio.getAuthorsForBook(id).map(_.map {
|
||||
libraryDbio
|
||||
.getAuthorsForBook(id)
|
||||
.map(_.map {
|
||||
case (x, y) => {
|
||||
(libraryDbio.authorsRowToAuthor(x), libraryDbio.booksRowToBooks(y))
|
||||
}
|
||||
@ -27,8 +30,6 @@ class SlickLibraryService @Inject() (
|
||||
}
|
||||
}
|
||||
|
||||
import libraryDbio.profile.api._
|
||||
|
||||
// Simple function that returns a book
|
||||
def findBookById(id: Long): Future[Option[Book]] =
|
||||
db.run(libraryDbio.findBookById(id).map(_.map(libraryDbio.booksRowToBooks)))
|
||||
|
@ -0,0 +1,97 @@
|
||||
package com.example.user.slick.services
|
||||
|
||||
import javax.inject._
|
||||
// import slick.jdbc.JdbcProfile
|
||||
import slick.jdbc.JdbcBackend.Database
|
||||
import com.example.models._
|
||||
import com.example.user.slick.dbios.SlickLibraryDbio
|
||||
import monix.eval.Task
|
||||
import com.example.services.TaskLibraryService
|
||||
|
||||
@Singleton
|
||||
class TaskSlickLibraryService @Inject() (
|
||||
db: Database,
|
||||
libraryDbio: SlickLibraryDbio
|
||||
) extends TaskLibraryService {
|
||||
import libraryDbio.profile.api._
|
||||
|
||||
override def findBookById(id: Long): Task[Option[Book]] =
|
||||
Task
|
||||
.deferFuture {
|
||||
db.run(
|
||||
libraryDbio.findBookById(id)
|
||||
)
|
||||
}
|
||||
.flatMap(record => Task.eval(record.map(libraryDbio.booksRowToBooks)))
|
||||
|
||||
override def findBooksWithAuthor: Task[Seq[(Book, Author)]] =
|
||||
Task.deferFuture(db.run(libraryDbio.findBooksWithAuthor)).flatMap {
|
||||
records =>
|
||||
Task.traverse(records) {
|
||||
case (x, y) =>
|
||||
Task.eval {
|
||||
(
|
||||
libraryDbio.booksRowToBooks(x),
|
||||
libraryDbio.authorsRowToAuthor(y)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// override def storeMulti(aggregates: Seq[AggregateType]): Task[Long] =
|
||||
// for {
|
||||
// records <- Task.traverse(aggregates) { aggregate =>
|
||||
// convertToRecord(aggregate)
|
||||
// }
|
||||
// result <- Task.deferFutureAction { implicit ec =>
|
||||
// import profile.api._
|
||||
// db.run(DBIO.sequence(records.foldLeft(Seq.empty[DBIO[Long]]) {
|
||||
// case (result, record) =>
|
||||
// result :+ dao.insertOrUpdate(record).map(_.toLong)
|
||||
// }))
|
||||
// .map(_.sum)
|
||||
// }
|
||||
// } yield result
|
||||
|
||||
override def insertBookAndAuthor(
|
||||
book: NewBook,
|
||||
author: NewAuthor
|
||||
): Task[(Long, Long)] =
|
||||
Task.deferFutureAction { implicit scheduler =>
|
||||
val action = for {
|
||||
authorId <- libraryDbio.insertAuthor2(author)
|
||||
bookId <- libraryDbio.insertBook2(book.copy(authorId = authorId))
|
||||
} yield (bookId, authorId)
|
||||
|
||||
db.run(action.transactionally)
|
||||
}
|
||||
|
||||
override def getAuthorsForBook(id: Long): Task[Seq[(Author, Book)]] =
|
||||
for {
|
||||
records <- Task.deferFuture(db.run(libraryDbio.getAuthorsForBook((id))))
|
||||
result <- Task.traverse(records) {
|
||||
case (authorsRow, booksRow) =>
|
||||
Task.eval(
|
||||
(
|
||||
libraryDbio.authorsRowToAuthor(authorsRow),
|
||||
libraryDbio.booksRowToBooks(booksRow)
|
||||
)
|
||||
)
|
||||
}
|
||||
} yield (result)
|
||||
|
||||
// Task.deferFutureAction { implicit scheduler =>
|
||||
// db.run(
|
||||
// libraryDbio
|
||||
// .getAuthorsForBook(id)
|
||||
// .map(_.map {
|
||||
// case (x, y) => {
|
||||
// (
|
||||
// libraryDbio.authorsRowToAuthor(x),
|
||||
// libraryDbio.booksRowToBooks(y)
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
// )
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue
Block a user