Skip to content

Commit 8d9e390

Browse files
committed
Implement multipart for netty-future, netty-cats & netty-zio
1 parent a4343ea commit 8d9e390

33 files changed

Lines changed: 436 additions & 297 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
steps:
3939
- name: Checkout
4040
uses: actions/checkout@v6
41-
- uses: sbt/setup-sbt@af116cce31c00823d3903ce687f9cda3a4f19f1b # v1, specifically v1.2.1
41+
- uses: sbt/setup-sbt@18c5326132e2b0900fd6bd70f8b2b212364d11c8 # v1, specifically v1.3.0
4242
- name: Set up JDK
4343
uses: actions/setup-java@v5
4444
with:
@@ -169,7 +169,7 @@ jobs:
169169
steps:
170170
- name: Checkout
171171
uses: actions/checkout@v6
172-
- uses: sbt/setup-sbt@af116cce31c00823d3903ce687f9cda3a4f19f1b # v1, specifically v1.2.1
172+
- uses: sbt/setup-sbt@18c5326132e2b0900fd6bd70f8b2b212364d11c8 # v1, specifically v1.3.0
173173
- name: Set up JDK
174174
uses: actions/setup-java@v5
175175
with:

.github/workflows/dependency-graph.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ jobs:
1515
runs-on: ubuntu-24.04
1616
steps:
1717
- uses: actions/checkout@v6
18-
- uses: sbt/setup-sbt@af116cce31c00823d3903ce687f9cda3a4f19f1b # v1, specifically v1.2.1
18+
- uses: sbt/setup-sbt@18c5326132e2b0900fd6bd70f8b2b212364d11c8 # v1, specifically v1.3.0
1919
- uses: scalacenter/sbt-dependency-submission@d84eef4c09e633bcf5f113bcad7fd5e9af1baee9 # v3, specifically v3.1.1

