From 33779e1c74ca1a3089381c8e6b6a8c95870a2f3c Mon Sep 17 00:00:00 2001 From: kciesielski Date: Tue, 5 Dec 2023 12:54:19 +0100 Subject: [PATCH 1/2] Drop zio1 support --- build.sbt | 105 ------------ .../zio1/TapirSttpClientZioWebSockets.scala | 10 -- .../sttp/ws/zio1/WebSocketToZioPipe.scala | 93 ----------- .../tapir/client/sttp/ws/zio1/package.scala | 3 - .../sttp/SttpClientWebSocketZioTests.scala | 29 ---- .../client/sttp/SttpClientZioTests.scala | 55 ------- .../tapir/internal/ShowPathTemplate.scala | 3 +- doc/client/sttp.md | 10 +- doc/server/armeria.md | 3 - doc/server/vertx.md | 3 - doc/server/zio-http4s.md | 8 +- doc/server/ziohttp.md | 3 +- doc/stability.md | 7 +- generated-doc/out/client/sttp.md | 10 +- generated-doc/out/server/vertx.md | 4 +- .../sttp/tapir/ztapir/RIOMonadError.scala | 16 -- .../tapir/ztapir/ZPartialServerEndpoint.scala | 82 ---------- .../main/scala/sttp/tapir/ztapir/ZTapir.scala | 48 ------ .../tapir/ztapir/ZioServerSentEvents.scala | 30 ---- .../scala/sttp/tapir/ztapir/package.scala | 3 - .../scala/sttp/tapir/ztapir/TestError.scala | 15 -- .../scala/sttp/tapir/ztapir/ZTapirTest.scala | 131 --------------- .../ztapir/ZioServerSentEventsTest.scala | 95 ----------- .../ztapir/instances/TestMonadError.scala | 23 --- .../sttp/tapir/json/zio/TapirJsonZio.scala | 47 ------ .../scala/sttp/tapir/json/zio/package.scala | 3 - .../tapir/json/zio/TapirJsonZioTest.scala | 95 ----------- project/Versions.scala | 4 - .../zio/ArmeriaZioServerInterpreter.scala | 27 ---- .../armeria/zio/ArmeriaZioServerOptions.scala | 74 --------- .../armeria/zio/RIOMonadAsyncError.scala | 37 ----- .../server/armeria/zio/TapirZioService.scala | 92 ----------- .../armeria/zio/ArmeriaZioServerTest.scala | 24 --- .../zio/ArmeriaZioTestServerInterpreter.scala | 19 --- .../server/http4s/ztapir/ConvertStreams.scala | 151 ------------------ .../ztapir/ZHttp4sServerInterpreter.scala | 66 -------- .../tapir/server/http4s/ztapir/package.scala | 16 -- .../server/http4s/ztapir/ZEndpointTest.scala | 27 ---- .../http4s/ztapir/ZHttp4sServerStubTest.scala | 36 ----- .../http4s/ztapir/ZHttp4sServerTest.scala | 63 -------- .../ztapir/ZHttp4sTestServerInterpreter.scala | 63 -------- .../server/ziohttp/ZioHttpBodyListener.scala | 20 --- .../server/ziohttp/ZioHttpInterpreter.scala | 80 ---------- .../server/ziohttp/ZioHttpRequestBody.scala | 48 ------ .../server/ziohttp/ZioHttpServerOptions.scala | 39 ----- .../server/ziohttp/ZioHttpServerRequest.scala | 30 ---- .../ziohttp/ZioHttpToResponseBody.scala | 61 ------- .../sttp/tapir/server/ziohttp/ziohttp.scala | 8 - .../ziohttp/ZioHttpCompositionTest.scala | 37 ----- .../ziohttp/ZioHttpServerStubTest.scala | 25 --- .../server/ziohttp/ZioHttpServerTest.scala | 63 -------- .../ZioHttpTestServerInterpreter.scala | 39 ----- 52 files changed, 16 insertions(+), 2067 deletions(-) delete mode 100644 client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/TapirSttpClientZioWebSockets.scala delete mode 100644 client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/WebSocketToZioPipe.scala delete mode 100644 client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/package.scala delete mode 100644 client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientWebSocketZioTests.scala delete mode 100644 client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientZioTests.scala delete mode 100644 integrations/zio1/src/main/scala/sttp/tapir/ztapir/RIOMonadError.scala delete mode 100644 integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZPartialServerEndpoint.scala delete mode 100644 integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZTapir.scala delete mode 100644 integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZioServerSentEvents.scala delete mode 100644 integrations/zio1/src/main/scala/sttp/tapir/ztapir/package.scala delete mode 100644 integrations/zio1/src/test/scala/sttp/tapir/ztapir/TestError.scala delete mode 100644 integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZTapirTest.scala delete mode 100644 integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZioServerSentEventsTest.scala delete mode 100644 integrations/zio1/src/test/scala/sttp/tapir/ztapir/instances/TestMonadError.scala delete mode 100644 json/zio1/src/main/scala/sttp/tapir/json/zio/TapirJsonZio.scala delete mode 100644 json/zio1/src/main/scala/sttp/tapir/json/zio/package.scala delete mode 100644 json/zio1/src/test/scalajvm/sttp/tapir/json/zio/TapirJsonZioTest.scala delete mode 100644 server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerInterpreter.scala delete mode 100644 server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerOptions.scala delete mode 100644 server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/RIOMonadAsyncError.scala delete mode 100644 server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/TapirZioService.scala delete mode 100644 server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerTest.scala delete mode 100644 server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioTestServerInterpreter.scala delete mode 100644 server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ConvertStreams.scala delete mode 100644 server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerInterpreter.scala delete mode 100644 server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/package.scala delete mode 100644 server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZEndpointTest.scala delete mode 100644 server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerStubTest.scala delete mode 100644 server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerTest.scala delete mode 100644 server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpBodyListener.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpInterpreter.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpRequestBody.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerOptions.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerRequest.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpToResponseBody.scala delete mode 100644 server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ziohttp.scala delete mode 100644 server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpCompositionTest.scala delete mode 100644 server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerStubTest.scala delete mode 100644 server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerTest.scala delete mode 100644 server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpTestServerInterpreter.scala diff --git a/build.sbt b/build.sbt index 7eb87d79b6..a3d0122ac2 100644 --- a/build.sbt +++ b/build.sbt @@ -166,7 +166,6 @@ lazy val rawAllAggregates = core.projectRefs ++ enumeratum.projectRefs ++ refined.projectRefs ++ iron.projectRefs ++ - zio1.projectRefs ++ zio.projectRefs ++ newtype.projectRefs ++ monixNewtype.projectRefs ++ @@ -185,7 +184,6 @@ lazy val rawAllAggregates = core.projectRefs ++ sprayJson.projectRefs ++ uPickleJson.projectRefs ++ tethysJson.projectRefs ++ - zio1Json.projectRefs ++ zioJson.projectRefs ++ protobuf.projectRefs ++ pbDirectProtobuf.projectRefs ++ @@ -207,9 +205,7 @@ lazy val rawAllAggregates = core.projectRefs ++ armeriaServer.projectRefs ++ armeriaServerCats.projectRefs ++ armeriaServerZio.projectRefs ++ - armeriaServerZio1.projectRefs ++ http4sServer.projectRefs ++ - http4sServerZio1.projectRefs ++ http4sServerZio.projectRefs ++ sttpStubServer.projectRefs ++ sttpMockServer.projectRefs ++ @@ -220,14 +216,12 @@ lazy val rawAllAggregates = core.projectRefs ++ vertxServer.projectRefs ++ vertxServerCats.projectRefs ++ vertxServerZio.projectRefs ++ - vertxServerZio1.projectRefs ++ jdkhttpServer.projectRefs ++ nettyServer.projectRefs ++ nettyServerLoom.projectRefs ++ nettyServerCats.projectRefs ++ nettyServerZio.projectRefs ++ nimaServer.projectRefs ++ - zio1HttpServer.projectRefs ++ zioHttpServer.projectRefs ++ awsLambdaCore.projectRefs ++ awsLambdaCatsEffect.projectRefs ++ @@ -240,7 +234,6 @@ lazy val rawAllAggregates = core.projectRefs ++ clientCore.projectRefs ++ http4sClient.projectRefs ++ sttpClient.projectRefs ++ - sttpClientWsZio1.projectRefs ++ playClient.projectRefs ++ play29Client.projectRefs ++ tests.projectRefs ++ @@ -679,26 +672,6 @@ lazy val iron: ProjectMatrix = (projectMatrix in file("integrations/iron")) ) .dependsOn(core) -lazy val zio1: ProjectMatrix = (projectMatrix in file("integrations/zio1")) - .settings(commonSettings) - .settings( - name := "tapir-zio1", - testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"), - libraryDependencies ++= Seq( - "dev.zio" %%% "zio" % Versions.zio1, - "dev.zio" %%% "zio-streams" % Versions.zio1, - "dev.zio" %%% "zio-test" % Versions.zio1 % Test, - "dev.zio" %%% "zio-test-sbt" % Versions.zio1 % Test, - "com.softwaremill.sttp.shared" %%% "zio1" % Versions.sttpShared - ) - ) - .jvmPlatform(scalaVersions = scala2And3Versions) - .jsPlatform( - scalaVersions = scala2And3Versions, - settings = commonJsSettings - ) - .dependsOn(core, serverCore % Test) - lazy val zio: ProjectMatrix = (projectMatrix in file("integrations/zio")) .settings(commonSettings) .settings( @@ -957,22 +930,6 @@ lazy val jsoniterScala: ProjectMatrix = (projectMatrix in file("json/jsoniter")) ) .dependsOn(core) -lazy val zio1Json: ProjectMatrix = (projectMatrix in file("json/zio1")) - .settings(commonSettings) - .settings( - name := "tapir-json-zio1", - libraryDependencies ++= Seq( - "dev.zio" %% "zio-json" % Versions.zio1Json, - scalaTest.value % Test - ) - ) - .jvmPlatform(scalaVersions = scala2And3Versions) - .jsPlatform( - scalaVersions = scala2Versions, - settings = commonJsSettings - ) - .dependsOn(core) - lazy val zioJson: ProjectMatrix = (projectMatrix in file("json/zio")) .settings(commonSettings) .settings( @@ -1330,18 +1287,6 @@ lazy val armeriaServerZio: ProjectMatrix = .jvmPlatform(scalaVersions = scala2And3Versions) .dependsOn(armeriaServer % CompileAndTest, zio, serverTests % Test) -lazy val armeriaServerZio1: ProjectMatrix = - (projectMatrix in file("server/armeria-server/zio1")) - .settings(commonJvmSettings) - .settings( - name := "tapir-armeria-server-zio1", - libraryDependencies ++= Seq( - "dev.zio" %% "zio-interop-reactivestreams" % Versions.zio1InteropReactiveStreams - ) - ) - .jvmPlatform(scalaVersions = scala2And3Versions) - .dependsOn(armeriaServer % CompileAndTest, zio1, serverTests % Test) - lazy val http4sServer: ProjectMatrix = (projectMatrix in file("server/http4s-server")) .settings(commonJvmSettings) .settings( @@ -1369,18 +1314,6 @@ lazy val http4sServer2_12 = http4sServer.jvm(scala2_12).dependsOn(serverTests.jv lazy val http4sServer2_13 = http4sServer.jvm(scala2_13).dependsOn(serverTests.jvm(scala2_13) % Test) lazy val http4sServer3 = http4sServer.jvm(scala3).dependsOn(serverTests.jvm(scala3) % Test) -lazy val http4sServerZio1: ProjectMatrix = (projectMatrix in file("server/http4s-server/zio1")) - .settings(commonJvmSettings) - .settings( - name := "tapir-http4s-server-zio1", - libraryDependencies ++= Seq( - "dev.zio" %% "zio-interop-cats" % Versions.zio1InteropCats, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test - ) - ) - .jvmPlatform(scalaVersions = scala2And3Versions) - .dependsOn(zio1, http4sServer, serverTests % Test) - lazy val http4sServerZio: ProjectMatrix = (projectMatrix in file("server/http4s-server/zio")) .settings(commonJvmSettings) .settings( @@ -1588,27 +1521,6 @@ lazy val vertxServerZio: ProjectMatrix = (projectMatrix in file("server/vertx-se .jvmPlatform(scalaVersions = scala2And3Versions) .dependsOn(serverCore, vertxServer % CompileAndTest, zio, serverTests % Test) -lazy val vertxServerZio1: ProjectMatrix = (projectMatrix in file("server/vertx-server/zio1")) - .settings(commonJvmSettings) - .settings( - name := "tapir-vertx-server-zio1", - libraryDependencies ++= Seq( - "dev.zio" %% "zio-interop-cats" % Versions.zio1InteropCats % Test - ) - ) - .settings(disableScaladocSettingsWhenScala3) - .jvmPlatform(scalaVersions = scala2And3Versions) - .dependsOn(serverCore, vertxServer % CompileAndTest, zio1, serverTests % Test) - -lazy val zio1HttpServer: ProjectMatrix = (projectMatrix in file("server/zio1-http-server")) - .settings(commonJvmSettings) - .settings( - name := "tapir-zio1-http-server", - libraryDependencies ++= Seq("dev.zio" %% "zio-interop-cats" % Versions.zio1InteropCats % Test, "io.d11" %% "zhttp" % "1.0.0.0-RC29") - ) - .jvmPlatform(scalaVersions = scala2And3Versions) - .dependsOn(serverCore, zio1, serverTests % Test) - lazy val zioHttpServer: ProjectMatrix = (projectMatrix in file("server/zio-http-server")) .settings(commonJvmSettings) .settings( @@ -2013,22 +1925,6 @@ lazy val sttpClient: ProjectMatrix = (projectMatrix in file("client/sttp-client" ) .dependsOn(clientCore, clientTests % Test) -lazy val sttpClientWsZio1: ProjectMatrix = (projectMatrix in file("client/sttp-client-ws-zio1")) - .settings(clientTestServerSettings) - .settings( - name := "tapir-sttp-client-ws-zio1" - ) - .jvmPlatform( - scalaVersions = scala2And3Versions, - settings = commonJvmSettings ++ Seq( - libraryDependencies ++= Seq( - "com.softwaremill.sttp.client3" %% "zio1" % Versions.sttp % Test, - "com.softwaremill.sttp.shared" %% "zio1" % Versions.sttpShared - ) - ) - ) - .dependsOn(sttpClient, clientTests % Test) - lazy val playClient: ProjectMatrix = (projectMatrix in file("client/play-client")) .settings(clientTestServerSettings) .settings(commonSettings) @@ -2243,7 +2139,6 @@ lazy val documentation: ProjectMatrix = (projectMatrix in file("generated-doc")) armeriaServer, armeriaServerCats, armeriaServerZio, - armeriaServerZio1, jdkhttpServer, circeJson, enumeratum, diff --git a/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/TapirSttpClientZioWebSockets.scala b/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/TapirSttpClientZioWebSockets.scala deleted file mode 100644 index 8affbb66ea..0000000000 --- a/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/TapirSttpClientZioWebSockets.scala +++ /dev/null @@ -1,10 +0,0 @@ -package sttp.tapir.client.sttp.ws.zio1 - -import sttp.capabilities.WebSockets -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.client.sttp.WebSocketToPipe - -trait TapirSttpClientZioWebSockets { - implicit val webSocketsSupportedForZioStreams: WebSocketToPipe[ZioStreams with WebSockets] = - new WebSocketToZioPipe[ZioStreams with WebSockets] -} diff --git a/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/WebSocketToZioPipe.scala b/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/WebSocketToZioPipe.scala deleted file mode 100644 index 8163ccc7ef..0000000000 --- a/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/WebSocketToZioPipe.scala +++ /dev/null @@ -1,93 +0,0 @@ -package sttp.tapir.client.sttp.ws.zio1 - -import sttp.capabilities.WebSockets -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.client.sttp.WebSocketToPipe -import sttp.tapir.model.WebSocketFrameDecodeFailure -import sttp.tapir.{DecodeResult, WebSocketBodyOutput} -import sttp.ws.{WebSocket, WebSocketFrame} -import zio.Task -import zio.stream.Stream - -import scala.reflect.ClassTag - -class WebSocketToZioPipe[R <: ZioStreams with WebSockets] extends WebSocketToPipe[R] { - override type S = ZioStreams - override type F[X] = Task[X] - - override def apply[REQ, RESP](s: Any)(ws: WebSocket[F], o: WebSocketBodyOutput[Any, REQ, RESP, _, ZioStreams]): Any = { - (in: Stream[Throwable, REQ]) => - val sends = in - .map(o.requests.encode) - .mapM(ws.send(_, isContinuation = false)) // TODO support fragmented frames - - def decode(frame: WebSocketFrame): F[Either[Unit, Option[RESP]]] = - o.responses.decode(frame) match { - case failure: DecodeResult.Failure => - Task.fail(new WebSocketFrameDecodeFailure(frame, failure)) - case DecodeResult.Value(v) => - Task.right[Option[RESP]](Some(v)) - } - - def raiseBadAccumulator[T](acc: WebSocketFrame, current: WebSocketFrame): F[T] = - Task.fail( - new WebSocketFrameDecodeFailure( - current, - DecodeResult.Error( - "Bad frame sequence", - new Exception( - s"Invalid accumulator frame: $acc, it can't be concatenated with $current" - ) - ) - ) - ) - - def concatOrDecode[A <: WebSocketFrame: ClassTag]( - acc: Option[WebSocketFrame], - frame: A, - last: Boolean - )(f: (A, A) => A): F[(Option[WebSocketFrame], Either[Unit, Option[RESP]])] = - if (last) (acc match { - case None => decode(frame) - case Some(x: A) => decode(f(x, frame)) - case Some(bad) => raiseBadAccumulator(bad, frame) - }).map(None -> _) - else - (acc match { - case None => Task.some(frame) - case Some(x: A) => Task.some(f(x, frame)) - case Some(bad) => raiseBadAccumulator(bad, frame) - }).map(acc => acc -> Left(())) - - val receives = Stream - .repeatEffect(ws.receive()) - .mapAccumM[Any, Throwable, Option[WebSocketFrame], Either[Unit, Option[RESP]]]( - None - ) { // left - ignore; right - close or response - case (acc, _: WebSocketFrame.Close) if !o.decodeCloseResponses => - Task.succeed(acc -> Right(None)) - case (acc, _: WebSocketFrame.Pong) if o.ignorePong => - Task.succeed(acc -> Left(())) - case (acc, WebSocketFrame.Ping(p)) if o.autoPongOnPing => - ws.send(WebSocketFrame.Pong(p)).as(acc -> Left(())) - case (prev, frame @ WebSocketFrame.Text(_, last, _)) => - concatOrDecode(prev, frame, last)((l, r) => r.copy(payload = l.payload + r.payload)) - case (prev, frame @ WebSocketFrame.Binary(_, last, _)) => - concatOrDecode(prev, frame, last)((l, r) => r.copy(payload = l.payload ++ r.payload)) - case (_, frame) => - Task.fail( - new WebSocketFrameDecodeFailure( - frame, - DecodeResult.Error( - "Unrecognised frame type", - new Exception(s"Unrecognised frame type: ${frame.getClass}") - ) - ) - ) - } - .collectRight - .collectWhileSome - - sends.drain.merge(receives) - } -} diff --git a/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/package.scala b/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/package.scala deleted file mode 100644 index a515deb556..0000000000 --- a/client/sttp-client-ws-zio1/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio1/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package sttp.tapir.client.sttp.ws - -package object zio1 extends TapirSttpClientZioWebSockets diff --git a/client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientWebSocketZioTests.scala b/client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientWebSocketZioTests.scala deleted file mode 100644 index 50b8cbc38c..0000000000 --- a/client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientWebSocketZioTests.scala +++ /dev/null @@ -1,29 +0,0 @@ -package sttp.tapir.client.sttp - -import cats.effect.IO -import sttp.capabilities.WebSockets -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.client.sttp.ws.zio1._ -import sttp.tapir.client.tests.ClientWebSocketTests -import zio.stream.Stream - -class SttpClientWebSocketZioTests extends SttpClientZioTests[WebSockets with ZioStreams] with ClientWebSocketTests[ZioStreams] { - override val streams: ZioStreams = ZioStreams - override def wsToPipe: WebSocketToPipe[WebSockets with ZioStreams] = implicitly - - override def sendAndReceiveLimited[A, B]( - p: Stream[Throwable, A] => Stream[Throwable, B], - receiveCount: Port, - as: List[A] - ): IO[List[B]] = IO.fromFuture( - IO.delay { - zio.Runtime.default - .unsafeRunToFuture( - Stream(as: _*).via(p).take(receiveCount).runCollect.map(_.toList) - ) - .future - } - ) - - webSocketTests() -} diff --git a/client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientZioTests.scala b/client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientZioTests.scala deleted file mode 100644 index 4b17558fdb..0000000000 --- a/client/sttp-client-ws-zio1/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientZioTests.scala +++ /dev/null @@ -1,55 +0,0 @@ -package sttp.tapir.client.sttp - -import cats.effect.IO -import sttp.capabilities.WebSockets -import sttp.capabilities.zio.ZioStreams -import sttp.client3._ -import sttp.client3.httpclient.zio.HttpClientZioBackend -import sttp.tapir.client.tests.ClientTests -import sttp.tapir.{DecodeResult, Endpoint} -import zio.Task - -abstract class SttpClientZioTests[R >: WebSockets with ZioStreams] extends ClientTests[R] { - val backend: SttpBackend[Task, R] = zio.Runtime.default.unsafeRun(HttpClientZioBackend()) - def wsToPipe: WebSocketToPipe[R] - - override def send[A, I, E, O]( - e: Endpoint[A, I, E, O, R], - port: Port, - securityArgs: A, - args: I, - scheme: String = "http" - ): IO[Either[E, O]] = - IO.fromFuture(IO.delay { - implicit val wst: WebSocketToPipe[R] = wsToPipe - val send = SttpClientInterpreter() - .toSecureRequestThrowDecodeFailures(e, Some(uri"$scheme://localhost:$port")) - .apply(securityArgs) - .apply(args) - .send(backend) - .map(_.body) - zio.Runtime.default.unsafeRunToFuture(send).future - }) - - override def safeSend[A, I, E, O]( - e: Endpoint[A, I, E, O, R], - port: Port, - securityArgs: A, - args: I - ): IO[DecodeResult[Either[E, O]]] = - IO.fromFuture(IO.delay { - implicit val wst: WebSocketToPipe[R] = wsToPipe - val send = SttpClientInterpreter() - .toSecureRequest(e, Some(uri"http://localhost:$port")) - .apply(securityArgs) - .apply(args) - .send(backend) - .map(_.body) - zio.Runtime.default.unsafeRunToFuture(send).future - }) - - override protected def afterAll(): Unit = { - zio.Runtime.default.unsafeRun(backend.close()) - super.afterAll() - } -} diff --git a/core/src/main/scala/sttp/tapir/internal/ShowPathTemplate.scala b/core/src/main/scala/sttp/tapir/internal/ShowPathTemplate.scala index 10d103dbc6..a2bfd1a836 100644 --- a/core/src/main/scala/sttp/tapir/internal/ShowPathTemplate.scala +++ b/core/src/main/scala/sttp/tapir/internal/ShowPathTemplate.scala @@ -15,7 +15,8 @@ private[tapir] object ShowPathTemplate { includeAuth: Boolean, showNoPathAs: String, showPathsAs: Option[String], - showQueryParamsAs: Option[String] + showQueryParamsAs: Option[String], + extraPara: Option[Int] = Some(3) ): String = { val inputs = e.securityInput.and(e.input).asVectorOfBasicInputs(includeAuth) diff --git a/doc/client/sttp.md b/doc/client/sttp.md index 5e36c3f978..a877301761 100644 --- a/doc/client/sttp.md +++ b/doc/client/sttp.md @@ -68,13 +68,11 @@ The required imports are as follows: ```scala import sttp.tapir.client.sttp.ws.akkahttp._ // for akka-streams import sttp.tapir.client.sttp.ws.fs2._ // for fs2 -import sttp.tapir.client.sttp.ws.zio._ // for zio 2.x -import sttp.tapir.client.sttp.ws.zio1._ // for zio 1.x +import sttp.tapir.client.sttp.ws.zio._ // for zio ``` -No additional dependencies are needed (except for zio1, which needs the `tapir-sttp-client-ws-zio1` dependency), as -both of the above implementations are included in the main interpreter, with dependencies on akka-streams, fs2 and zio -being marked as optional (hence these are not transitive). +No additional dependencies are needed, as both of the above implementations are included in the main interpreter, +with dependencies on akka-streams, fs2 and zio being marked as optional (hence these are not transitive). ## Overwriting the response specification @@ -117,4 +115,4 @@ There are limitations existing on some clients that prevent the description gene For security reasons the [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) header is not accessible from frontend JavaScript code. It means that any endpoint containing a `.out(setCookie("token"))` will fail to be decoded on the client side when using Fetch. -A solution is to use the `setCookieOpt` function instead an let the browser do its job when dealing with cookies. \ No newline at end of file +A solution is to use the `setCookieOpt` function instead an let the browser do its job when dealing with cookies. diff --git a/doc/server/armeria.md b/doc/server/armeria.md index 35927e6bd0..21994dcdcd 100644 --- a/doc/server/armeria.md +++ b/doc/server/armeria.md @@ -155,10 +155,7 @@ val tapirService = ArmeriaCatsServerInterpreter(dispatcher).toService(streamingR Add the following dependency ```scala -// for zio 2: "com.softwaremill.sttp.tapir" %% "tapir-armeria-server-zio" % "@VERSION@" -// for zio 1: -"com.softwaremill.sttp.tapir" %% "tapir-armeria-server-zio1" % "@VERSION@" ``` to use this interpreter with ZIO. diff --git a/doc/server/vertx.md b/doc/server/vertx.md index 8b890d9294..7bd65d41bc 100644 --- a/doc/server/vertx.md +++ b/doc/server/vertx.md @@ -145,10 +145,7 @@ val attach = VertxCatsServerInterpreter(dispatcher).route(streamedResponse.serve Add the following dependency ```scala -// for zio2: "com.softwaremill.sttp.tapir" %% "tapir-vertx-server-zio" % "@VERSION@" -// for zio1: -"com.softwaremill.sttp.tapir" %% "tapir-vertx-server-zio1" % "@VERSION@" ``` to use this interpreter with ZIO. diff --git a/doc/server/zio-http4s.md b/doc/server/zio-http4s.md index 385ada1ad8..9d4bd6a234 100644 --- a/doc/server/zio-http4s.md +++ b/doc/server/zio-http4s.md @@ -4,8 +4,7 @@ The `tapir-zio` module defines type aliases and extension methods which make it [ZIO](https://zio.dev) and tapir. Moreover, `tapir-zio-http4s-server` contains an interpreter useful when exposing the endpoints using the [http4s](https://http4s.org) server. -The `*-zio` modules depend on ZIO 2.x. For ZIO 1.x support, use modules with the `*-zio1` suffix. - +The `*-zio` modules depend on ZIO 2.x. You'll need the following dependency for the `ZServerEndpoint` type alias and helper classes: ```scala @@ -15,10 +14,7 @@ You'll need the following dependency for the `ZServerEndpoint` type alias and he or just add the zio-http4s integration which already depends on `tapir-zio`: ```scala -// for zio 2: "com.softwaremill.sttp.tapir" %% "tapir-http4s-server-zio" % "@VERSION@" -// for zio 1: -"com.softwaremill.sttp.tapir" %% "tapir-http4s-server-zio1" % "@VERSION@" ``` Next, instead of the usual `import sttp.tapir._`, you should import (or extend the `ZTapir` trait, see [MyTapir](../mytapir.md)): @@ -186,4 +182,4 @@ val routes: HttpRoutes[Task] = ## Examples -There's a couple of [examples](../examples.md) of using the ZIO integration available.\ \ No newline at end of file +There's a couple of [examples](../examples.md) of using the ZIO integration available.\ diff --git a/doc/server/ziohttp.md b/doc/server/ziohttp.md index dbd59838bd..00ada39417 100644 --- a/doc/server/ziohttp.md +++ b/doc/server/ziohttp.md @@ -4,8 +4,7 @@ The `tapir-zio` module defines type aliases and extension methods which make it [ZIO](https://zio.dev) and tapir. Moreover, `tapir-zio-http-server` contains an interpreter useful when exposing the endpoints using the [ZIO Http](https://github.com/dream11/zio-http) server. -The `*-zio` modules depend on ZIO 2.x. For ZIO 1.x support, use modules with the `*-zio1` suffix. - +The `*-zio` modules depend on ZIO 2.x. You'll need the following dependency for the `ZServerEndpoint` type alias and helper classes: ```scala diff --git a/doc/stability.md b/doc/stability.md index 27c3621feb..69a847375c 100644 --- a/doc/stability.md +++ b/doc/stability.md @@ -29,8 +29,7 @@ The modules are categorised using the following levels: | pekko-http| stabilising | | play | stabilising | | vertx | stabilising | -| zio1-http | experimental | -| zio-http | experimental | +| zio-http | stabilising | ## Client interpreters @@ -66,8 +65,7 @@ The modules are categorised using the following levels: | newtype | stabilising | | monix-newtype | stabilising | | refined | stabilising | -| zio | experimental | -| zio1 | stabilising | +| zio | stabilising | | zio-prelude | experimental | | iron | experimental | @@ -84,7 +82,6 @@ The modules are categorised using the following levels: | upickle | stabilising | | pickler | experimental | | zio-json | experimental | -| zio1-json | experimental | ## Testing modules diff --git a/generated-doc/out/client/sttp.md b/generated-doc/out/client/sttp.md index 0e12264d14..078042f60f 100644 --- a/generated-doc/out/client/sttp.md +++ b/generated-doc/out/client/sttp.md @@ -68,13 +68,11 @@ The required imports are as follows: ```scala import sttp.tapir.client.sttp.ws.akkahttp._ // for akka-streams import sttp.tapir.client.sttp.ws.fs2._ // for fs2 -import sttp.tapir.client.sttp.ws.zio._ // for zio 2.x -import sttp.tapir.client.sttp.ws.zio1._ // for zio 1.x +import sttp.tapir.client.sttp.ws.zio._ // for zio ``` -No additional dependencies are needed (except for zio1, which needs the `tapir-sttp-client-ws-zio1` dependency), as -both of the above implementations are included in the main interpreter, with dependencies on akka-streams, fs2 and zio -being marked as optional (hence these are not transitive). +No additional dependencies are needed, as both of the above implementations are included in the main interpreter, +with dependencies on akka-streams, fs2 and zio being marked as optional (hence these are not transitive). ## Overwriting the response specification @@ -117,4 +115,4 @@ There are limitations existing on some clients that prevent the description gene For security reasons the [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) header is not accessible from frontend JavaScript code. It means that any endpoint containing a `.out(setCookie("token"))` will fail to be decoded on the client side when using Fetch. -A solution is to use the `setCookieOpt` function instead an let the browser do its job when dealing with cookies. \ No newline at end of file +A solution is to use the `setCookieOpt` function instead an let the browser do its job when dealing with cookies. diff --git a/generated-doc/out/server/vertx.md b/generated-doc/out/server/vertx.md index 1a4098eeae..a9d61c9167 100644 --- a/generated-doc/out/server/vertx.md +++ b/generated-doc/out/server/vertx.md @@ -145,10 +145,8 @@ val attach = VertxCatsServerInterpreter(dispatcher).route(streamedResponse.serve Add the following dependency ```scala -// for zio2: +// for zio: "com.softwaremill.sttp.tapir" %% "tapir-vertx-server-zio" % "1.9.3" -// for zio1: -"com.softwaremill.sttp.tapir" %% "tapir-vertx-server-zio1" % "1.9.3" ``` to use this interpreter with ZIO. diff --git a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/RIOMonadError.scala b/integrations/zio1/src/main/scala/sttp/tapir/ztapir/RIOMonadError.scala deleted file mode 100644 index 88c0013a15..0000000000 --- a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/RIOMonadError.scala +++ /dev/null @@ -1,16 +0,0 @@ -package sttp.tapir.ztapir - -import sttp.monad.MonadError -import zio.{RIO, URIO} - -class RIOMonadError[R] extends MonadError[RIO[R, *]] { - override def unit[T](t: T): RIO[R, T] = URIO.succeed(t) - override def map[T, T2](fa: RIO[R, T])(f: T => T2): RIO[R, T2] = fa.map(f) - override def flatMap[T, T2](fa: RIO[R, T])(f: T => RIO[R, T2]): RIO[R, T2] = fa.flatMap(f) - override def error[T](t: Throwable): RIO[R, T] = RIO.fail(t) - override protected def handleWrappedError[T](rt: RIO[R, T])(h: PartialFunction[Throwable, RIO[R, T]]): RIO[R, T] = rt.catchSome(h) - override def eval[T](t: => T): RIO[R, T] = RIO.effect(t) - override def suspend[T](t: => RIO[R, T]): RIO[R, T] = RIO.effectSuspend(t) - override def flatten[T](ffa: RIO[R, RIO[R, T]]): RIO[R, T] = ffa.flatten - override def ensure[T](f: RIO[R, T], e: => RIO[R, Unit]): RIO[R, T] = f.ensuring(e.ignore) -} diff --git a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZPartialServerEndpoint.scala b/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZPartialServerEndpoint.scala deleted file mode 100644 index ef2ac47db8..0000000000 --- a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZPartialServerEndpoint.scala +++ /dev/null @@ -1,82 +0,0 @@ -package sttp.tapir.ztapir - -import sttp.tapir.server.ServerEndpoint -import sttp.tapir.{ - Endpoint, - EndpointErrorOutputVariantsOps, - EndpointInfo, - EndpointInfoOps, - EndpointInput, - EndpointInputsOps, - EndpointMetaOps, - EndpointOutput, - EndpointOutputsOps -} -import zio.ZIO - -/** An endpoint with the security logic provided, and the main logic yet unspecified. See [[RichZEndpoint.zServerLogic]]. - * - * The provided security part of the server logic transforms inputs of type `SECURITY_INPUT`, either to an error of type `ERROR_OUTPUT`, or - * value of type `PRINCIPAL`. - * - * The part of the server logic which is not provided, will have to transform a tuple: `(PRINCIPAL, INPUT)` either into an error, or a - * value of type `OUTPUT`. - * - * Inputs/outputs can be added to partial endpoints as to regular endpoints, however the shape of the error outputs is fixed and cannot be - * changed. Hence, it's possible to create a base, secured input, and then specialise it with inputs, outputs and logic as needed. - * - * @tparam SECURITY_INPUT - * Type of the security inputs, transformed into PRINCIPAL - * @tparam PRINCIPAL - * Type of transformed security input. - * @tparam INPUT - * Input parameter types. - * @tparam ERROR_OUTPUT - * Error output parameter types. - * @tparam OUTPUT - * Output parameter types. - * @tparam C - * The capabilities that are required by this endpoint's inputs/outputs. `Any`, if no requirements. - */ -case class ZPartialServerEndpoint[R, SECURITY_INPUT, PRINCIPAL, INPUT, ERROR_OUTPUT, OUTPUT, -C]( - endpoint: Endpoint[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, C], - securityLogic: SECURITY_INPUT => ZIO[R, ERROR_OUTPUT, PRINCIPAL] -) extends EndpointInputsOps[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, C] - with EndpointOutputsOps[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, C] - with EndpointErrorOutputVariantsOps[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, C] - with EndpointInfoOps[C] - with EndpointMetaOps { outer => - - override type ThisType[-_R] = ZPartialServerEndpoint[R, SECURITY_INPUT, PRINCIPAL, INPUT, ERROR_OUTPUT, OUTPUT, _R] - override type EndpointType[_A, _I, _E, _O, -_R] = ZPartialServerEndpoint[R, _A, PRINCIPAL, _I, _E, _O, _R] - - override def securityInput: EndpointInput[SECURITY_INPUT] = endpoint.securityInput - override def input: EndpointInput[INPUT] = endpoint.input - def errorOutput: EndpointOutput[ERROR_OUTPUT] = endpoint.errorOutput - override def output: EndpointOutput[OUTPUT] = endpoint.output - override def info: EndpointInfo = endpoint.info - - override private[tapir] def withInput[I2, C2]( - input: EndpointInput[I2] - ): ZPartialServerEndpoint[R, SECURITY_INPUT, PRINCIPAL, I2, ERROR_OUTPUT, OUTPUT, C with C2] = - copy(endpoint = endpoint.copy(input = input)) - override private[tapir] def withOutput[O2, C2](output: EndpointOutput[O2]) = copy(endpoint = endpoint.copy(output = output)) - override private[tapir] def withInfo(info: EndpointInfo) = copy(endpoint = endpoint.copy(info = info)) - override private[tapir] def withErrorOutputVariant[E2, C2]( - errorOutput: EndpointOutput[E2], - embedE: ERROR_OUTPUT => E2 - ): ZPartialServerEndpoint[R, SECURITY_INPUT, PRINCIPAL, INPUT, E2, OUTPUT, C with C2] = - this.copy( - endpoint = endpoint.copy(errorOutput = errorOutput), - securityLogic = a => securityLogic(a).mapError(embedE) - ) - - override protected def showType: String = "PartialServerEndpoint" - - def serverLogic[R0](logic: PRINCIPAL => INPUT => ZIO[R0, ERROR_OUTPUT, OUTPUT]): ZServerEndpoint[R with R0, C] = - ServerEndpoint( - endpoint, - _ => securityLogic(_: SECURITY_INPUT).either.resurrect, - _ => (u: PRINCIPAL) => (i: INPUT) => logic(u)(i).either.resurrect - ) -} diff --git a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZTapir.scala b/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZTapir.scala deleted file mode 100644 index eebb51edd8..0000000000 --- a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZTapir.scala +++ /dev/null @@ -1,48 +0,0 @@ -package sttp.tapir.ztapir - -import sttp.tapir._ -import sttp.tapir.server.ServerEndpoint -import zio.{RIO, ZIO} - -trait ZTapir { - type ZServerEndpoint[R, -C] = ServerEndpoint[C, RIO[R, *]] - - implicit class RichZEndpoint[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, C]( - e: Endpoint[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, C] - ) { - - /** Combine this public endpoint description with a function, which implements the server-side logic. The logic returns a result, which - * is either an error or a successful output, wrapped in an effect type `F`. For secure endpoints, use [[zServerSecurityLogic]]. - * - * A server endpoint can be passed to a server interpreter. Each server interpreter supports effects of a specific type(s). - * - * Both the endpoint and logic function are considered complete, and cannot be later extended through the returned [[ServerEndpoint]] - * value (except for endpoint meta-data). Secure endpoints allow providing the security logic before all the inputs and outputs are - * specified. - */ - def zServerLogic[R](logic: INPUT => ZIO[R, ERROR_OUTPUT, OUTPUT])(implicit aIsUnit: SECURITY_INPUT =:= Unit): ZServerEndpoint[R, C] = - ServerEndpoint.public(e.asInstanceOf[Endpoint[Unit, INPUT, ERROR_OUTPUT, OUTPUT, C]], _ => logic(_: INPUT).either.resurrect) - - /** Combine this endpoint description with a function, which implements the security logic of the endpoint. - * - * Subsequently, the endpoint inputs and outputs can be extended (but not error outputs!). Then the main server logic can be provided, - * given a function which accepts as arguments the result of the security logic and the remaining input. The final result is then a - * [[ServerEndpoint]]. - * - * A complete server endpoint can be passed to a server interpreter. Each server interpreter supports effects of a specific type(s). - * - * An example use-case is defining an endpoint with fully-defined errors, and with security logic built-in. Such an endpoint can be - * then extended by multiple other endpoints, by specifying different inputs, outputs and the main logic. - */ - def zServerSecurityLogic[R, PRINCIPAL]( - f: SECURITY_INPUT => ZIO[R, ERROR_OUTPUT, PRINCIPAL] - ): ZPartialServerEndpoint[R, SECURITY_INPUT, PRINCIPAL, INPUT, ERROR_OUTPUT, OUTPUT, C] = - ZPartialServerEndpoint(e, f) - } - - implicit class RichZServerEndpoint[R, C](zse: ZServerEndpoint[R, C]) { - - /** Extends the environment so that it can be made uniform across multiple endpoints. */ - def widen[R2 <: R]: ZServerEndpoint[R2, C] = zse.asInstanceOf[ZServerEndpoint[R2, C]] // this is fine - } -} diff --git a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZioServerSentEvents.scala b/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZioServerSentEvents.scala deleted file mode 100644 index 312c1bb830..0000000000 --- a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/ZioServerSentEvents.scala +++ /dev/null @@ -1,30 +0,0 @@ -package sttp.tapir.ztapir - -import zio.stream.{Stream, ZTransducer} -import sttp.model.sse.ServerSentEvent -import zio.Chunk - -object ZioServerSentEvents { - def serialiseSSEToBytes: Stream[Throwable, ServerSentEvent] => Stream[Throwable, Byte] = sseStream => { - sseStream - .map(sse => { - s"${sse.toString()}\n\n" - }) - .mapConcatChunk(s => Chunk.fromArray(s.getBytes("UTF-8"))) - } - - def parseBytesToSSE: Stream[Throwable, Byte] => Stream[Throwable, ServerSentEvent] = stream => { - stream - .aggregate(ZTransducer.utf8Decode) - .aggregate(ZTransducer.splitLines) - .mapAccum(List.empty[String]) { case (acc, line) => - if (line.isEmpty) (Nil, Some(acc.reverse)) - else (line :: acc, None) - } - .collect { case Some(l) => - l - } - .filter(_.nonEmpty) - .map(ServerSentEvent.parse) - } -} diff --git a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/package.scala b/integrations/zio1/src/main/scala/sttp/tapir/ztapir/package.scala deleted file mode 100644 index a2e6d107bc..0000000000 --- a/integrations/zio1/src/main/scala/sttp/tapir/ztapir/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package sttp.tapir - -package object ztapir extends Tapir with ZTapir diff --git a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/TestError.scala b/integrations/zio1/src/test/scala/sttp/tapir/ztapir/TestError.scala deleted file mode 100644 index 7116834f3d..0000000000 --- a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/TestError.scala +++ /dev/null @@ -1,15 +0,0 @@ -package sttp.tapir.ztapir - -import sttp.tapir.CodecFormat.TextPlain -import sttp.tapir.{Codec, DecodeResult} - -sealed trait TestError - -object TestError { - case object SomeError extends TestError - - implicit val codec: Codec[String, TestError, TextPlain] = Codec.string.mapDecode { - case "SomeError" => DecodeResult.Value(SomeError: TestError) - case value => DecodeResult.Error(value, new RuntimeException(s"Unable to decode value $value")) - }(_.toString) -} diff --git a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZTapirTest.scala b/integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZTapirTest.scala deleted file mode 100644 index d5dd2f81c2..0000000000 --- a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZTapirTest.scala +++ /dev/null @@ -1,131 +0,0 @@ -package sttp.tapir.ztapir - -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.interceptor.RequestResult -import sttp.tapir.server.interpreter.{BodyListener, RawValue, RequestBody, ServerInterpreter, ToResponseBody} -import sttp.capabilities.{Streams, WebSockets} -import sttp.model.{HasHeaders, Header, Method, QueryParams, StatusCode, Uri} -import sttp.tapir.{AttributeKey, CodecFormat, PublicEndpoint, RawBodyType, WebSocketBodyOutput} -import sttp.tapir.model.{ConnectionInfo, ServerRequest} -import sttp.tapir.server.model.ServerResponse -import zio.{UIO, ZIO} -import sttp.tapir.ztapir.instances.TestMonadError._ -import zio.test.DefaultRunnableSpec -import zio.test._ -import zio.test.Assertion._ -import zio.test.environment._ - -import java.nio.charset.Charset -import scala.util.{Success, Try} -import scala.collection.immutable.Seq - -object ZTapirTest extends DefaultRunnableSpec with ZTapir { - - def spec: ZSpec[TestEnvironment, Any] = - suite("ZTapir tests")(testZServerLogicErrorHandling, testZServerSecurityLogicErrorHandling) - - type ResponseBodyType = String - - type RequestBodyType = String - - private val exampleRequestBody = new RequestBody[TestEffect, RequestBodyType] { - override val streams: Streams[RequestBodyType] = null.asInstanceOf[Streams[RequestBodyType]] - override def toRaw[R](serverRequest: ServerRequest, bodyType: RawBodyType[R], maxBytes: Option[Long]): TestEffect[RawValue[R]] = ??? - override def toStream(serverRequest: ServerRequest, maxBytes: Option[Long]): streams.BinaryStream = ??? - } - - private val exampleToResponse: ToResponseBody[ResponseBodyType, RequestBodyType] = new ToResponseBody[ResponseBodyType, RequestBodyType] { - override val streams: Streams[RequestBodyType] = null.asInstanceOf[Streams[RequestBodyType]] - override def fromRawValue[R](v: R, headers: HasHeaders, format: CodecFormat, bodyType: RawBodyType[R]): ResponseBodyType = "Sample body" - override def fromStreamValue( - v: streams.BinaryStream, - headers: HasHeaders, - format: CodecFormat, - charset: Option[Charset] - ): ResponseBodyType = ??? - override def fromWebSocketPipe[REQ, RESP]( - pipe: streams.Pipe[REQ, RESP], - o: WebSocketBodyOutput[streams.Pipe[REQ, RESP], REQ, RESP, _, RequestBodyType] - ): ResponseBodyType = ??? - } - - private val testRequest: ServerRequest = new ServerRequest { - override def protocol: String = ??? - override def connectionInfo: ConnectionInfo = ??? - override def underlying: Any = ??? - override def pathSegments: List[String] = List("foo", "bar") - override def queryParameters: QueryParams = QueryParams() - override def method: Method = ??? - override def uri: Uri = ??? - override def headers: scala.collection.immutable.Seq[Header] = scala.collection.immutable.Seq(Header("X-User-Name", "John")) - override def attribute[T](k: AttributeKey[T]): Option[T] = None - override def attribute[T](k: AttributeKey[T], v: T): ServerRequest = this - override def withUnderlying(underlying: Any): ServerRequest = this - } - - implicit val bodyListener: BodyListener[TestEffect, ResponseBodyType] = new BodyListener[TestEffect, ResponseBodyType] { - override def onComplete(body: ResponseBodyType)(cb: Try[Unit] => TestEffect[Unit]): TestEffect[String] = { - cb(Success(())).map(_ => body) - } - } - - private def errorToResponse(error: Throwable): UIO[RequestResult.Response[ResponseBodyType]] = - UIO(RequestResult.Response(ServerResponse[ResponseBodyType](StatusCode.InternalServerError, Nil, Some(error.getMessage), None))) - - final case class User(name: String) - - private def failedAutLogic(userName: String): UIO[User] = ZIO(10 / 0).orDie.as(User(userName)) - - private val testZServerLogicErrorHandling = testM("zServerLogic error handling") { - val testEndpoint: PublicEndpoint[Unit, TestError, String, Any] = - endpoint.in("foo" / "bar").errorOut(plainBody[TestError]).out(stringBody) - - def logic(input: Unit): ZIO[Any, TestError, String] = ZIO(10 / 0).orDie.map(_.toString) - val serverEndpoint: ZServerEndpoint[Any, Any] = testEndpoint.zServerLogic(logic) - - val interpreter = new ServerInterpreter[ZioStreams with WebSockets, TestEffect, ResponseBodyType, RequestBodyType]( - _ => List(serverEndpoint), - exampleRequestBody, - exampleToResponse, - List.empty, - _ => ZIO.unit - ) - - interpreter(testRequest) - .catchAll(errorToResponse) - .map { result => - assert(result)( - isSubtype[RequestResult.Response[String]](hasField("code", _.response.code, equalTo(StatusCode.InternalServerError))) - ) - } - } - - private val testZServerSecurityLogicErrorHandling = testM("zServerLogicForCurrent error handling") { - val securedEndpoint: ZPartialServerEndpoint[Any, String, User, Unit, TestError, Unit, Any] = - endpoint.securityIn(header[String]("X-User-Name")).errorOut(plainBody[TestError]).zServerSecurityLogic[Any, User](failedAutLogic) - - val testPartialEndpoint: ZPartialServerEndpoint[Any, String, User, Unit, TestError, String, Any] = - securedEndpoint.in("foo" / "bar").out(stringBody) - - def logic(user: User, rest: Unit): ZIO[Any, TestError, String] = ZIO.succeed("Hello World") - - val serverEndpoint: ZServerEndpoint[Any, Any] = - testPartialEndpoint.serverLogic[Any](user => unit => logic(user, unit)) - - val interpreter = new ServerInterpreter[ZioStreams with WebSockets, TestEffect, ResponseBodyType, RequestBodyType]( - _ => List(serverEndpoint), - exampleRequestBody, - exampleToResponse, - List.empty, - _ => ZIO.unit - ) - - interpreter(testRequest) - .catchAll(errorToResponse) - .map { result => - assert(result)( - isSubtype[RequestResult.Response[String]](hasField("code", _.response.code, equalTo(StatusCode.InternalServerError))) - ) - } - } -} diff --git a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZioServerSentEventsTest.scala b/integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZioServerSentEventsTest.scala deleted file mode 100644 index 22d779d8e9..0000000000 --- a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/ZioServerSentEventsTest.scala +++ /dev/null @@ -1,95 +0,0 @@ -package sttp.tapir.ztapir - -import sttp.model.sse.ServerSentEvent -import zio.Chunk -import zio.test.{DefaultRunnableSpec, ZSpec} -import zio.test.environment.TestEnvironment -import zio.test._ -import zio.test.Assertion._ -import zio.stream._ - -import java.nio.charset.Charset - -object ZioServerSentEventsTest extends DefaultRunnableSpec { - def spec: ZSpec[TestEnvironment, Any] = - suite("ZioServerSentEvents tests")( - testM("serialiseSSEToBytes should successfully serialise simple Server Sent Event to ByteString") { - val sse: Stream[Nothing, ServerSentEvent] = Stream(ServerSentEvent(Some("data"), Some("event"), Some("id1"), Some(10))) - val serialised = ZioServerSentEvents.serialiseSSEToBytes(sse) - serialised.runCollect.map { sseEvents => - assert(sseEvents.toList)(equalTo(s"""data: data - |event: event - |id: id1 - |retry: 10 - | - |""".stripMargin.getBytes(Charset.forName("UTF-8")).toList)) - } - }, - testM("serialiseSSEToBytes should omit fields that are not set") { - val sse = Stream(ServerSentEvent(Some("data"), None, Some("id1"), None)) - val serialised = ZioServerSentEvents.serialiseSSEToBytes(sse) - serialised.runCollect.map { sseEvents => - assert(sseEvents.toList)(equalTo(s"""data: data - |id: id1 - | - |""".stripMargin.getBytes(Charset.forName("UTF-8")).toList)) - } - }, - testM("serialiseSSEToBytes should successfully serialise multiline data event") { - val sse = Stream( - ServerSentEvent( - Some("""some data info 1 - |some data info 2 - |some data info 3""".stripMargin), - None, - None, - None - ) - ) - val serialised = ZioServerSentEvents.serialiseSSEToBytes(sse) - serialised.runCollect.map { sseEvents => - assert(sseEvents.toList)(equalTo(s"""data: some data info 1 - |data: some data info 2 - |data: some data info 3 - | - |""".stripMargin.getBytes(Charset.forName("UTF-8")).toList)) - } - }, - testM("parseBytesToSSE should successfully parse SSE bytes to SSE structure") { - val sseBytes = Stream.fromChunk( - Chunk.fromArray( - """data: event1 data - |event: event1 - |id: id1 - |retry: 5 - | - | - |data: event2 data1 - |data: event2 data2 - |data: event2 data3 - |id: id2 - | - |""".stripMargin.getBytes(Charset.forName("UTF-8")) - ) - ) - val parsed = ZioServerSentEvents.parseBytesToSSE(sseBytes) - parsed.runCollect.map { events => - assert(events.toList)( - equalTo( - List( - ServerSentEvent(Some("event1 data"), Some("event1"), Some("id1"), Some(5)), - ServerSentEvent( - Some("""event2 data1 - |event2 data2 - |event2 data3""".stripMargin), - None, - Some("id2"), - None - ) - ) - ) - ) - } - } - ) -} diff --git a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/instances/TestMonadError.scala b/integrations/zio1/src/test/scala/sttp/tapir/ztapir/instances/TestMonadError.scala deleted file mode 100644 index 193da4bb10..0000000000 --- a/integrations/zio1/src/test/scala/sttp/tapir/ztapir/instances/TestMonadError.scala +++ /dev/null @@ -1,23 +0,0 @@ -package sttp.tapir.ztapir.instances - -import sttp.monad.MonadError -import zio.{RIO, ZIO} - -object TestMonadError { - type TestEffect[A] = RIO[Any, A] - - implicit val testEffectMonadError: MonadError[TestEffect] = new MonadError[TestEffect] { - override def unit[T](t: T): TestEffect[T] = ZIO.succeed(t) - - override def map[T, T2](fa: TestEffect[T])(f: T => T2): TestEffect[T2] = fa.map(f) - - override def flatMap[T, T2](fa: TestEffect[T])(f: T => TestEffect[T2]): TestEffect[T2] = fa.flatMap(f) - - override def error[T](t: Throwable): TestEffect[T] = ZIO.fail(t) - - override protected def handleWrappedError[T](rt: TestEffect[T])(h: PartialFunction[Throwable, TestEffect[T]]): TestEffect[T] = - rt.catchSome(h) - - override def ensure[T](f: TestEffect[T], e: => TestEffect[Unit]): TestEffect[T] = f.ensuring(e.ignore) - } -} diff --git a/json/zio1/src/main/scala/sttp/tapir/json/zio/TapirJsonZio.scala b/json/zio1/src/main/scala/sttp/tapir/json/zio/TapirJsonZio.scala deleted file mode 100644 index c731346cc1..0000000000 --- a/json/zio1/src/main/scala/sttp/tapir/json/zio/TapirJsonZio.scala +++ /dev/null @@ -1,47 +0,0 @@ -package sttp.tapir.json.zio - -import sttp.tapir.Codec.JsonCodec -import sttp.tapir.DecodeResult.Error.{JsonDecodeException, JsonError} -import sttp.tapir.DecodeResult.{Error, Value} -import sttp.tapir.Schema.SName -import sttp.tapir.SchemaType.{SCoproduct, SProduct} -import sttp.tapir._ -import zio.json.ast.Json -import zio.json.ast.Json.Obj -import zio.json.{JsonDecoder, JsonEncoder, _} - -trait TapirJsonZio { - - def jsonBody[T: JsonEncoder: JsonDecoder: Schema]: EndpointIO.Body[String, T] = stringBodyUtf8AnyFormat(zioCodec[T]) - - def jsonBodyWithRaw[T: JsonEncoder: JsonDecoder: Schema]: EndpointIO.Body[String, (String, T)] = stringBodyUtf8AnyFormat( - implicitly[JsonCodec[(String, T)]] - ) - - def jsonQuery[T: JsonEncoder: JsonDecoder: Schema](name: String): EndpointInput.Query[T] = - queryAnyFormat[T, CodecFormat.Json](name, Codec.jsonQuery(zioCodec)) - - implicit def zioCodec[T: JsonEncoder: JsonDecoder: Schema]: JsonCodec[T] = - sttp.tapir.Codec.json[T] { s => - zio.json.JsonDecoder.apply[T].decodeJson(s) match { - case Left(error) => - val (message, path) = parseErrorMessage(error) - Error(s, JsonDecodeException(List(JsonError(message, path)), new Exception(error))) - case Right(value) => Value(value) - } - } { t => t.toJson } - - private def parseErrorMessage(errorMessage: String): (String, List[FieldName]) = { - val leftParenIndex = errorMessage.indexOf('(') - if (leftParenIndex >= 0) { - val path = errorMessage.substring(0, leftParenIndex) - val message = errorMessage.substring(leftParenIndex + 1, errorMessage.length - 1) - message -> path.split("\\.").toList.filter(_.nonEmpty).map(FieldName.apply) - } else { - errorMessage -> List.empty - } - } - - implicit val schemaForZioJsonValue: Schema[Json] = Schema.any - implicit val schemaForZioJsonObject: Schema[Obj] = Schema.anyObject[Obj].name(SName("zio.json.ast.Json.Obj")) -} diff --git a/json/zio1/src/main/scala/sttp/tapir/json/zio/package.scala b/json/zio1/src/main/scala/sttp/tapir/json/zio/package.scala deleted file mode 100644 index b4a30b30a9..0000000000 --- a/json/zio1/src/main/scala/sttp/tapir/json/zio/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package sttp.tapir.json - -package object zio extends TapirJsonZio diff --git a/json/zio1/src/test/scalajvm/sttp/tapir/json/zio/TapirJsonZioTest.scala b/json/zio1/src/test/scalajvm/sttp/tapir/json/zio/TapirJsonZioTest.scala deleted file mode 100644 index ab7388666f..0000000000 --- a/json/zio1/src/test/scalajvm/sttp/tapir/json/zio/TapirJsonZioTest.scala +++ /dev/null @@ -1,95 +0,0 @@ -package sttp.tapir.json.zio - -import org.scalatest.Assertion -import org.scalatest.flatspec.AnyFlatSpecLike -import org.scalatest.matchers.should.Matchers -import sttp.tapir.Codec.JsonCodec -import sttp.tapir.DecodeResult.Error.{JsonDecodeException, JsonError} -import sttp.tapir.DecodeResult.Value -import sttp.tapir.SchemaType.{SCoproduct, SProduct} -import sttp.tapir.generic.auto._ -import sttp.tapir.{DecodeResult, FieldName, Schema} -import zio.json.DeriveJsonCodec - -class TapirJsonZioTest extends AnyFlatSpecLike with Matchers { - - case class Customer(name: String, yearOfBirth: Int, lastPurchase: Option[Long]) - case class Item(serialNumber: Long, price: Int) - case class Order(items: Seq[Item], customer: Customer) - - implicit val customerZioCodec: zio.json.JsonCodec[Customer] = DeriveJsonCodec.gen[Customer] - implicit val itemZioCodec: zio.json.JsonCodec[Item] = DeriveJsonCodec.gen[Item] - implicit val orderZioCodec: zio.json.JsonCodec[Order] = DeriveJsonCodec.gen[Order] - - val customerCodec: JsonCodec[Customer] = zioCodec[Customer] - - def testEncodeDecode[T: Schema: zio.json.JsonEncoder: zio.json.JsonDecoder](original: T): Assertion = { - val codec = zioCodec[T] - val encoded = codec.encode(original) - codec.decode(encoded) match { - case Value(d) => - d shouldBe original - case f: DecodeResult.Failure => - fail(f.toString) - } - } - - it should "encode and decode Scala case class with non-empty Option elements" in { - val customer = Customer("Alita", 1985, Some(1566150331L)) - testEncodeDecode(customer) - } - - it should "encode and decode Scala case class with empty Option elements" in { - val customer = Customer("Alita", 1985, None) - testEncodeDecode(customer) - } - - it should "encode and decode Scala case class with list" in { - val order = Order(Seq(Item(100, 200), Item(101, 300)), Customer("Alita", 1985, None)) - testEncodeDecode(order) - } - - it should "encode to non-prettified Json" in { - val customer = Customer("Alita", 1985, None) - val codec = zioCodec[Customer] - val expected = """{"name":"Alita","yearOfBirth":1985}""" - codec.encode(customer) shouldBe expected - } - - it should "return a JSON specific error on object decode failure" in { - val input = """{"items":[]}""" - val actual = customerCodec.decode(input) - actual shouldBe a[DecodeResult.Error] - - val failure = actual.asInstanceOf[DecodeResult.Error] - failure.original shouldEqual input - failure.error shouldBe a[JsonDecodeException] - - val error = failure.error.asInstanceOf[JsonDecodeException] - error.errors shouldEqual - List(JsonError("missing", List(FieldName("name")))) - error.underlying shouldBe a[Exception] - } - it should "return a JSON specific error on array decode failure" in { - val input = """[{}]""" - val actual = zioCodec[Seq[Item]].decode(input) - actual shouldBe a[DecodeResult.Error] - - val failure = actual.asInstanceOf[DecodeResult.Error] - failure.original shouldEqual input - failure.error shouldBe a[JsonDecodeException] - - val error = failure.error.asInstanceOf[JsonDecodeException] - error.errors shouldEqual - List(JsonError("missing", List(FieldName("[0]"), FieldName("serialNumber")))) - error.underlying shouldBe a[Exception] - } - - it should "return a coproduct schema for a JsonValue" in { - schemaForZioJsonValue.schemaType shouldBe a[SCoproduct[_]] - } - - it should "return a coproduct schema for a JsonObject" in { - schemaForZioJsonObject.schemaType shouldBe a[SProduct[_]] - } -} diff --git a/project/Versions.scala b/project/Versions.scala index cd59303bdc..04837df78c 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -31,10 +31,6 @@ object Versions { val refined = "0.11.0" val iron = "2.3.0" val enumeratum = "1.7.3" - val zio1 = "1.0.18" - val zio1InteropCats = "13.0.0.2" - val zio1Json = "0.2.0" - val zio1InteropReactiveStreams = "1.3.12" val zio = "2.0.19" val zioInteropCats = "23.0.0.8" val zioInteropReactiveStreams = "2.0.2" diff --git a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerInterpreter.scala b/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerInterpreter.scala deleted file mode 100644 index 32ef251599..0000000000 --- a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerInterpreter.scala +++ /dev/null @@ -1,27 +0,0 @@ -package sttp.tapir.server.armeria.zio - -import _root_.zio._ -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.armeria.TapirService -import sttp.tapir.ztapir.ZServerEndpoint - -trait ArmeriaZioServerInterpreter[R] { - - def armeriaServerOptions: ArmeriaZioServerOptions[RIO[R, *]] - - def toService(serverEndpoints: ZServerEndpoint[R, ZioStreams])(implicit runtime: Runtime[R]): TapirService[ZioStreams, RIO[R, *]] = - toService(List(serverEndpoints)) - - def toService(serverEndpoints: List[ZServerEndpoint[R, ZioStreams]])(implicit runtime: Runtime[R]): TapirService[ZioStreams, RIO[R, *]] = - TapirZioService(serverEndpoints, armeriaServerOptions) -} - -object ArmeriaZioServerInterpreter { - def apply[R]( - serverOptions: ArmeriaZioServerOptions[RIO[R, *]] = ArmeriaZioServerOptions.default[R] - ): ArmeriaZioServerInterpreter[R] = { - new ArmeriaZioServerInterpreter[R] { - override def armeriaServerOptions: ArmeriaZioServerOptions[RIO[R, *]] = serverOptions - } - } -} diff --git a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerOptions.scala b/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerOptions.scala deleted file mode 100644 index 0abec811cd..0000000000 --- a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerOptions.scala +++ /dev/null @@ -1,74 +0,0 @@ -package sttp.tapir.server.armeria.zio - -import _root_.zio.{RIO, Task, URIO} -import com.linecorp.armeria.common.CommonPools -import org.slf4j.{Logger, LoggerFactory} -import sttp.tapir.server.armeria.ArmeriaServerOptions -import sttp.tapir.server.interceptor.log.DefaultServerLog -import sttp.tapir.server.interceptor.{CustomiseInterceptors, Interceptor} -import sttp.tapir.{Defaults, TapirFile} - -import scala.util.control.NonFatal - -final case class ArmeriaZioServerOptions[F[_]]( - createFile: () => F[TapirFile], - deleteFile: TapirFile => F[Unit], - interceptors: List[Interceptor[F]] -) extends ArmeriaServerOptions[F] { - def prependInterceptor(i: Interceptor[F]): ArmeriaZioServerOptions[F] = - copy(interceptors = i :: interceptors) - def appendInterceptor(i: Interceptor[F]): ArmeriaZioServerOptions[F] = - copy(interceptors = interceptors :+ i) -} - -object ArmeriaZioServerOptions { - - /** Allows customising the interceptors used by the server interpreter. */ - def customiseInterceptors[R]: CustomiseInterceptors[RIO[R, *], ArmeriaZioServerOptions[RIO[R, *]]] = - CustomiseInterceptors( - createOptions = (ci: CustomiseInterceptors[RIO[R, *], ArmeriaZioServerOptions[RIO[R, *]]]) => { - ArmeriaZioServerOptions( - defaultCreateFile, - defaultDeleteFile, - ci.interceptors - ) - } - ).serverLog(defaultServerLog[R]) - - private val logger: Logger = LoggerFactory.getLogger(this.getClass.getPackage.getName) - - implicit def default[R]: ArmeriaZioServerOptions[RIO[R, *]] = customiseInterceptors.options - - def defaultCreateFile[R](): RIO[R, TapirFile] = blocking(Defaults.createTempFile()) - - def defaultDeleteFile[R](file: TapirFile): RIO[R, Unit] = blocking(Defaults.deleteFile()(file)) - - def defaultServerLog[R]: DefaultServerLog[RIO[R, *]] = DefaultServerLog( - doLogWhenReceived = debugLog(_, None), - doLogWhenHandled = debugLog[R], - doLogAllDecodeFailures = debugLog[R], - doLogExceptions = (msg: String, ex: Throwable) => URIO.succeed { logger.warn(msg, ex) }, - noLog = URIO.unit - ) - - private def debugLog[R](msg: String, exOpt: Option[Throwable]): RIO[R, Unit] = - URIO.succeed(exOpt match { - case None => logger.debug(msg) - case Some(ex) => logger.debug(msg, ex) - }) - - private def blocking[R, T](body: => T): RIO[R, T] = { - Task.effectAsync { cb => - CommonPools - .blockingTaskExecutor() - .execute(() => { - try { - cb(Task.succeed(body)) - } catch { - case NonFatal(ex) => - cb(Task.fail(ex)) - } - }) - } - } -} diff --git a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/RIOMonadAsyncError.scala b/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/RIOMonadAsyncError.scala deleted file mode 100644 index b8b92a5140..0000000000 --- a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/RIOMonadAsyncError.scala +++ /dev/null @@ -1,37 +0,0 @@ -package sttp.tapir.server.armeria.zio - -import sttp.monad.{Canceler, MonadAsyncError} -import zio._ - -// Forked from sttp.client3.impl.zio.RIOMonadAsyncError -private class RIOMonadAsyncError[R] extends MonadAsyncError[RIO[R, *]] { - override def unit[T](t: T): RIO[R, T] = RIO.succeed(t) - - override def map[T, T2](fa: RIO[R, T])(f: T => T2): RIO[R, T2] = fa.map(f) - - override def flatMap[T, T2](fa: RIO[R, T])(f: T => RIO[R, T2]): RIO[R, T2] = - fa.flatMap(f) - - override def async[T](register: (Either[Throwable, T] => Unit) => Canceler): RIO[R, T] = - RIO.effectAsyncInterrupt { cb => - val canceler = register { - case Left(t) => cb(RIO.fail(t)) - case Right(t) => cb(RIO.succeed(t)) - } - - Left(UIO.succeed(canceler.cancel())) - } - - override def error[T](t: Throwable): RIO[R, T] = RIO.fail(t) - - override protected def handleWrappedError[T](rt: RIO[R, T])(h: PartialFunction[Throwable, RIO[R, T]]): RIO[R, T] = - rt.catchSome(h) - - override def eval[T](t: => T): RIO[R, T] = RIO.effect(t) - - override def suspend[T](t: => RIO[R, T]): RIO[R, T] = RIO.effectSuspend(t) - - override def flatten[T](ffa: RIO[R, RIO[R, T]]): RIO[R, T] = ffa.flatten - - override def ensure[T](f: RIO[R, T], e: => RIO[R, Unit]): RIO[R, T] = f.ensuring(e.ignore) -} diff --git a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/TapirZioService.scala b/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/TapirZioService.scala deleted file mode 100644 index 47e32b80a5..0000000000 --- a/server/armeria-server/zio1/src/main/scala/sttp/tapir/server/armeria/zio/TapirZioService.scala +++ /dev/null @@ -1,92 +0,0 @@ -package sttp.tapir.server.armeria.zio - -import _root_.zio._ -import _root_.zio.interop.reactivestreams._ -import _root_.zio.stream.Stream -import com.linecorp.armeria.common.{HttpData, HttpRequest, HttpResponse} -import com.linecorp.armeria.server.ServiceRequestContext -import org.reactivestreams.Publisher -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.ServerEndpoint -import sttp.tapir.server.armeria._ -import sttp.tapir.server.interceptor.reject.RejectInterceptor -import sttp.tapir.server.interpreter.{FilterServerEndpoints, ServerInterpreter} - -import java.util.concurrent.CompletableFuture -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Success} - -private[zio] final case class TapirZioService[R]( - serverEndpoints: List[ServerEndpoint[ZioStreams, RIO[R, *]]], - armeriaServerOptions: ArmeriaZioServerOptions[RIO[R, *]] -)(implicit runtime: Runtime[R]) - extends TapirService[ZioStreams, RIO[R, *]] { - - private[this] implicit val monad: RIOMonadAsyncError[R] = new RIOMonadAsyncError() - private[this] implicit val bodyListener: ArmeriaBodyListener[RIO[R, *]] = new ArmeriaBodyListener - - private[this] val zioStreamCompatible: StreamCompatible[ZioStreams] = ZioStreamCompatible(runtime) - - override def serve(ctx: ServiceRequestContext, req: HttpRequest): HttpResponse = { - implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(ctx.eventLoop()) - implicit val rioFutureConversion: RioFutureConversion[R] = new RioFutureConversion[R] - - val interpreter: ServerInterpreter[ZioStreams, RIO[R, *], ArmeriaResponseType, ZioStreams] = - new ServerInterpreter[ZioStreams, RIO[R, *], ArmeriaResponseType, ZioStreams]( - FilterServerEndpoints(serverEndpoints), - new ArmeriaRequestBody(armeriaServerOptions, zioStreamCompatible), - new ArmeriaToResponseBody(zioStreamCompatible), - RejectInterceptor.disableWhenSingleEndpoint(armeriaServerOptions.interceptors, serverEndpoints), - armeriaServerOptions.deleteFile - ) - - val serverRequest = new ArmeriaServerRequest(ctx) - val future = new CompletableFuture[HttpResponse]() - val result = interpreter(serverRequest).map(ResultMapping.toArmeria) - - val cancellable = runtime.unsafeRunToFuture(result) - cancellable.future.onComplete { - case Failure(exception) => - future.completeExceptionally(exception) - case Success(value) => - future.complete(value) - } - - val httpResponse = HttpResponse.from(future) - httpResponse - .whenComplete() - .asInstanceOf[CompletableFuture[Unit]] - .exceptionally { case (_: Throwable) => - cancellable.cancel() - () - } - httpResponse - } -} - -private object ZioStreamCompatible { - def apply(runtime: Runtime[Any]): StreamCompatible[ZioStreams] = { - new StreamCompatible[ZioStreams] { - override val streams: ZioStreams = ZioStreams - - override def asStreamMessage(stream: Stream[Throwable, Byte]): Publisher[HttpData] = - runtime.unsafeRun(stream.mapChunks(c => Chunk.single(HttpData.wrap(c.toArray))).toPublisher) - - override def fromArmeriaStream(publisher: Publisher[HttpData], maxBytes: Option[Long]): Stream[Throwable, Byte] = - publisher.toStream().mapConcatChunk(httpData => Chunk.fromArray(httpData.array())) - } - } -} - -private class RioFutureConversion[R](implicit ec: ExecutionContext, runtime: Runtime[R]) extends FutureConversion[RIO[R, *]] { - def from[T](f: => Future[T]): RIO[R, T] = { - RIO.effectAsync { cb => - f.onComplete { - case Failure(exception) => cb(Task.fail(exception)) - case Success(value) => cb(Task.succeed(value)) - } - } - } - - override def to[A](f: => RIO[R, A]): Future[A] = runtime.unsafeRunToFuture(f) -} diff --git a/server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerTest.scala b/server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerTest.scala deleted file mode 100644 index 7d1b356c84..0000000000 --- a/server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioServerTest.scala +++ /dev/null @@ -1,24 +0,0 @@ -package sttp.tapir.server.armeria.zio - -import cats.effect.{IO, Resource} -import sttp.capabilities.zio.ZioStreams -import sttp.monad.MonadError -import sttp.tapir.server.tests._ -import sttp.tapir.tests.{Test, TestSuite} -import sttp.tapir.ztapir.RIOMonadError -import zio.Task - -class ArmeriaZioServerTest extends TestSuite { - - override def tests: Resource[IO, List[Test]] = backendResource.map { backend => - - implicit val monadError: MonadError[Task] = new RIOMonadError - - val interpreter = new ArmeriaZioTestServerInterpreter() - val createServerTest = new DefaultCreateServerTest(backend, interpreter) - - new AllServerTests(createServerTest, interpreter, backend, basic = false, options = false).tests() ++ - new ServerBasicTests(createServerTest, interpreter, supportsUrlEncodedPathSegments = false).tests() ++ - new ServerStreamingTests(createServerTest, maxLengthSupported = false).tests(ZioStreams)(_ => Task.unit) - } -} diff --git a/server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioTestServerInterpreter.scala b/server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioTestServerInterpreter.scala deleted file mode 100644 index b1c3937909..0000000000 --- a/server/armeria-server/zio1/src/test/scala/sttp/tapir/server/armeria/zio/ArmeriaZioTestServerInterpreter.scala +++ /dev/null @@ -1,19 +0,0 @@ -package sttp.tapir.server.armeria.zio - -import _root_.zio.{Runtime, Task} -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.ServerEndpoint -import sttp.tapir.server.armeria.{ArmeriaTestServerInterpreter, TapirService} - -class ArmeriaZioTestServerInterpreter extends ArmeriaTestServerInterpreter[ZioStreams, Task, ArmeriaZioServerOptions[Task]] { - import ArmeriaZioTestServerInterpreter._ - - override def route(es: List[ServerEndpoint[ZioStreams, Task]], interceptors: Interceptors): TapirService[ZioStreams, Task] = { - val options: ArmeriaZioServerOptions[Task] = interceptors(ArmeriaZioServerOptions.customiseInterceptors).options - ArmeriaZioServerInterpreter(options).toService(es) - } -} - -object ArmeriaZioTestServerInterpreter { - implicit val runtime: Runtime[Any] = Runtime.default -} diff --git a/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ConvertStreams.scala b/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ConvertStreams.scala deleted file mode 100644 index 4145fc28db..0000000000 --- a/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ConvertStreams.scala +++ /dev/null @@ -1,151 +0,0 @@ -package sttp.tapir.server.http4s.ztapir - -import sttp.capabilities.fs2.Fs2Streams -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.CodecFormat.OctetStream -import sttp.tapir.EndpointOutput.OneOfVariant -import sttp.tapir.server.ServerEndpoint -import sttp.tapir.ztapir.ZServerEndpoint -import sttp.tapir.{Codec, Endpoint, EndpointIO, EndpointInput, EndpointOutput, Mapping, Schema, StreamBodyIO, WebSocketBodyOutput} -import zio.stream.interop.fs2z._ -import zio.{RIO, Task} - -/** Converts server endpoints using ZioStreams to endpoints using Fs2Streams */ -object ConvertStreams { - - def apply[R, C]( - se: ZServerEndpoint[R, ZioStreams with C] - ): ServerEndpoint[Fs2Streams[RIO[R, *]] with C, RIO[R, *]] = - ServerEndpoint( - Endpoint( - forInput(se.securityInput).asInstanceOf[EndpointInput[se.SECURITY_INPUT]], - forInput(se.input).asInstanceOf[EndpointInput[se.INPUT]], - forOutput(se.errorOutput).asInstanceOf[EndpointOutput[se.ERROR_OUTPUT]], - forOutput(se.output).asInstanceOf[EndpointOutput[se.OUTPUT]], - se.info - ), - se.securityLogic, - se.logic - ) - - // the occasional casts are needed as we know that we return the same input as originally, but the compiler doesn't - private def forInput(input: EndpointInput[_]): EndpointInput[_] = { - input match { - // streaming inputs - case EndpointIO.StreamBodyWrapper(wrapped) => EndpointIO.StreamBodyWrapper(apply(wrapped)) - // traversing wrapped inputs - case EndpointInput.Pair(left, right, combine, split) => EndpointInput.Pair(forInput(left), forInput(right), combine, split) - case EndpointIO.Pair(left, right, combine, split) => - EndpointIO.Pair(forInput(left).asInstanceOf[EndpointIO[_]], forInput(right).asInstanceOf[EndpointIO[_]], combine, split) - case EndpointInput.MappedPair(wrapped, mapping) => - EndpointInput.MappedPair(forInput(wrapped).asInstanceOf[EndpointInput.Pair[_, _, Any]], mapping.asInstanceOf[Mapping[Any, Any]]) - case EndpointIO.MappedPair(wrapped, mapping) => - EndpointIO.MappedPair(forInput(wrapped).asInstanceOf[EndpointIO.Pair[_, _, Any]], mapping.asInstanceOf[Mapping[Any, Any]]) - case EndpointInput.Auth(wrapped, challenge, authType, info) => - EndpointInput.Auth(forInput(wrapped).asInstanceOf[EndpointInput.Single[_]], challenge, authType, info) - case EndpointIO.OneOfBody(variants, mapping) => - EndpointIO.OneOfBody( - variants.map { - case EndpointIO.OneOfBodyVariant(range, Left(body)) => - EndpointIO.OneOfBodyVariant(range, Left(forInput(body).asInstanceOf[EndpointIO.Body[_, Any]])) - case EndpointIO.OneOfBodyVariant(range, Right(body)) => - EndpointIO.OneOfBodyVariant(range, Right(forInput(body).asInstanceOf[EndpointIO.StreamBodyWrapper[_, Any]])) - }, - mapping.asInstanceOf[Mapping[Any, Any]] - ) - // all other cases - unchanged - case _ => input - } - } - - private def forOutput(output: EndpointOutput[_]): EndpointOutput[_] = { - output match { - // streaming & ws outputs - case EndpointIO.StreamBodyWrapper(wrapped) => EndpointIO.StreamBodyWrapper(apply(wrapped)) - case EndpointOutput.WebSocketBodyWrapper(wrapped) => EndpointOutput.WebSocketBodyWrapper(apply(wrapped)) - // traversing wrapped outputs - case EndpointOutput.Pair(left, right, combine, split) => EndpointOutput.Pair(forOutput(left), forOutput(right), combine, split) - case EndpointIO.Pair(left, right, combine, split) => - EndpointIO.Pair(forOutput(left).asInstanceOf[EndpointIO[_]], forOutput(right).asInstanceOf[EndpointIO[_]], combine, split) - case EndpointOutput.MappedPair(wrapped, mapping) => - EndpointOutput.MappedPair(forOutput(wrapped).asInstanceOf[EndpointOutput.Pair[_, _, Any]], mapping.asInstanceOf[Mapping[Any, Any]]) - case EndpointIO.MappedPair(wrapped, mapping) => - EndpointIO.MappedPair(forOutput(wrapped).asInstanceOf[EndpointIO.Pair[_, _, Any]], mapping.asInstanceOf[Mapping[Any, Any]]) - case EndpointOutput.OneOf(mappings, mapping) => - EndpointOutput.OneOf[Any, Any]( - mappings.map(m => OneOfVariant(forOutput(m.output), m.appliesTo)), - mapping.asInstanceOf[Mapping[Any, Any]] - ) - case EndpointIO.OneOfBody(variants, mapping) => - EndpointIO.OneOfBody( - variants.map { - case EndpointIO.OneOfBodyVariant(range, Left(body)) => - EndpointIO.OneOfBodyVariant(range, Left(forOutput(body).asInstanceOf[EndpointIO.Body[_, Any]])) - case EndpointIO.OneOfBodyVariant(range, Right(body)) => - EndpointIO.OneOfBodyVariant(range, Right(forOutput(body).asInstanceOf[EndpointIO.StreamBodyWrapper[_, Any]])) - }, - mapping.asInstanceOf[Mapping[Any, Any]] - ) - // all other cases - unchanged - case _ => output - } - } - - private val fs2StreamsToZioStreamsCodec: Codec[fs2.Stream[Task, Byte], zio.stream.Stream[Throwable, Byte], OctetStream] = - Codec - .id[fs2.Stream[Task, Byte], OctetStream](OctetStream(), Schema.binary) - .map(_.toZStream())(_.toFs2Stream) - - private def apply[R, BS, T, S](s: StreamBodyIO[BS, T, S]): StreamBodyIO[fs2.Stream[Task, Byte], T, Fs2Streams[RIO[R, *]]] = { - // we know that BS == zio.stream.Stream[Throwable, Byte] and S == ZioStreams - val s2 = s.asInstanceOf[StreamBodyIO[zio.stream.Stream[Throwable, Byte], T, ZioStreams]] - StreamBodyIO( - Fs2Streams[RIO[R, *]], - fs2StreamsToZioStreamsCodec - .mapDecode(s2.codec.decode)(s2.codec.encode) - .schema(s2.codec.schema) - .format(s2.codec.format), - s2.info, - s2.charset, - s2.encodedExamples - ) - } - - private def fs2PipeToZioPipeCodec[A, B] - : Codec[fs2.Pipe[Task, A, B], zio.stream.Stream[Throwable, A] => zio.stream.Stream[Throwable, B], OctetStream] = - Codec - .id[fs2.Pipe[Task, A, B], OctetStream](OctetStream(), Schema.binary) - .map { (fs2Pipe: fs2.Pipe[Task, A, B]) => (zioStreamA: zio.stream.Stream[Throwable, A]) => - fs2Pipe(zioStreamA.toFs2Stream).toZStream() - } { (zioPipe: zio.stream.Stream[Throwable, A] => zio.stream.Stream[Throwable, B]) => (fs2StreamA: fs2.Stream[Task, A]) => - zioPipe(fs2StreamA.toZStream()).toFs2Stream - } - - private def apply[R, PIPE_REQ_RESP, REQ, RESP, T, S]( - w: WebSocketBodyOutput[PIPE_REQ_RESP, REQ, RESP, T, S] - ): WebSocketBodyOutput[fs2.Pipe[Task, REQ, RESP], REQ, RESP, T, Fs2Streams[RIO[R, *]]] = { - // we know that: - // * PIPE_REQ_RESP == zio.stream.Stream[Throwable, REQ] => zio.stream.Stream[Throwable, RESP] - // * S == ZioStreams - val w2 = - w.asInstanceOf[WebSocketBodyOutput[zio.stream.Stream[Throwable, REQ] => zio.stream.Stream[Throwable, RESP], REQ, RESP, T, ZioStreams]] - WebSocketBodyOutput( - Fs2Streams[RIO[R, *]], - w2.requests, - w2.responses, - fs2PipeToZioPipeCodec - .mapDecode(w2.codec.decode)(w2.codec.encode) - .schema(w2.codec.schema) - .format(w2.codec.format), - w2.info, - w2.requestsInfo, - w2.responsesInfo, - w2.concatenateFragmentedFrames, - w2.ignorePong, - w2.autoPongOnPing, - w2.decodeCloseRequests, - w2.decodeCloseResponses, - w2.autoPing - ) - } -} diff --git a/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerInterpreter.scala b/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerInterpreter.scala deleted file mode 100644 index c37b349ced..0000000000 --- a/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerInterpreter.scala +++ /dev/null @@ -1,66 +0,0 @@ -package sttp.tapir.server.http4s.ztapir - -import org.http4s.HttpRoutes -import org.http4s.server.websocket.WebSocketBuilder2 -import sttp.capabilities.WebSockets -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.http4s.{Http4sServerInterpreter, Http4sServerOptions} -import sttp.tapir.ztapir._ -import zio.RIO -import zio.blocking.Blocking -import zio.clock.Clock -import zio.interop.catz._ - -trait ZHttp4sServerInterpreter[R] { - - def zHttp4sServerOptions: Http4sServerOptions[RIO[R with Clock with Blocking, *]] = - Http4sServerOptions.default - - def from(se: ZServerEndpoint[R, ZioStreams]): ServerEndpointsToRoutes = from(List(se)) - - def from(serverEndpoints: List[ZServerEndpoint[R, ZioStreams]]): ServerEndpointsToRoutes = - new ServerEndpointsToRoutes(serverEndpoints) - - def fromWebSocket(se: ZServerEndpoint[R, ZioStreams with WebSockets]): WebSocketServerEndpointsToRoutes = fromWebSocket(List(se)) - - def fromWebSocket(serverEndpoints: List[ZServerEndpoint[R, ZioStreams with WebSockets]]): WebSocketServerEndpointsToRoutes = - new WebSocketServerEndpointsToRoutes(serverEndpoints) - - // This is needed to avoid too eager type inference. Having ZHttp4sServerInterpreter.toRoutes would require users - // to explicitly provide the env type (R) as a type argument - so that it's not automatically inferred to include - // Clock - class ServerEndpointsToRoutes( - serverEndpoints: List[ZServerEndpoint[R, ZioStreams]] - ) { - def toRoutes: HttpRoutes[RIO[R with Clock with Blocking, *]] = { - Http4sServerInterpreter(zHttp4sServerOptions).toRoutes( - serverEndpoints.map(se => ConvertStreams(se.widen[R with Clock with Blocking])) - ) - } - } - - class WebSocketServerEndpointsToRoutes( - serverEndpoints: List[ZServerEndpoint[R, ZioStreams with WebSockets]] - ) { - def toRoutes: WebSocketBuilder2[RIO[R with Clock with Blocking, *]] => HttpRoutes[RIO[R with Clock with Blocking, *]] = { - Http4sServerInterpreter(zHttp4sServerOptions).toWebSocketRoutes( - serverEndpoints.map(se => ConvertStreams(se.widen[R with Clock with Blocking])) - ) - } - } -} - -object ZHttp4sServerInterpreter { - def apply[R](): ZHttp4sServerInterpreter[R] = { - new ZHttp4sServerInterpreter[R] {} - } - - def apply[R]( - serverOptions: Http4sServerOptions[RIO[R with Clock with Blocking, *]] - ): ZHttp4sServerInterpreter[R] = { - new ZHttp4sServerInterpreter[R] { - override def zHttp4sServerOptions: Http4sServerOptions[RIO[R with Clock with Blocking, *]] = - serverOptions - } - } -} diff --git a/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/package.scala b/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/package.scala deleted file mode 100644 index f9827dd1e9..0000000000 --- a/server/http4s-server/zio1/src/main/scala/sttp/tapir/server/http4s/ztapir/package.scala +++ /dev/null @@ -1,16 +0,0 @@ -package sttp.tapir.server.http4s - -import sttp.capabilities.zio.ZioStreams -import sttp.model.sse.ServerSentEvent -import sttp.tapir.ztapir.ZioServerSentEvents -import sttp.tapir.{CodecFormat, StreamBodyIO, streamTextBody} -import zio.stream._ - -import java.nio.charset.Charset - -package object ztapir { - val serverSentEventsBody: StreamBodyIO[Stream[Throwable, Byte], Stream[Throwable, ServerSentEvent], ZioStreams] = { - streamTextBody(ZioStreams)(CodecFormat.TextEventStream(), Some(Charset.forName("UTF-8"))) - .map(ZioServerSentEvents.parseBytesToSSE)(ZioServerSentEvents.serialiseSSEToBytes) - } -} diff --git a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZEndpointTest.scala b/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZEndpointTest.scala deleted file mode 100644 index 801759ecf8..0000000000 --- a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZEndpointTest.scala +++ /dev/null @@ -1,27 +0,0 @@ -package sttp.tapir.server.http4s.ztapir - -import org.http4s.HttpRoutes -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers -import zio.{Has, RIO, ZIO} -import sttp.tapir.ztapir._ -import zio.blocking.Blocking -import zio.clock.Clock - -class ZEndpointTest extends AnyFlatSpec with Matchers { - it should "compile with widened endpoints" in { - trait Component1 - trait Component2 - type Service1 = Has[Component1] - type Service2 = Has[Component2] - - val serverEndpoint1: ZServerEndpoint[Service1, Any] = - endpoint.serverLogic(_ => ZIO.succeed(Right(())): ZIO[Service1, Nothing, Either[Unit, Unit]]) - val serverEndpoint2: ZServerEndpoint[Service2, Any] = - endpoint.serverLogic(_ => ZIO.succeed(Right(())): ZIO[Service2, Nothing, Either[Unit, Unit]]) - - type Env = Service1 with Service2 - val routes: HttpRoutes[RIO[Env with Clock with Blocking, *]] = - ZHttp4sServerInterpreter().from(List(serverEndpoint1.widen[Env], serverEndpoint2.widen[Env])).toRoutes - } -} diff --git a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerStubTest.scala b/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerStubTest.scala deleted file mode 100644 index 208ab23c30..0000000000 --- a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerStubTest.scala +++ /dev/null @@ -1,36 +0,0 @@ -package sttp.tapir.server.http4s.ztapir - -import sttp.capabilities.zio.ZioStreams -import sttp.client3.testing.SttpBackendStub -import sttp.tapir.integ.cats.effect.CatsMonadError -import sttp.tapir.server.http4s.Http4sServerOptions -import sttp.tapir.server.interceptor.CustomiseInterceptors -import sttp.tapir.server.tests.{CreateServerStubTest, ServerStubStreamingTest, ServerStubTest} -import zio.blocking.Blocking -import zio.clock.Clock -import zio.interop.catz._ -import zio.stream.ZStream -import zio.{RIO, Runtime} - -import scala.concurrent.Future - -object ZHttp4sCreateServerStubTest - extends CreateServerStubTest[ - RIO[Clock with Blocking, *], - Http4sServerOptions[RIO[Clock with Blocking, *]] - ] { - override def customiseInterceptors: CustomiseInterceptors[RIO[Clock with Blocking, *], Http4sServerOptions[RIO[Clock with Blocking, *]]] = - Http4sServerOptions - .customiseInterceptors[RIO[Clock with Blocking, *]] - - override def stub[R]: SttpBackendStub[RIO[Clock with Blocking, *], R] = SttpBackendStub(new CatsMonadError[RIO[Clock with Blocking, *]]) - override def asFuture[A]: RIO[Clock with Blocking, A] => Future[A] = rio => Runtime.default.unsafeRunToFuture(rio) -} - -class ZHttp4sServerStubTest extends ServerStubTest(ZHttp4sCreateServerStubTest) - -class ZHttp4sServerStubStreamingTest extends ServerStubStreamingTest(ZHttp4sCreateServerStubTest, ZioStreams) { - - /** Must be an instance of streams.BinaryStream */ - override def sampleStream: Any = ZStream.fromIterable(List("hello")) -} diff --git a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerTest.scala b/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerTest.scala deleted file mode 100644 index 5c15e3e06a..0000000000 --- a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sServerTest.scala +++ /dev/null @@ -1,63 +0,0 @@ -package sttp.tapir.server.http4s.ztapir - -import cats.effect._ -import org.scalatest.OptionValues -import sttp.capabilities.zio.ZioStreams -import sttp.client3._ -import sttp.model.sse.ServerSentEvent -import sttp.monad.MonadError -import sttp.tapir._ -import sttp.tapir.integ.cats.effect.CatsMonadError -import sttp.tapir.server.tests._ -import sttp.tapir.tests.{Test, TestSuite} -import zio.{RIO, Task, UIO} -import zio.blocking.Blocking -import zio.clock.Clock -import zio.interop.catz._ -import org.scalatest.matchers.should.Matchers._ -import sttp.capabilities.fs2.Fs2Streams -import sttp.tapir.server.http4s.Http4sServerSentEvents - -import java.util.UUID -import scala.util.Random - -class ZHttp4sServerTest extends TestSuite with OptionValues { - - override def tests: Resource[IO, List[Test]] = backendResource.map { backend => - implicit val m: MonadError[RIO[Clock with Blocking, *]] = new CatsMonadError[RIO[Clock with Blocking, *]] - - val interpreter = new ZHttp4sTestServerInterpreter() - val createServerTest = new DefaultCreateServerTest(backend, interpreter) - - def randomUUID = Some(UUID.randomUUID().toString) - val sse1 = ServerSentEvent(randomUUID, randomUUID, randomUUID, Some(Random.nextInt(200))) - val sse2 = ServerSentEvent(randomUUID, randomUUID, randomUUID, Some(Random.nextInt(200))) - - def additionalTests(): List[Test] = List( - createServerTest.testServer( - endpoint.out(serverSentEventsBody), - "Send and receive SSE" - )((_: Unit) => UIO(Right(zio.stream.Stream(sse1, sse2)))) { (backend, baseUri) => - basicRequest - .response(asStream[IO, List[ServerSentEvent], Fs2Streams[IO]](Fs2Streams[IO]) { stream => - Http4sServerSentEvents - .parseBytesToSSE[IO] - .apply(stream) - .compile - .toList - }) - .get(baseUri) - .send(backend) - .map(_.body.right.toOption.value shouldBe List(sse1, sse2)) - } - ) - - new AllServerTests(createServerTest, interpreter, backend).tests() ++ - new ServerStreamingTests(createServerTest, maxLengthSupported = false).tests(ZioStreams)(_ => Task.unit) ++ - new ServerWebSocketTests(createServerTest, ZioStreams) { - override def functionToPipe[A, B](f: A => B): streams.Pipe[A, B] = in => in.map(f) - override def emptyPipe[A, B]: streams.Pipe[A, B] = _ => zio.stream.Stream.empty - }.tests() ++ - additionalTests() - } -} diff --git a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala b/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala deleted file mode 100644 index b97c19c154..0000000000 --- a/server/http4s-server/zio1/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala +++ /dev/null @@ -1,63 +0,0 @@ -package sttp.tapir.server.http4s.ztapir - -import cats.data.NonEmptyList -import cats.effect.{IO, Resource} -import cats.syntax.all._ -import org.http4s.blaze.server.BlazeServerBuilder -import org.http4s.server.websocket.WebSocketBuilder2 -import org.http4s.{HttpApp, HttpRoutes} -import sttp.capabilities.WebSockets -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.http4s.Http4sServerOptions -import sttp.tapir.server.http4s.ztapir.ZHttp4sTestServerInterpreter._ -import sttp.tapir.server.tests.TestServerInterpreter -import sttp.tapir.tests._ -import sttp.tapir.ztapir.ZServerEndpoint -import zio.RIO -import zio.blocking.Blocking -import zio.clock.Clock -import zio.interop.catz._ -import zio.interop.catz.implicits._ - -import scala.concurrent.ExecutionContext -import scala.concurrent.duration.FiniteDuration - -object ZHttp4sTestServerInterpreter { - type F[A] = RIO[Clock with Blocking, A] - type Routes = WebSocketBuilder2[F] => HttpRoutes[F] - type ServerOptions = Http4sServerOptions[F] -} - -class ZHttp4sTestServerInterpreter extends TestServerInterpreter[F, ZioStreams with WebSockets, ServerOptions, Routes] { - implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global - - override def route(es: List[ZServerEndpoint[Clock with Blocking, ZioStreams with WebSockets]], interceptors: Interceptors): Routes = { - val serverOptions: ServerOptions = interceptors(Http4sServerOptions.customiseInterceptors).options - ZHttp4sServerInterpreter(serverOptions).fromWebSocket(es).toRoutes - } - - override def serverWithStop( - routes: NonEmptyList[Routes], - gracefulShutdownTimeout: Option[FiniteDuration] - ): Resource[IO, (Port, KillSwitch)] = { - val service: WebSocketBuilder2[RIO[Clock with Blocking, *]] => HttpApp[RIO[Clock with Blocking, *]] = - wsb => routes.map(_.apply(wsb)).reduceK.orNotFound - - val serverResource = BlazeServerBuilder[RIO[Clock with Blocking, *]] - .withExecutionContext(ExecutionContext.global) - .bindHttp(0, "localhost") - .withHttpWebSocketApp(service) - .resource - .map(_.address.getPort) - - // Converting a zio.RIO-resource to an cats.IO-resource - val runtime = implicitly[zio.Runtime[Clock with Blocking]] - Resource - .eval(IO.fromFuture(IO(runtime.unsafeRunToFuture(serverResource.allocated)))) - .flatMap { case (port, release) => // Blaze has no graceful shutdown support https://github.com/http4s/blaze/issues/676 - Resource.make(IO.pure((port, IO.fromFuture(IO(runtime.unsafeRunToFuture(release)))))) { case (_, release) => - release - } - } - } -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpBodyListener.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpBodyListener.scala deleted file mode 100644 index cb2780e689..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpBodyListener.scala +++ /dev/null @@ -1,20 +0,0 @@ -package sttp.tapir.server.ziohttp - -import sttp.tapir.server.interpreter.BodyListener -import zio.RIO -import zio.stream.ZStream - -import scala.util.{Failure, Success, Try} - -class ZioHttpBodyListener[R] extends BodyListener[RIO[R, *], ZioHttpResponseBody] { - override def onComplete(body: ZioHttpResponseBody)(cb: Try[Unit] => RIO[R, Unit]): RIO[R, ZioHttpResponseBody] = - RIO - .access[R] - .apply { r => - val (stream, contentLength) = body - ( - stream.onError(cause => cb(Failure(cause.squash)).orDie.provide(r)) ++ ZStream.fromEffect(cb(Success(()))).provide(r).drain, - contentLength - ) - } -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpInterpreter.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpInterpreter.scala deleted file mode 100644 index caae3b5980..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpInterpreter.scala +++ /dev/null @@ -1,80 +0,0 @@ -package sttp.tapir.server.ziohttp - -import io.netty.handler.codec.http.HttpResponseStatus -import sttp.capabilities.zio.ZioStreams -import sttp.model.{HeaderNames, Header => SttpHeader} -import sttp.monad.MonadError -import sttp.tapir.server.interceptor.RequestResult -import sttp.tapir.server.interceptor.reject.RejectInterceptor -import sttp.tapir.server.interpreter.{FilterServerEndpoints, ServerInterpreter} -import sttp.tapir.ztapir._ -import zhttp.http.{Http, HttpData, Request, Response, Status, Header => ZioHttpHeader, Headers => ZioHttpHeaders} -import zio._ - -trait ZioHttpInterpreter[R] { - - def zioHttpServerOptions: ZioHttpServerOptions[R] = ZioHttpServerOptions.default - - def toHttp[R2](se: ZServerEndpoint[R2, ZioStreams]): Http[R & R2, Throwable, Request, Response] = - toHttp(List(se)) - - def toHttp[R2](ses: List[ZServerEndpoint[R2, ZioStreams]]): Http[R & R2, Throwable, Request, Response] = { - implicit val bodyListener: ZioHttpBodyListener[R & R2] = new ZioHttpBodyListener[R & R2] - implicit val monadError: MonadError[RIO[R & R2, *]] = new RIOMonadError[R & R2] - val widenedSes = ses.map(_.widen[R & R2]) - val widenedServerOptions = zioHttpServerOptions.widen[R & R2] - - val interpreter = new ServerInterpreter[ZioStreams, RIO[R & R2, *], ZioHttpResponseBody, ZioStreams]( - FilterServerEndpoints(widenedSes), - new ZioHttpRequestBody(widenedServerOptions), - new ZioHttpToResponseBody, - RejectInterceptor.disableWhenSingleEndpoint(widenedServerOptions.interceptors, widenedSes), - zioHttpServerOptions.deleteFile - ) - - Http.collectHttp[Request] { case req => - Http - .fromZIO( - interpreter - .apply(ZioHttpServerRequest(req)) - .map { - case RequestResult.Response(resp) => - val baseHeaders = resp.headers.groupBy(_.name).map(sttpToZioHttpHeader).toList - val allHeaders = resp.body match { - case Some((_, Some(contentLength))) if resp.contentLength.isEmpty => - (HeaderNames.ContentLength, contentLength.toString) :: baseHeaders - case _ => baseHeaders - } - - Http.succeed( - Response( - status = Status.fromHttpResponseStatus(HttpResponseStatus.valueOf(resp.code.code)), - headers = ZioHttpHeaders(allHeaders), - data = resp.body.map { case (stream, _) => HttpData.fromStream(stream) }.getOrElse(HttpData.empty) - ) - ) - case RequestResult.Failure(_) => Http.empty - } - ) - .flatten - } - } - - private def sttpToZioHttpHeader(hl: (String, Seq[SttpHeader])): ZioHttpHeader = - (hl._1, hl._2.map(f => f.value).mkString(", ")) - -} - -object ZioHttpInterpreter { - def apply[R](serverOptions: ZioHttpServerOptions[R]): ZioHttpInterpreter[R] = { - new ZioHttpInterpreter[R] { - override def zioHttpServerOptions: ZioHttpServerOptions[R] = serverOptions - } - } - - def apply(): ZioHttpInterpreter[Any] = { - new ZioHttpInterpreter[Any] { - override def zioHttpServerOptions: ZioHttpServerOptions[Any] = ZioHttpServerOptions.default[Any] - } - } -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpRequestBody.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpRequestBody.scala deleted file mode 100644 index 4b178e546a..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpRequestBody.scala +++ /dev/null @@ -1,48 +0,0 @@ -package sttp.tapir.server.ziohttp - -import sttp.capabilities -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.{FileRange, InputStreamRange} -import sttp.tapir.RawBodyType -import sttp.tapir.model.ServerRequest -import sttp.tapir.server.interpreter.RawValue -import sttp.tapir.server.interpreter.RequestBody -import zhttp.http.Request -import zio.{RIO, Task, ZIO} -import zio.blocking.Blocking -import zio.stream.{Stream, ZSink, ZStream} - -import java.io.ByteArrayInputStream -import java.nio.ByteBuffer - -class ZioHttpRequestBody[R](serverOptions: ZioHttpServerOptions[R]) extends RequestBody[RIO[R, *], ZioStreams] { - override val streams: capabilities.Streams[ZioStreams] = ZioStreams - - override def toRaw[RAW](serverRequest: ServerRequest, bodyType: RawBodyType[RAW], maxBytes: Option[Long]): Task[RawValue[RAW]] = bodyType match { - case RawBodyType.StringBody(defaultCharset) => asByteArray(serverRequest).map(new String(_, defaultCharset)).map(RawValue(_)) - case RawBodyType.ByteArrayBody => asByteArray(serverRequest).map(RawValue(_)) - case RawBodyType.ByteBufferBody => asByteArray(serverRequest).map(bytes => ByteBuffer.wrap(bytes)).map(RawValue(_)) - case RawBodyType.InputStreamBody => asByteArray(serverRequest).map(new ByteArrayInputStream(_)).map(RawValue(_)) - case RawBodyType.InputStreamRangeBody => - asByteArray(serverRequest).map(bytes => new InputStreamRange(() => new ByteArrayInputStream(bytes))).map(RawValue(_)) - case RawBodyType.FileBody => - for { - tmpFile <- serverOptions.createFile(serverRequest) - _ <- toStream(serverRequest, None).asInstanceOf[Stream[Throwable, Byte]].run(ZSink.fromFile(tmpFile.toPath)).provideLayer(Blocking.live) - } yield { - val fileRange = FileRange(tmpFile) - RawValue(fileRange, Seq(fileRange)) - } - case RawBodyType.MultipartBody(_, _) => ZIO.fail(new UnsupportedOperationException("Multipart is not supported")) - } - - override def toStream(serverRequest: ServerRequest, maxBytes: Option[Long]): streams.BinaryStream = - stream(serverRequest).asInstanceOf[streams.BinaryStream] - - private def asByteArray(serverRequest: ServerRequest): Task[Array[Byte]] = zioHttpRequest(serverRequest).body.map(_.toArray) - - private def stream(serverRequest: ServerRequest): Stream[Throwable, Byte] = - ZStream.fromEffect(zioHttpRequest(serverRequest).body).flattenChunks - - private def zioHttpRequest(serverRequest: ServerRequest) = serverRequest.underlying.asInstanceOf[Request] -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerOptions.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerOptions.scala deleted file mode 100644 index 21861700b2..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerOptions.scala +++ /dev/null @@ -1,39 +0,0 @@ -package sttp.tapir.server.ziohttp - -import sttp.tapir.model.ServerRequest -import sttp.tapir.server.interceptor.{CustomiseInterceptors, Interceptor} -import sttp.tapir.{Defaults, TapirFile} -import zio.{RIO, Task} - -case class ZioHttpServerOptions[R]( - createFile: ServerRequest => Task[TapirFile], - deleteFile: TapirFile => RIO[R, Unit], - interceptors: List[Interceptor[RIO[R, *]]] -) { - def prependInterceptor(i: Interceptor[RIO[R, *]]): ZioHttpServerOptions[R] = - copy(interceptors = i :: interceptors) - def appendInterceptor(i: Interceptor[RIO[R, *]]): ZioHttpServerOptions[R] = - copy(interceptors = interceptors :+ i) - - def widen[R2 <: R]: ZioHttpServerOptions[R2] = this.asInstanceOf[ZioHttpServerOptions[R2]] -} - -object ZioHttpServerOptions { - - /** Allows customising the interceptors used by the server interpreter. */ - def customiseInterceptors[R]: CustomiseInterceptors[RIO[R, *], ZioHttpServerOptions[R]] = - CustomiseInterceptors( - createOptions = (ci: CustomiseInterceptors[RIO[R, *], ZioHttpServerOptions[R]]) => - ZioHttpServerOptions( - defaultCreateFile, - defaultDeleteFile, - ci.interceptors - ) - ) - - def defaultCreateFile: ServerRequest => Task[TapirFile] = _ => Task.effect(Defaults.createTempFile()) - - def defaultDeleteFile[R]: TapirFile => Task[Unit] = file => Task.effect(Defaults.deleteFile()(file)) - - def default[R]: ZioHttpServerOptions[R] = customiseInterceptors.options -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerRequest.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerRequest.scala deleted file mode 100644 index 4870ccd559..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpServerRequest.scala +++ /dev/null @@ -1,30 +0,0 @@ -package sttp.tapir.server.ziohttp - -import sttp.model.{QueryParams, Uri, Header => SttpHeader, Method => SttpMethod} -import sttp.tapir.{AttributeKey, AttributeMap} -import sttp.tapir.model.{ConnectionInfo, ServerRequest} -import zhttp.http.Request - -import java.net.InetSocketAddress -import scala.collection.immutable.Seq - -case class ZioHttpServerRequest(req: Request, attributes: AttributeMap = AttributeMap.Empty) extends ServerRequest { - override def protocol: String = "HTTP/1.1" // missing field in request - - private def remote: Option[InetSocketAddress] = - for { - host <- req.url.host - port <- req.url.port - } yield new InetSocketAddress(host, port) - - override lazy val connectionInfo: ConnectionInfo = ConnectionInfo(None, remote, None) - override def underlying: Any = req - override lazy val pathSegments: List[String] = req.url.path.toList - override lazy val queryParameters: QueryParams = QueryParams.fromMultiMap(req.url.queryParams) - override lazy val method: SttpMethod = SttpMethod(req.method.toJava.name().toUpperCase) - override lazy val uri: Uri = Uri.unsafeParse(req.url.encode) - override lazy val headers: Seq[SttpHeader] = req.headers.toList.map { case (k, v) => SttpHeader(k, v) } - override def attribute[T](k: AttributeKey[T]): Option[T] = attributes.get(k) - override def attribute[T](k: AttributeKey[T], v: T): ZioHttpServerRequest = copy(attributes = attributes.put(k, v)) - override def withUnderlying(underlying: Any): ServerRequest = ZioHttpServerRequest(req = underlying.asInstanceOf[Request], attributes) -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpToResponseBody.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpToResponseBody.scala deleted file mode 100644 index aa42fdd01a..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpToResponseBody.scala +++ /dev/null @@ -1,61 +0,0 @@ -package sttp.tapir.server.ziohttp - -import sttp.capabilities.zio.ZioStreams -import sttp.model.HasHeaders -import sttp.tapir.server.interpreter.ToResponseBody -import sttp.tapir.{CodecFormat, FileRange, RawBodyType, WebSocketBodyOutput} -import zio.Chunk -import zio.blocking.Blocking -import zio.stream.{Stream, ZStream} - -import java.nio.charset.Charset - -class ZioHttpToResponseBody extends ToResponseBody[ZioHttpResponseBody, ZioStreams] { - override val streams: ZioStreams = ZioStreams - - override def fromRawValue[R](v: R, headers: HasHeaders, format: CodecFormat, bodyType: RawBodyType[R]): ZioHttpResponseBody = - rawValueToEntity(bodyType, v) - - override def fromStreamValue( - v: streams.BinaryStream, - headers: HasHeaders, - format: CodecFormat, - charset: Option[Charset] - ): ZioHttpResponseBody = (v, None) - - override def fromWebSocketPipe[REQ, RESP]( - pipe: streams.Pipe[REQ, RESP], - o: WebSocketBodyOutput[streams.Pipe[REQ, RESP], REQ, RESP, _, ZioStreams] - ): ZioHttpResponseBody = - (Stream.empty, None) // TODO - - private def rawValueToEntity[R](bodyType: RawBodyType[R], r: R): ZioHttpResponseBody = { - bodyType match { - case RawBodyType.StringBody(charset) => - val bytes = r.toString.getBytes(charset) - (ZStream.fromIterable(bytes), Some(bytes.length.toLong)) - case RawBodyType.ByteArrayBody => (Stream.fromChunk(Chunk.fromArray(r)), Some((r: Array[Byte]).length.toLong)) - case RawBodyType.ByteBufferBody => (Stream.fromChunk(Chunk.fromByteBuffer(r)), None) - case RawBodyType.InputStreamBody => (ZStream.fromInputStream(r).provideLayer(Blocking.live), None) - case RawBodyType.InputStreamRangeBody => - r.range - .map(range => - ( - ZStream.fromInputStream(r.inputStreamFromRangeStart()).take(range.contentLength).provideLayer(Blocking.live), - Some(range.contentLength) - ) - ) - .getOrElse((ZStream.fromInputStream(r.inputStream()).provideLayer(Blocking.live), None)) - case RawBodyType.FileBody => - val tapirFile = r: FileRange - tapirFile.range - .flatMap(r => - r.startAndEnd.map(s => - (ZStream.fromFile(tapirFile.file.toPath).drop(s._1).take(r.contentLength).provideLayer(Blocking.live), Some(r.contentLength)) - ) - ) - .getOrElse((ZStream.fromFile(tapirFile.file.toPath).provideLayer(Blocking.live), Some(tapirFile.file.length))) - case RawBodyType.MultipartBody(_, _) => throw new UnsupportedOperationException("Multipart is not supported") - } - } -} diff --git a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ziohttp.scala b/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ziohttp.scala deleted file mode 100644 index d9d06a7ea4..0000000000 --- a/server/zio1-http-server/src/main/scala/sttp/tapir/server/ziohttp/ziohttp.scala +++ /dev/null @@ -1,8 +0,0 @@ -package sttp.tapir.server - -import zio.stream.ZStream - -package object ziohttp { - // a stream with optional length (if known) - private[ziohttp] type ZioHttpResponseBody = (ZStream[Any, Throwable, Byte], Option[Long]) -} diff --git a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpCompositionTest.scala b/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpCompositionTest.scala deleted file mode 100644 index acc26c06b4..0000000000 --- a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpCompositionTest.scala +++ /dev/null @@ -1,37 +0,0 @@ -package sttp.tapir.server.ziohttp - -import cats.data.NonEmptyList -import sttp.client3._ -import sttp.model.StatusCode -import sttp.tapir.server.tests.CreateServerTest -import sttp.tapir.ztapir._ -import zhttp.http._ -import zio.{Task, ZIO} -import org.scalatest.matchers.should.Matchers._ - -class ZioHttpCompositionTest( - createServerTest: CreateServerTest[Task, Any, ZioHttpServerOptions[Any], Http[Any, Throwable, zhttp.http.Request, zhttp.http.Response]] -) { - import createServerTest._ - - def tests() = List( - testServer( - "zio http apps compose after creation", { - val ep1 = endpoint.get.in("p1").zServerLogic[Any](_ => ZIO.unit) - val ep3 = endpoint.get.in("p3").zServerLogic[Any](_ => ZIO.fail(new RuntimeException("boom"))) - - val route1: RHttpApp[Any] = ZioHttpInterpreter().toHttp(ep1) - val route2: RHttpApp[Any] = Http.collect { case Method.GET -> !! / "p2" => - zhttp.http.Response.ok - } - val route3: RHttpApp[Any] = ZioHttpInterpreter().toHttp(ep3) - - NonEmptyList.of(route3, route1, route2) - } - ) { (backend, baseUri) => - basicRequest.get(uri"$baseUri/p1").send(backend).map(_.code shouldBe StatusCode.Ok) >> - basicRequest.get(uri"$baseUri/p2").send(backend).map(_.code shouldBe StatusCode.Ok) >> - basicRequest.get(uri"$baseUri/p3").send(backend).map(_.code shouldBe StatusCode.BadRequest) - } - ) -} diff --git a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerStubTest.scala b/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerStubTest.scala deleted file mode 100644 index d5e34132c9..0000000000 --- a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerStubTest.scala +++ /dev/null @@ -1,25 +0,0 @@ -package sttp.tapir.server.ziohttp - -import sttp.capabilities.zio.ZioStreams -import sttp.client3.testing.SttpBackendStub -import sttp.tapir.server.interceptor.CustomiseInterceptors -import sttp.tapir.server.tests.{CreateServerStubTest, ServerStubStreamingTest, ServerStubTest} -import sttp.tapir.ztapir.RIOMonadError -import zio.stream.ZStream -import zio.{Runtime, Schedule, Task} - -import scala.concurrent.Future - -object ZioHttpCreateServerStubTest extends CreateServerStubTest[Task, ZioHttpServerOptions[Any]] { - override def customiseInterceptors: CustomiseInterceptors[Task, ZioHttpServerOptions[Any]] = ZioHttpServerOptions.customiseInterceptors - override def stub[R]: SttpBackendStub[Task, R] = SttpBackendStub(new RIOMonadError[Any]) - override def asFuture[A]: Task[A] => Future[A] = task => Runtime.default.unsafeRunToFuture(task) -} - -class ZioHttpServerStubTest extends ServerStubTest(ZioHttpCreateServerStubTest) - -class ZioHttpServerStubStreamingTest extends ServerStubStreamingTest(ZioHttpCreateServerStubTest, ZioStreams) { - - /** Must be an instance of streams.BinaryStream */ - override def sampleStream: Any = ZStream("1").repeat(Schedule.forever).take(60000 * 1024) -} diff --git a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerTest.scala b/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerTest.scala deleted file mode 100644 index 53224d17dd..0000000000 --- a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpServerTest.scala +++ /dev/null @@ -1,63 +0,0 @@ -package sttp.tapir.server.ziohttp - -import cats.effect.{IO, Resource} -import io.netty.util.CharsetUtil -import org.scalactic.source.Position.here -import org.scalatest.compatible.Assertion -import org.scalatest.matchers.should.Matchers._ -import sttp.capabilities.zio.ZioStreams -import sttp.monad.MonadError -import sttp.tapir.server.tests._ -import sttp.tapir.tests.{Test, TestSuite} -import sttp.tapir.ztapir._ -import zhttp.http._ -import zhttp.service.server.ServerChannelFactory -import zhttp.service.{EventLoopGroup, ServerChannelFactory} -import zio.interop.catz._ -import zio.{Runtime, Task, UIO, ZIO} - -class ZioHttpServerTest extends TestSuite { - - override def tests: Resource[IO, List[Test]] = backendResource.flatMap { backend => - implicit val r: Runtime[Any] = Runtime.default - // creating the netty dependencies once, to speed up tests - (EventLoopGroup.auto(0) ++ ServerChannelFactory.auto).build.toResource[IO].map { - (nettyDeps: EventLoopGroup with ServerChannelFactory) => - val interpreter = new ZioHttpTestServerInterpreter(nettyDeps) - val createServerTest = new DefaultCreateServerTest(backend, interpreter) - - def additionalTests(): List[Test] = List( - // https://github.com/softwaremill/tapir/issues/1914 - Test("zio http route can be used as a function") { - val ep = endpoint.get.in("p1").out(stringBody).zServerLogic[Any](_ => ZIO.succeed("response")) - val route = ZioHttpInterpreter().toHttp(ep) - val test: UIO[Assertion] = route(Request(url = URL.apply(Path.empty / "p1"))) - .flatMap(response => response.data.toByteBuf.map(_.toString(CharsetUtil.UTF_8))) - .map(_ shouldBe "response") - .catchAll(_ => ZIO.succeed(fail("Unable to extract body from Http response"))) - zio.Runtime.default.unsafeRunToFuture(test) - } - ) - - implicit val m: MonadError[Task] = new RIOMonadError[Any] - - new ServerBasicTests( - createServerTest, - interpreter, - multipleValueHeaderSupport = false, - inputStreamSupport = true, - supportsUrlEncodedPathSegments = false, - supportsMultipleSetCookieHeaders = false, - invulnerableToUnsanitizedHeaders = false - ).tests() ++ - // TODO: re-enable static content once a newer zio http is available. Currently these tests often fail with: - // Cause: java.io.IOException: parsing HTTP/1.1 status line, receiving [f2 content], parser state [STATUS_LINE] - new AllServerTests(createServerTest, interpreter, backend, basic = false, staticContent = false, multipart = false, file = true) - .tests() ++ - new ServerStreamingTests(createServerTest, maxLengthSupported = false).tests(ZioStreams)(_ => Task.unit) ++ - new ZioHttpCompositionTest(createServerTest).tests() // ++ - // TODO: only works with zio2 - // additionalTests() - } - } -} diff --git a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpTestServerInterpreter.scala b/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpTestServerInterpreter.scala deleted file mode 100644 index b6a51d2951..0000000000 --- a/server/zio1-http-server/src/test/scala/sttp/tapir/server/ziohttp/ZioHttpTestServerInterpreter.scala +++ /dev/null @@ -1,39 +0,0 @@ -package sttp.tapir.server.ziohttp - -import cats.data.NonEmptyList -import cats.effect.{IO, Resource} -import sttp.capabilities.zio.ZioStreams -import sttp.tapir.server.ServerEndpoint -import sttp.tapir.server.tests.TestServerInterpreter -import sttp.tapir.tests._ -import zhttp.http._ -import zhttp.service.{EventLoopGroup, Server, ServerChannelFactory} -import zio._ -import zio.interop.catz._ -import scala.concurrent.duration.FiniteDuration - -class ZioHttpTestServerInterpreter(nettyDeps: EventLoopGroup with ServerChannelFactory) - extends TestServerInterpreter[Task, ZioStreams, ZioHttpServerOptions[Any], Http[Any, Throwable, Request, Response]] { - - override def route(es: List[ServerEndpoint[ZioStreams, Task]], interceptors: Interceptors): Http[Any, Throwable, Request, Response] = { - val serverOptions: ZioHttpServerOptions[Any] = interceptors(ZioHttpServerOptions.customiseInterceptors).options - ZioHttpInterpreter(serverOptions).toHttp(es) - } - - override def serverWithStop( - routes: NonEmptyList[Http[Any, Throwable, Request, Response]], - gracefulShutdownTimeout: Option[FiniteDuration] - ): Resource[IO, (Port, KillSwitch)] = { - implicit val r: Runtime[Any] = Runtime.default - val server: Server[Any, Throwable] = Server.app(routes.toList.reduce(_ ++ _)) - // ZIO HTTP 1.x doesn't offer graceful shutdown with timeout OOTB - Resource.make( - Server - .make(server ++ Server.port(0)) - .provide(nettyDeps) - .map(_.port) - .toResource[IO] - .allocated - ) { case (_, release) => release } - } -} From 76b1e8cc449cd61a6b30d00f975b188c3bb8690e Mon Sep 17 00:00:00 2001 From: kciesielski Date: Tue, 5 Dec 2023 13:06:43 +0100 Subject: [PATCH 2/2] Remove zio1 JS tests --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7695b6b8ad..0245a087aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,10 +91,10 @@ jobs: # https://github.com/scala-js/scala-js/issues/4317 has a solution - name: Test if: matrix.target-platform == 'JS' && matrix.scala-version == '2.12' - run: sbt $SBT_JAVA_OPTS catsJS2_12/test circeJsonJS2_12/test clientCoreJS2_12/test clientTestsJS2_12/test coreJS2_12/test enumeratumJS2_12/test jsoniterScalaJS2_12/test newtypeJS2_12/test openapiDocsJS2_12/test playJsonJS2_12/test redocJS2_12/test serverCoreJS2_12/test sttpClientJS2_12/test testingJS2_12/test testsJS2_12/test uPickleJsonJS2_12/test zio1JsonJS2_12/test zioJsonJS2_12/test + run: sbt $SBT_JAVA_OPTS catsJS2_12/test circeJsonJS2_12/test clientCoreJS2_12/test clientTestsJS2_12/test coreJS2_12/test enumeratumJS2_12/test jsoniterScalaJS2_12/test newtypeJS2_12/test openapiDocsJS2_12/test playJsonJS2_12/test redocJS2_12/test serverCoreJS2_12/test sttpClientJS2_12/test testingJS2_12/test testsJS2_12/test uPickleJsonJS2_12/test zioJsonJS2_12/test - name: Test if: matrix.target-platform == 'JS' && matrix.scala-version == '2.13' - run: sbt $SBT_JAVA_OPTS catsJS/test circeJsonJS/test clientCoreJS/test clientTestsJS/test coreJS/test enumeratumJS/test jsoniterScalaJS/test newtypeJS/test openapiDocsJS/test playJsonJS/test redocJS/test serverCoreJS/test sttpClientJS/test testingJS/test testsJS/test uPickleJsonJS/test zio1JsonJS/test zioJsonJS/test + run: sbt $SBT_JAVA_OPTS catsJS/test circeJsonJS/test clientCoreJS/test clientTestsJS/test coreJS/test enumeratumJS/test jsoniterScalaJS/test newtypeJS/test openapiDocsJS/test playJsonJS/test redocJS/test serverCoreJS/test sttpClientJS/test testingJS/test testsJS/test uPickleJsonJS/test zioJsonJS/test - name: Test if: matrix.target-platform == 'JS' && matrix.scala-version == '3' run: sbt $SBT_JAVA_OPTS catsJS3/test circeJsonJS3/test clientCoreJS3/test clientTestsJS3/test coreJS3/test jsoniterScalaJS3/test openapiDocsJS3/test redocJS3/test serverCoreJS3/test sttpClientJS3/test testingJS3/test testsJS3/test uPickleJsonJS3/test zioJsonJS3/test