Could not find Quill::Mysql[+SnakeCase] inside ZEnvironment
hepengzheng opened this issue · comments
Hi, I'm a newbie to zio-quill and I could not bootstrap my first app with the following error:
timestamp=2023-01-15T13:48:35.216237Z level=ERROR thread=#zio-fiber-1 message="" cause="Exception in thread "zio-fiber-6,5" java.lang.Error: Defect in zio.ZEnvironment: Could not find Quill::Mysql[+SnakeCase] inside ZEnvironment(Quill$::Mysql[+SnakeCase] -> io.getquill.jdbczio.Quill$Mysql@7519998a)
at org.example.zioserver.respository.CardRepository.layer(CardRepository.scala:43)
at org.example.zioserver.respository.CardRepository.layer(CardRepository.scala:44)
at org.example.zioserver.respository.CardRepository.layer(CardRepository.scala:45)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)
at org.example.zioserver.Zioserver.run(Zioserver.scala:19)"
Dependencies and version
scalaVersion = 3.2.0
sbt.version = 1.8.2
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % "2.0.5",
"dev.zio" %% "zio-json" % "0.4.2",
"dev.zio" %% "zio-http" % "0.0.3",
"dev.zio" %% "zio-logging" % "2.1.7",
"dev.zio" %% "zio-logging-slf4j" % "2.1.7",
"ch.qos.logback" % "logback-classic" % "1.4.5",
"io.getquill" %% "quill-zio" % "4.6.0",
"io.getquill" %% "quill-jdbc-zio" % "4.6.0",
"mysql" % "mysql-connector-java" % "8.0.30",
)
Steps to reproduce the behavior
The model layer:
import io.getquill.*
import io.getquill.jdbczio.Quill
import zio.json.*
import zio.{ZIO, ZLayer}
import java.sql.SQLException
import java.time.LocalDate
import java.util.Date
case class Card(
id: Long,
number: String,
createAt: LocalDate,
UpdateAt: LocalDate,
)
object Card {
implicit val encoder: JsonEncoder[Card] =
DeriveJsonEncoder.gen[Card]
}
class CardRepository(quill: Quill.Mysql[SnakeCase]) {
import quill._
def getCards: ZIO[Any, SQLException, List[Card]] = {
run(query[Card])
}
}
object CardRepository {
def getCards: ZIO[CardRepository, SQLException, List[Card]] = {
ZIO.serviceWithZIO[CardRepository](_.getCards)
}
val layer: ZLayer[Quill.Mysql[SnakeCase], Nothing, CardRepository] = {
ZLayer {
for {
quill <- ZIO.service[Quill.Mysql[SnakeCase]]
} yield new CardRepository(quill)
}
}
}
The http app:
import org.example.zioserver.respository.{Card, CardRepository}
import zio.*
import zio.http.*
import zio.http.model.*
import zio.json._
import java.sql.SQLException
object CardApp {
def apply(): Http[CardService, Throwable, Request, Response] = {
Http.collectZIO[Request] {
case Method.GET -> !! / "cards" =>
CardService.listAddCard.map(_.toJson).map(Response.json(_))
}
}
}
trait CardService {
def listAllCard: Task[List[Card]]
}
object CardService {
def listAddCard: ZIO[CardService, Throwable, List[Card]] = {
ZIO.serviceWithZIO[CardService](_.listAllCard)
}
}
case class CardServiceImpl(cardRepository: CardRepository) extends CardService {
override def listAllCard: Task[List[Card]] = {
cardRepository.getCards.foldCauseZIO(
e => ZIO.logError(s"Failed to get cards $e").as(List()),
cards => ZIO.succeed(cards)
)
}
}
object CardServiceImpl {
val layer: ZLayer[CardRepository, Nothing, CardService] = {
ZLayer {
for {
cardRepository <- ZIO.service[CardRepository]
} yield CardServiceImpl(cardRepository)
}
}
}
The main app:
import io.getquill.*
import io.getquill.jdbczio.Quill
import org.example.zioserver.demo.card.{CardApp, CardServiceImpl}
import org.example.zioserver.demo.counter.CounterApp
import org.example.zioserver.respository.CardRepository
import zio.*
import zio.http.*
import zio.http.model.*
import zio.logging.backend.SLF4J
object Zioserver extends ZIOAppDefault {
val run: ZIO[Any, Throwable, Nothing] = Server.serve(CardApp())
.provide(
Server.live,
ZLayer.succeed(ServerConfig.default.port(8082)),
CardServiceImpl.layer,
CardRepository.layer,
Quill.DataSource.fromPrefix("dbConfig"),
Quill.Mysql.fromNamingStrategy(SnakeCase),
)
}
How should I solve it? Thanks a lot.
@getquill/maintainers
I updated my code as such and it works:
class CardRepository(dataSource: DataSource) {
import org.example.zioserver.QuillContext._
def getCards: ZIO[Any, SQLException, List[Card]] = {
run(query[Card]).provideEnvironment(ZEnvironment(dataSource))
}
}
object CardRepository {
def getCards: ZIO[CardRepository, SQLException, List[Card]] = {
ZIO.serviceWithZIO[CardRepository](_.getCards)
}
val layer: ZLayer[DataSource, Nothing, CardRepository] = {
ZLayer.fromFunction(new CardRepository(_))
}
}
the QuillContext
:
object QuillContext extends MysqlZioJdbcContext(SnakeCase) {
val dataSourceLayer: ZLayer[Any, Nothing, DataSource] =
Quill.DataSource.fromPrefix("dbConfig").orDie
}
object Zioserver extends ZIOAppDefault {
val run: ZIO[Any, Throwable, Nothing] = Server.serve(CardApp())
.provide(
Server.live,
ZLayer.succeed(ServerConfig.default.port(8082)),
CardServiceImpl.layer,
CardRepository.layer,
QuillContext.dataSourceLayer,
)
}
but what's wrong with my original code?
@PengzhengHe This Quill.Mysql.fromNamingStrategy(SnakeCase)
is not something you want to inject. It's some macro stuff so I guess it doesn't work when injecting at runtime and should be known at compile time. That's probably why you need to use this QuillContext
object ;)
@guizmaii Hi, thanks for answering me. I wrote my original code based on the example on the quill home page: https://getquill.io/#docs
case class Person(name: String, age: Int)
class DataService(quill: Quill.Postgres[SnakeCase]) {
import quill._
def getPeople: ZIO[Any, SQLException, List[Person]] = run(query[Person])
}
object DataService {
def getPeople: ZIO[DataService, SQLException, List[Person]] =
ZIO.serviceWithZIO[DataService](_.getPeople)
val live = ZLayer.fromFunction(new DataService(_))
}
object Main extends ZIOAppDefault {
override def run = {
DataService.getPeople
.provide(
DataService.live,
Quill.Postgres.fromNamingStrategy(SnakeCase),
Quill.DataSource.fromPrefix("myDatabaseConfig")
)
.debug("Results")
.exitCode
}
}
It seems like the only difference is I was using Mysql.
Interesting. We're using PG, and we're using the similar object Context ...
approach you used to make things work.
We've never tried to inject it.
We've never tried to inject it.
did you mean the Quill.Mysql.fromNamingStrategy(SnakeCase)
thing?
I am not sure whether the code example on the home page is outdated or just wrong. But with the QuillContext
object I have to write a .provideEnvironment(ZEnvironment(dataSource))
for every sql it's tedious. ;)
did you mean the Quill.Mysql.fromNamingStrategy(SnakeCase) thing?
Yes
Thanks.