build.sbt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,9 @@ lazy val tests: ProjectMatrix = (projectMatrix in file("tests"))
546546
"com.softwaremill.common" %%% "tagging" % "2.3.5",
547547
scalaTest.value,
548548
logback
549-
)
549+
),
550+
scalacOptions += "-Wconf:msg=unused value of type org.scalatest.Assertion:s",
551+
scalacOptions += "-Wconf:msg=unused value of type org.scalatest.compatible.Assertion:s"
550552
)
551553
.jvmPlatform(
552554
scalaVersions = scala2And3Versions,
@@ -1646,6 +1648,7 @@ lazy val nettyServer: ProjectMatrix = (projectMatrix in file("server/netty-serve
16461648
"io.netty" % "netty-all" % Versions.nettyAll,
16471649
"org.playframework.netty" % "netty-reactive-streams-http" % Versions.nettyReactiveStreams,
16481650
"org.apache.httpcomponents" % "httpmime" % "4.5.14",
1651+
"org.scala-lang.modules" %% "scala-java8-compat" % Versions.scalaJava8Compat,
16491652
slf4j
16501653
)
16511654
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.12.11
1+
sbt.version=1.12.12

perf-tests/perf-tests-e2e/src/main/scala/sttp/tapir/perf/netty/cats/NettyCats.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import sttp.tapir.server.netty.cats.NettyCatsServer
1212
import sttp.tapir.server.netty.cats.NettyCatsServerOptions
1313
import sttp.capabilities.fs2.Fs2Streams
1414

15-
import scala.concurrent.duration._
16-
1715
object Tapir extends Endpoints {
1816
val wsResponseStream = Stream.fixedRate[IO](WebSocketSingleResponseLag, dampen = false)
1917
val wsEndpoint = wsBaseEndpoint

project/Versions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ object Versions {
4949
val play29Server = "2.9.11"
5050
val tethys = "0.29.8"
5151
val vertx = "5.1.2"
52-
val jsScalaJavaTime = "2.6.0"
53-
val nativeScalaJavaTime = "2.6.0"
52+
val jsScalaJavaTime = "2.7.0"
53+
val nativeScalaJavaTime = "2.7.0"
5454
val jwtScala = "10.0.4"
5555
val derevo = "0.14.0"
5656
val newtype = "0.4.4"

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.12.11
1+
sbt.version=1.12.12
Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
package sttp.tapir.server.netty.cats.internal
22

33
import cats.effect.Async
4-
import cats.syntax.all._
54
import fs2.Chunk
65
import fs2.io.file.{Files, Path}
76
import io.netty.handler.codec.http.HttpContent
8-
import org.playframework.netty.http.StreamedHttpRequest
97
import org.reactivestreams.Publisher
108
import sttp.capabilities.fs2.Fs2Streams
119
import sttp.monad.MonadError
1210
import sttp.tapir.integ.cats.effect.CatsMonadError
1311
import sttp.tapir.model.ServerRequest
14-
import sttp.tapir.server.interpreter.RawValue
1512
import sttp.tapir.server.netty.internal.{NettyStreamingRequestBody, StreamCompatible}
16-
import sttp.tapir.{RawBodyType, RawPart, TapirFile}
13+
import sttp.tapir.{RawPart, TapirFile}
14+
15+
import java.nio.file.{Files => JFiles}
16+
import scala.util.Try
1717

1818
private[cats] class NettyCatsRequestBody[F[_]: Async](
1919
val createFile: ServerRequest => F[TapirFile],
20-
val streamCompatible: StreamCompatible[Fs2Streams[F]]
21-
) extends NettyStreamingRequestBody[F, Fs2Streams[F]] {
20+
val streamCompatible: StreamCompatible[Fs2Streams[F]],
21+
multipartTempDirectory: Option[TapirFile] = None,
22+
multipartMinSizeForDisk: Option[Long] = None
23+
) extends NettyStreamingRequestBody[F, Fs2Streams[F]](
24+
multipartTempDirectory,
25+
multipartMinSizeForDisk
26+
) {
2227

2328
override implicit val monad: MonadError[F] = new CatsMonadError()
2429

30+
import cats.implicits._
31+
32+
override protected val listMonadToMonadOfList: List[F[RawPart]] => F[List[RawPart]] = _.sequence
33+
2534
override def publisherToBytes(publisher: Publisher[HttpContent], contentLength: Option[Long], maxBytes: Option[Long]): F[Array[Byte]] =
2635
streamCompatible.fromPublisher(publisher, maxBytes).compile.to(Chunk).map(_.toArray[Byte])
2736

28-
def publisherToMultipart(
29-
nettyRequest: StreamedHttpRequest,
30-
serverRequest: ServerRequest,
31-
m: RawBodyType.MultipartBody,
32-
maxBytes: Option[Long]
33-
): F[RawValue[Seq[RawPart]]] = monad.error(new UnsupportedOperationException("Multipart requests are not supported"))
34-
3537
override def writeToFile(serverRequest: ServerRequest, file: TapirFile, maxBytes: Option[Long]): F[Unit] =
3638
(toStream(serverRequest, maxBytes)
3739
.asInstanceOf[streamCompatible.streams.BinaryStream])
@@ -42,5 +44,5 @@ private[cats] class NettyCatsRequestBody[F[_]: Async](
4244
.drain
4345

4446
override def writeBytesToFile(bytes: Array[Byte], file: TapirFile): F[Unit] =
45-
monad.error(new UnsupportedOperationException("Multipart requests are not supported"))
47+
monad.fromTry(for { _ <- Try(JFiles.write(file.toPath, bytes)) } yield ())
4648
}

server/netty-server/cats/src/test/scala/sttp/tapir/server/netty/cats/NettyCatsServerTest.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,15 @@ class NettyCatsServerTest extends TestSuite with EitherValues {
2424

2525
val interpreter = new NettyCatsTestServerInterpreter(eventLoopGroup, dispatcher)
2626
val createServerTest = new DefaultCreateServerTest(backend, interpreter)
27-
val ioSleeper: Sleeper[IO] = new Sleeper[IO] {
28-
override def sleep(duration: FiniteDuration): IO[Unit] = IO.sleep(duration)
29-
}
27+
val ioSleeper: Sleeper[IO] = (duration: FiniteDuration) => IO.sleep(duration)
3028
def drainFs2(stream: Fs2Streams[IO]#BinaryStream): IO[Unit] =
3129
stream.compile.drain.void
3230

3331
val tests = new AllServerTests(
3432
createServerTest,
3533
interpreter,
3634
backend,
37-
multipart = false
35+
partOtherHeaderSupport = false
3836
)
3937
.tests() ++
4038
new ServerStreamingTests(createServerTest).tests(Fs2Streams[IO])(drainFs2) ++

server/netty-server/cats/src/test/scala/sttp/tapir/server/netty/cats/NettyCatsTestServerInterpreter.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ package sttp.tapir.server.netty.cats
22

33
import cats.effect.std.Dispatcher
44
import cats.effect.{IO, Resource}
5-
import io.netty.channel.nio.NioEventLoopGroup
5+
import io.netty.channel.EventLoopGroup
66
import sttp.capabilities.WebSockets
77
import sttp.tapir.server.ServerEndpoint
88
import sttp.tapir.server.netty.{NettyConfig, Route}
99
import sttp.tapir.server.tests.TestServerInterpreter
1010
import sttp.tapir.tests._
1111
import sttp.capabilities.fs2.Fs2Streams
12+
1213
import scala.concurrent.duration.FiniteDuration
1314

14-
class NettyCatsTestServerInterpreter(eventLoopGroup: NioEventLoopGroup, dispatcher: Dispatcher[IO])
15+
class NettyCatsTestServerInterpreter(eventLoopGroup: EventLoopGroup, dispatcher: Dispatcher[IO])
1516
extends TestServerInterpreter[IO, Fs2Streams[IO] with WebSockets, NettyCatsServerOptions[IO], Route[IO]] {
1617
override def route(es: List[ServerEndpoint[Fs2Streams[IO] with WebSockets, IO]], interceptors: Interceptors): Route[IO] = {
1718
val serverOptions: NettyCatsServerOptions[IO] = interceptors(

0 commit comments

Comments
 (0)