Reputation: 11
I use ZLayer and Sttp Client(async to create simple http requester application but I found type mismatch error that I couldn't solve it. Can anyone tell me why I'm getting the type mismatch error?
I use these versions of scala & libraries.
java -> 8.282.08.1-amzn
scala -> s.13.5
dev.zio -> 1.0.7
com.softwaremill.sttp.client3 -> 3.3.0
type mismatch;
found : zio.ZLayer[sttp.client3.asynchttpclient.zio.SttpClient,Nothing,ZlayerAndSttp.HttpBin]
(which expands to) zio.ZLayer[zio.Has[sttp.client3.SttpBackend[zio.Task,sttp.capabilities.zio.ZioStreams with sttp.capabilities.WebSockets]],Nothing,zio.Has[ZlayerAndSttp.HttpBin.Service]]
required: zio.ZLayer[ZlayerAndSttp.HttpBin,?,?]
(which expands to) zio.ZLayer[zio.Has[ZlayerAndSttp.HttpBin.Service],?,?]
program.provideCustomLayer((AsyncHttpClientZioBackend.layer() >>> HttpBin.live) >>> HttpBin.live)
Here's the whole code
import zio._
import sttp.client3._
import sttp.client3.circe._
import sttp.client3.asynchttpclient.zio._
import io.circe.generic.auto._
import zio.console.Console
object ZlayerAndSttp extends App {
case class HttpBinResponse(origin: String, headers: Map[String, String])
type HttpBin = Has[HttpBin.Service]
object HttpBin {
trait Service {
def sendRequest: ZIO[HttpBin with SttpClient, Throwable, HttpBinResponse]
}
val live: ZLayer[SttpClient, Nothing, HttpBin] = ZLayer.succeed(new Service {
override def sendRequest: ZIO[HttpBin with SttpClient, Throwable, HttpBinResponse] = {
val request = basicRequest
.get(uri"https://httpbin.org/get")
.response(asJson[HttpBinResponse])
sendR(request).map(_.body).absolve.map(res => HttpBinResponse(res.origin, res.headers))
}
})
def sendRequest: ZIO[HttpBin with SttpClient, Throwable, HttpBinResponse] = ZIO.accessM(_.get.sendRequest)
}
val request = basicRequest
.get(uri"https://httpbin.org/get")
.response(asJson[HttpBinResponse])
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
val program = for {
result <- HttpBin.sendRequest
_ <- console.putStrLn(s"${result.origin}, ${result.headers}")
} yield ()
program.provideCustomLayer((AsyncHttpClientZioBackend.layer() >>> HttpBin.live) >>> HttpBin.live) // type mismatch
.exitCode
// ↓these lines of code run with no errors but I can't understand why
// val program: ZIO[Console with SttpClient, Throwable, Unit] = for {
// response <- send(request)
// _ <- console.putStrLn(s"${response.body.toString}")
// } yield ()
// program.provideCustomLayer(AsyncHttpClientZioBackend.layer()).exitCode
}
}
Upvotes: 1
Views: 408
Reputation: 18663
You seem to be making your life a bit more complicated than needed.
What the end code looks like depends on what you want to achieve here. If you are trying to hide the use of Sttp
behind the HttpBin
interface, then your layer definition should instead look like:
val live: ZLayer[SttpClient, Nothing, HttpBin] =
(for {
client <- ZIO.environment[SttpClient]
} yield new Service {
override def sendRequest: ZIO[Any, Throwable, HttpBinResponse] = {
val request = basicRequest
.get(uri"https://httpbin.org/get")
.response(asJson[HttpBinResponse])
sendR(request)
.map(_.body)
.absolve
.map(res => HttpBinResponse(res.origin, res.headers))
.provide(client)
}
}).toLayer
Then your accessor method becomes:
def sendRequest: ZIO[HttpBin, Throwable, HttpBinResponse] =
ZIO.accessM(_.get.sendRequest)
And you can use it with:
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
val program = for {
result <- HttpBin.sendRequest
_ <- console.putStrLn(s"${result.origin}, ${result.headers}")
} yield ()
program
.provideCustomLayer(AsyncHttpClientZioBackend.layer() >>> HttpBin.live)
.exitCode
The thing to note here is that with layer composition I am using just the vertical operator because HttpBin.live
depends on a layer with SttpClient
but we are "hiding" that fact from the calling method so you could create a test
variant of HttpBin
that didn't require Sttp if needed.
If you don't need the information hiding you can instead remove the intermediate layer altogether and just treat your sendRequest
as a stand alone method.
object HttpBin {
def sendRequest: ZIO[SttpClient, Throwable, HttpBinResponse] = {
val request = basicRequest
.get(uri"https://httpbin.org/get")
.response(asJson[HttpBinResponse])
sendR(request)
.map(_.body)
.absolve
.map(res => HttpBinResponse(res.origin, res.headers))
}
Then you can just call this method and all you need to do is provide the SttpClient
layer.
Upvotes: 0