diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala new file mode 100644 index 0000000..828fc6d --- /dev/null +++ b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala @@ -0,0 +1,14 @@ +package works.iterative.tapir + +import zio.* +import sttp.tapir.PublicEndpoint + +opaque type Client[I, E, O] = I => IO[E, O] + +object Client: + def apply[I, E, O](f: I => IO[E, O]): Client[I, E, O] = f + + extension [I, E, O](f: Client[I, E, O]) def apply(i: I): IO[E, O] = f(i) + +trait ClientEndpointFactory: + def make[I, E, O](endpoint: PublicEndpoint[I, E, O, Any]): Client[I, E, O] diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala new file mode 100644 index 0000000..828fc6d --- /dev/null +++ b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala @@ -0,0 +1,14 @@ +package works.iterative.tapir + +import zio.* +import sttp.tapir.PublicEndpoint + +opaque type Client[I, E, O] = I => IO[E, O] + +object Client: + def apply[I, E, O](f: I => IO[E, O]): Client[I, E, O] = f + + extension [I, E, O](f: Client[I, E, O]) def apply(i: I): IO[E, O] = f(i) + +trait ClientEndpointFactory: + def make[I, E, O](endpoint: PublicEndpoint[I, E, O, Any]): Client[I, E, O] diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala b/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala new file mode 100644 index 0000000..208b904 --- /dev/null +++ b/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala @@ -0,0 +1,50 @@ +package works.iterative.tapir + +import zio.* +import sttp.tapir.PublicEndpoint +import sttp.tapir.client.sttp.WebSocketToPipe + +class LiveClientEndpointFactory(using + baseUri: BaseUri, + backend: CustomTapir.Backend +) extends ClientEndpointFactory + with CustomTapir: + + override def make[I, E, O]( + endpoint: PublicEndpoint[I, E, O, Any] + ): Client[I, E, O] = mkClient(endpoint) + + private def mkClient[I, E, O]( + endpoint: PublicEndpoint[I, E, O, Any] + )(using + baseUri: BaseUri, + backend: Backend, + wsToPipe: WebSocketToPipe[Any] + ): Client[I, E, O] = Client((input: I) => + val req = toRequest(endpoint, baseUri.toUri) + val fetch = req(input).followRedirects(false).send(backend) + for + resp <- fetch.orDie + body <- resp.body match + case DecodeResult.Value(v) => ZIO.succeed(v) + case err: DecodeResult.Failure => + ZIO.die( + new RuntimeException( + s"Unexpected response status: ${resp.code} ${resp.statusText} - ${err}" + ) + ) + v <- ZIO.fromEither(body) + yield v + ) + +object LiveClientEndpointFactory: + val layer: URLayer[BaseUri & CustomTapir.Backend, ClientEndpointFactory] = + ZLayer { + for + given BaseUri <- ZIO.service[BaseUri] + given CustomTapir.Backend <- ZIO.service[CustomTapir.Backend] + yield LiveClientEndpointFactory() + } + + val default: ZLayer[BaseUri, Throwable, ClientEndpointFactory] = + CustomTapir.clientLayer >>> layer