From f1516a113d4ebff5bbd15a1ada5c6e32e5ad28c0 Mon Sep 17 00:00:00 2001 From: Nabil Abdel-Hafeez <7283535+987Nabil@users.noreply.github.com> Date: Fri, 4 Aug 2023 19:12:29 +0200 Subject: [PATCH] migrate into main --- .../zio/http/codec/internal/BodyCodec.scala | 26 +++++++++---------- .../http/codec/internal/EncoderDecoder.scala | 10 ------- .../scala/zio/http/endpoint/Endpoint.scala | 18 +++++++++---- .../http/endpoint/EndpointMiddleware.scala | 24 ++++++++++++----- .../http/endpoint/EndpointRoundtripSpec.scala | 17 ++++++------ .../http/internal/middlewares/CorsSpec.scala | 2 -- 6 files changed, 52 insertions(+), 45 deletions(-) diff --git a/zio-http/src/main/scala/zio/http/codec/internal/BodyCodec.scala b/zio-http/src/main/scala/zio/http/codec/internal/BodyCodec.scala index 78e850c524..2371023ff2 100644 --- a/zio-http/src/main/scala/zio/http/codec/internal/BodyCodec.scala +++ b/zio-http/src/main/scala/zio/http/codec/internal/BodyCodec.scala @@ -16,15 +16,10 @@ package zio.http.codec.internal -import zio._ -import zio.stacktracer.TracingImplicits.disableAutoTrace - -import zio.stream.ZStream - -import zio.http.{Body, MediaType} import java.nio.charset.Charset import zio._ +import zio.stacktracer.TracingImplicits.disableAutoTrace import zio.stream.{ZPipeline, ZStream} @@ -54,7 +49,7 @@ private[internal] sealed trait BodyCodec[A] { self => /** * Attempts to decode the `A` from a body using the given codec. */ - def decodeFromBody(body: Body, codec: Codec[String, Char, Element]): IO[Throwable, A] + def decodeFromBody(body: Body, codec: Codec[String, Char, Element])(implicit trace: Trace): IO[Throwable, A] /** * Encodes the `A` to a body in the given codec. @@ -64,7 +59,7 @@ private[internal] sealed trait BodyCodec[A] { self => /** * Encodes the `A` to a body in the given codec. */ - def encodeToBody(value: A, codec: Codec[String, Char, Element]): Body + def encodeToBody(value: A, codec: Codec[String, Char, Element])(implicit trace: Trace): Body /** * Erases the type for easier use in the internal implementation. @@ -98,11 +93,12 @@ private[internal] object BodyCodec { def decodeFromBody(body: Body, codec: BinaryCodec[Unit])(implicit trace: Trace): IO[Nothing, Unit] = ZIO.unit - def decodeFromBody(body: Body, codec: Codec[String, Char, Unit]): IO[Nothing, Unit] = ZIO.unit + def decodeFromBody(body: Body, codec: Codec[String, Char, Unit])(implicit trace: Trace): IO[Nothing, Unit] = + ZIO.unit def encodeToBody(value: Unit, codec: BinaryCodec[Unit])(implicit trace: Trace): Body = Body.empty - def encodeToBody(value: Unit, codec: Codec[String, Char, Unit]): Body = Body.empty + def encodeToBody(value: Unit, codec: Codec[String, Char, Unit])(implicit trace: Trace): Body = Body.empty def schema: Schema[Unit] = Schema[Unit] @@ -118,14 +114,14 @@ private[internal] object BodyCodec { else body.asChunk.flatMap(chunk => ZIO.fromEither(codec.decode(chunk))) } - def decodeFromBody(body: Body, codec: Codec[String, Char, A]): IO[Throwable, A] = + def decodeFromBody(body: Body, codec: Codec[String, Char, A])(implicit trace: Trace): IO[Throwable, A] = if (schema == Schema[Unit]) ZIO.unit.asInstanceOf[IO[Throwable, A]] else body.asString.flatMap(chunk => ZIO.fromEither(codec.decode(chunk))) def encodeToBody(value: A, codec: BinaryCodec[A])(implicit trace: Trace): Body = Body.fromChunk(codec.encode(value)) - def encodeToBody(value: A, codec: Codec[String, Char, A]): Body = + def encodeToBody(value: A, codec: Codec[String, Char, A])(implicit trace: Trace): Body = Body.fromString(codec.encode(value)) type Element = A @@ -138,13 +134,15 @@ private[internal] object BodyCodec { ): IO[Throwable, ZStream[Any, Nothing, E]] = ZIO.succeed((body.asStream >>> codec.streamDecoder).orDie) - def decodeFromBody(body: Body, codec: Codec[String, Char, E]): IO[Throwable, ZStream[Any, Nothing, E]] = + def decodeFromBody(body: Body, codec: Codec[String, Char, E])(implicit + trace: Trace, + ): IO[Throwable, ZStream[Any, Nothing, E]] = ZIO.succeed((body.asStream >>> ZPipeline.decodeCharsWith(Charset.defaultCharset()) >>> codec.streamDecoder).orDie) def encodeToBody(value: ZStream[Any, Nothing, E], codec: BinaryCodec[E])(implicit trace: Trace): Body = Body.fromStream(value >>> codec.streamEncoder) - def encodeToBody(value: ZStream[Any, Nothing, E], codec: Codec[String, Char, E]): Body = + def encodeToBody(value: ZStream[Any, Nothing, E], codec: Codec[String, Char, E])(implicit trace: Trace): Body = Body.fromStream(value >>> codec.streamEncoder.map(_.toByte)) type Element = E diff --git a/zio-http/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala b/zio-http/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala index 7658f9a47c..ba01b057d6 100644 --- a/zio-http/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala +++ b/zio-http/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala @@ -17,20 +17,10 @@ package zio.http.codec.internal import zio._ - import zio.stacktracer.TracingImplicits.disableAutoTrace import zio.stream.ZStream -import zio.schema.codec._ -import zio.schema.{Schema, StandardType} - -import zio.stream.ZStream -import zio.stream.ZStream -import zio.schema.Schema -import zio.schema.codec.{BinaryCodec, Codec} - -import zio.stream.ZStream import zio.schema.Schema import zio.schema.codec.{BinaryCodec, Codec} diff --git a/zio-http/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/src/main/scala/zio/http/endpoint/Endpoint.scala index 134fe5c0bb..c8705f441a 100644 --- a/zio-http/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -24,9 +24,10 @@ import zio.stream.ZStream import zio.schema._ -import zio.http.codec._ -import zio.http.endpoint.Endpoint.OutErrors -import zio.http.{Handler, MediaType, Route, RoutePattern, Status} +import zio.http.Header.Accept.MediaTypeWithQFactor +import zio.http._ +import zio.http.codec.{HttpCodec, _} +import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} /** * An [[zio.http.endpoint.Endpoint]] represents an API endpoint for the HTTP @@ -150,9 +151,13 @@ final case class Endpoint[PathInput, Input, Err, Output, Middleware <: EndpointM val handlers = self.alternatives.map { endpoint => Handler.fromFunctionZIO { (request: zio.http.Request) => + val outputMediaTypes = request.headers + .get(Header.Accept) + .map(_.mimeTypes) + .getOrElse(defaultMediaTypes) endpoint.input.decodeRequest(request).orDie.flatMap { value => - original(value).map(endpoint.output.encodeResponse(_)).catchAll { error => - ZIO.succeed(endpoint.error.encodeResponse(error)) + original(value).map(endpoint.output.encodeResponse(_, outputMediaTypes)).catchAll { error => + ZIO.succeed(endpoint.error.encodeResponse(error, outputMediaTypes)) } } } @@ -691,4 +696,7 @@ object Endpoint { self.copy[PathInput, Input, alt.Out, Output, Middleware](error = self.error | codec) } } + + private[endpoint] val defaultMediaTypes = + NonEmptyChunk(MediaTypeWithQFactor(MediaType.application.`json`, Some(1))) } diff --git a/zio-http/src/main/scala/zio/http/endpoint/EndpointMiddleware.scala b/zio-http/src/main/scala/zio/http/endpoint/EndpointMiddleware.scala index e27036ea0b..028e5868ab 100644 --- a/zio-http/src/main/scala/zio/http/endpoint/EndpointMiddleware.scala +++ b/zio-http/src/main/scala/zio/http/endpoint/EndpointMiddleware.scala @@ -16,10 +16,11 @@ package zio.http.endpoint -import zio.ZIO +import zio.{Chunk, ZIO} +import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ -import zio.http.codec._ +import zio.http.codec.{HttpCodec, _} /** * A description of endpoint middleware, in terms of what the middleware @@ -55,14 +56,25 @@ sealed trait EndpointMiddleware { self => ): HandlerAspect[R, S] = HandlerAspect.interceptHandlerStateful( Handler.fromFunctionZIO[Request] { request => + val outputMediaTypes = + request.headers + .get(Header.Accept) + .map(_.mimeTypes.toChunk) + .getOrElse(Chunk(MediaTypeWithQFactor(MediaType.application.`json`, Some(1.0)))) input.decodeRequest(request).orDie.flatMap { in => - incoming(in).catchAll(e => ZIO.fail(error.encodeResponse(e))).map((s: S) => (s, (request, s))) + incoming(in) + .catchAll(e => ZIO.fail(error.encodeResponse(e, outputMediaTypes))) + .map((s: S) => ((s, outputMediaTypes), (request, s))) } }, )( - Handler.fromFunctionZIO[(S, Response)] { case (state, response) => - outgoing(state) - .fold(e => error.encodeResponse(e), (out: Out) => response.patch(output.encodeResponsePatch(out))) + Handler.fromFunctionZIO[((S, Chunk[MediaTypeWithQFactor]), Response)] { + case ((state, outputMediaTypes), response) => + outgoing(state) + .fold( + e => error.encodeResponse(e, outputMediaTypes), + (out: Out) => response.patch(output.encodeResponsePatch(out, outputMediaTypes)), + ) }, ) diff --git a/zio-http/src/test/scala/zio/http/endpoint/EndpointRoundtripSpec.scala b/zio-http/src/test/scala/zio/http/endpoint/EndpointRoundtripSpec.scala index 5f1999bb2d..0278f5d391 100644 --- a/zio-http/src/test/scala/zio/http/endpoint/EndpointRoundtripSpec.scala +++ b/zio-http/src/test/scala/zio/http/endpoint/EndpointRoundtripSpec.scala @@ -144,19 +144,20 @@ object EndpointRoundtripSpec extends ZIOSpecDefault { }, test("simple get with protobuf encoding") { val usersPostAPI = - Endpoint - .get(literal("users") / int("userId") / literal("posts") / int("postId")) + Endpoint(GET / "users" / int("userId") / "posts" / int("postId")) .out[Post] .header(HeaderCodec.accept) val usersPostHandler = - usersPostAPI.implement { case (userId, postId, _) => - ZIO.succeed(Post(postId, "title", "body", userId)) + usersPostAPI.implement { + Handler.fromFunction { case (userId, postId, _) => + Post(postId, "title", "body", userId) + } } testEndpoint( usersPostAPI, - usersPostHandler, + Routes(usersPostHandler), (10, 20, Header.Accept(MediaType.parseCustomMediaType("application/protobuf").get)), Post(20, "title", "body", 10), ) && assertZIO(TestConsole.output)(contains("ContentType: application/protobuf\n")) @@ -196,9 +197,9 @@ object EndpointRoundtripSpec extends ZIOSpecDefault { }, test("throwing error in handler") { val api = Endpoint(POST / string("id") / "xyz" / string("name") / "abc") - .query(query("details")) - .query(query("args").optional) - .query(query("env").optional) + .query(QueryCodec.query("details")) + .query(QueryCodec.query("args").optional) + .query(QueryCodec.query("env").optional) .outError[String](Status.BadRequest) .out[String] ?? Doc.p("doc") diff --git a/zio-http/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala b/zio-http/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala index a199bbb231..2b2701eb6e 100644 --- a/zio-http/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala +++ b/zio-http/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala @@ -23,8 +23,6 @@ import zio.http.Header.AccessControlAllowMethods import zio.http.Middleware.{CorsConfig, cors} import zio.http._ import zio.http.internal.HttpAppTestExtensions -import zio.http.internal.middlewares.CorsSpec.app -import zio.http.internal.middlewares.Cors.CorsConfig object CorsSpec extends ZIOSpecDefault with HttpAppTestExtensions { def extractStatus(response: Response): Status = response.status