Skip to content

Commit

Permalink
Make generic to Doobie/Anorm
Browse files Browse the repository at this point in the history
  • Loading branch information
gaelrenoux committed Mar 9, 2024
1 parent 61e34cf commit b9e104e
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ package object anorm extends Wrapper {
override final def connectionFromJdbc(connection: => JdbcConnection)(implicit trace: Trace): ZIO[Any, Nothing, Connection] =
ZIO.succeed(connection)
}

override final type DatabaseT[M] = DatabaseTBase[M, Connection]

object DatabaseT extends DatabaseTBase.Companion[Connection, DbContext] {
def apply[M: Tag]: Module[M] = new Module[M](Database)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.gaelrenoux.tranzactio

import zio.{Trace, ZEnvironment, ZIO, ZLayer, Tag}

/**
* This is a type database, to use when you have multiple databases in your application. Simply provide a marker type,
* and ZIO will be able to differentiate between multiple DatabaseT[_] types in your environment.
* @tparam M Marker type, no instances */
class DatabaseTBase[M: Tag, Connection](underlying: DatabaseOps.ServiceOps[Connection]) extends DatabaseOps.ServiceOps[Connection] {

override def transaction[R, E, A](task: => ZIO[Connection with R, E, A], commitOnFailure: => Boolean)
(implicit errorStrategies: ErrorStrategiesRef, trace: Trace): ZIO[R with Any, Either[DbException, E], A] =
underlying.transaction[R, E, A](task, commitOnFailure = commitOnFailure)


override def autoCommit[R, E, A](task: => ZIO[Connection with R, E, A])
(implicit errorStrategies: ErrorStrategiesRef, trace: Trace): ZIO[R with Any, Either[DbException, E], A] =
underlying.autoCommit[R, E, A](task)

}

object DatabaseTBase {
trait Companion[Connection, DbContext] {
type Module[M] = DatabaseTBase.Module[M, Connection, DbContext]

def apply[M: Tag]: Module[M]
}

class Module[M: Tag, Connection: Tag, DbContext](underlying: DatabaseModuleBase[Connection, DatabaseOps.ServiceOps[Connection], DbContext])
extends DatabaseModuleBase[Connection, DatabaseTBase[M, Connection], DbContext] {
override def fromConnectionSource(implicit dbContext: DbContext, trace: Trace): ZLayer[ConnectionSource, Nothing, DatabaseTBase[M, Connection]] =
underlying.fromConnectionSource.map(env => ZEnvironment(new DatabaseTBase[M, Connection](env.get)))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.gaelrenoux.tranzactio

import zio.Trace
import zio.{Tag, Trace}


/** A specific wrapper package for one specific library (e.g. Doobie). */
Expand Down Expand Up @@ -31,4 +31,10 @@ trait Wrapper {
/** Wraps a library-specific query into a TranzactIO. */
def tzio[A](q: => Query[A])(implicit trace: Trace): TranzactIO[A]

/** Parameterized type for databases, allowing for multiple databases to be handled */
type DatabaseT[M]

val DatabaseT: DatabaseTBase.Companion[Connection, DbContext] // scalastyle:ignore field.name


}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package io.github.gaelrenoux.tranzactio

import _root_.doobie.free.KleisliInterpreter
import _root_.doobie.LogHandler
import _root_.doobie.free.KleisliInterpreter
import _root_.doobie.util.transactor.{Strategy, Transactor}
import cats.effect.Resource
import io.github.gaelrenoux.tranzactio.test.DatabaseModuleTestOps
import io.github.gaelrenoux.tranzactio.test.NoopJdbcConnection
import io.github.gaelrenoux.tranzactio.test.{DatabaseModuleTestOps, NoopJdbcConnection}
import zio.interop.catz._
import zio.stream.ZStream
import zio.stream.interop.fs2z._
Expand Down Expand Up @@ -76,4 +75,10 @@ package object doobie extends Wrapper {
implicit val Default: DbContext = DbContext(LogHandler.noop[Task])
}

override final type DatabaseT[M] = DatabaseTBase[M, Connection]

object DatabaseT extends DatabaseTBase.Companion[Connection, DbContext] {
def apply[M: Tag]: Module[M] = new Module[M](Database)
}

}
5 changes: 3 additions & 2 deletions examples/src/test/scala/samples/SamplesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ object SamplesSpec extends ZIOSpecDefault {
suite("SamplesSpec")(
testApp("Doobie", doobie.LayeredApp),
testApp("Doobie-Streaming", doobie.LayeredAppStreaming),
testAppMultipleDatabases("Doobie", doobie.LayeredAppMultipleDatabases),
testApp("Anorm", anorm.LayeredApp),
testAppMultipleDatabases("Doobie", doobie.LayeredAppMultipleDatabases)
testAppMultipleDatabases("Anorm", anorm.LayeredAppMultipleDatabases)
)

private def testApp(name: String, app: ZIOAppDefault): MySpec =
Expand All @@ -34,7 +35,7 @@ object SamplesSpec extends ZIOSpecDefault {

private def testAppMultipleDatabases(name: String, app: ZIOAppDefault): MySpec =

test(s"$name LayeredAppMultipleDatabases prints its progress for the trio, then its progress for the mentor") {
test(s"$name LayeredAppMultipleDatabases prints its progress for the trio, then its progress for the mentor, then the team") {
for {
_ <- app.run.provide(ignoredAppArgs ++ Scope.default)
output <- TestConsole.output
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package samples.anorm

import io.github.gaelrenoux.tranzactio.DbException
import io.github.gaelrenoux.tranzactio.anorm._
import samples.{Conf, ConnectionPool, Person}
import zio._

/** A sample app where all modules are linked through ZLayer. Should run as is (make sure you have com.h2database:h2 in
* your dependencies). */
object LayeredAppMultipleDatabases extends zio.ZIOAppDefault {

/** Marker trait for the first DB */
trait Db1

/** Marker trait for the second DB */
trait Db2

private val database1: ZLayer[Any, Any, DatabaseT[Db1]] = {
// Fresh calls are required so that the confs and datasource aren't conflated with the other one
val conf = Conf.live("samble-anorm-app-1").fresh
val dbRecoveryConf = conf >>> ZLayer.fromFunction((_: Conf).dbRecovery).fresh
val datasource = conf >>> ConnectionPool.live.fresh
(datasource ++ dbRecoveryConf) >>> DatabaseT[Db1].fromDatasourceAndErrorStrategies
}

private val database2: ZLayer[Any, Any, DatabaseT[Db2]] = {
// Fresh calls are required so that the confs and datasource aren't conflated with the other one
val conf = Conf.live("samble-anorm-app-2").fresh
val dbRecoveryConf = conf >>> ZLayer.fromFunction((_: Conf).dbRecovery).fresh
val datasource = conf >>> ConnectionPool.live.fresh
(datasource ++ dbRecoveryConf) >>> DatabaseT[Db2].fromDatasourceAndErrorStrategies
}

private val personQueries = PersonQueries.live

type AppEnv = DatabaseT[Db1] with DatabaseT[Db2] with PersonQueries
private val appEnv = database1 ++ database2 ++ personQueries

override def run: ZIO[ZIOAppArgs with Scope, Any, Any] =
for {
_ <- Console.printLine("Starting the app")
team <- myApp().provideLayer(appEnv)
_ <- Console.printLine(team.mkString(", "))
} yield ExitCode(0)

/** Main code for the application. Results in a big ZIO depending on the AppEnv. */
def myApp(): ZIO[PersonQueries with DatabaseT[Db2] with DatabaseT[Db1], DbException, List[Person]] = {
val queries1: ZIO[PersonQueries with Connection, DbException, List[Person]] = for {
_ <- Console.printLine("Creating the table").orDie
_ <- PersonQueries.setup
_ <- Console.printLine("Inserting the trio").orDie
_ <- PersonQueries.insert(Person("Buffy", "Summers"))
_ <- PersonQueries.insert(Person("Willow", "Rosenberg"))
_ <- PersonQueries.insert(Person("Alexander", "Harris"))
_ <- Console.printLine("Reading the trio").orDie
trio <- PersonQueries.list
} yield trio

val queries2: ZIO[PersonQueries with Connection, DbException, List[Person]] = for {
_ <- Console.printLine("Creating the table").orDie
_ <- PersonQueries.setup
_ <- Console.printLine("Inserting the mentor").orDie
_ <- PersonQueries.insert(Person("Rupert", "Giles"))
_ <- Console.printLine("Reading the mentor").orDie
mentor <- PersonQueries.list
} yield mentor

val zTrio: ZIO[PersonQueries with DatabaseT[Db1], DbException, List[Person]] = DatabaseT[Db1].transactionOrWiden(queries1)
val zMentor: ZIO[PersonQueries with DatabaseT[Db2], DbException, List[Person]] = DatabaseT[Db2].transactionOrWiden(queries2)

for {
trio <- zTrio
mentor <- zMentor
} yield trio ++ mentor
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package samples.doobie

import io.github.gaelrenoux.tranzactio.{DbException, FailingConnectionSource}
import io.github.gaelrenoux.tranzactio.DbException
import io.github.gaelrenoux.tranzactio.doobie._
import samples.{Conf, ConnectionPool, Person}
import zio._
Expand Down

0 comments on commit b9e104e

Please sign in to comment.