From 0f11e9ab5213a4e764e312fb246dfb7dd35b8267 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Tue, 19 Nov 2024 12:37:24 +1100 Subject: [PATCH 1/3] Optimize some `zio.http.endpoint.Endpoint` code Replace the `NonEmptyChunk.formChunk(...).getOrElse(...)` with `chunk.nonEmptyOrElse(...)(...)` --- .../scala/zio/http/endpoint/Endpoint.scala | 96 +++++++++---------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 7d3f09426..c72963fd1 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -257,34 +257,35 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( def implementHandler[Env](original: Handler[Env, Err, Input, Output])(implicit trace: Trace): Route[Env, Nothing] = { import HttpCodecError.asHttpCodecError - def authCodec(authType: AuthType): HttpCodec[HttpCodecType.RequestType, Unit] = authType match { - case AuthType.None => HttpCodec.empty - case AuthType.Basic => - HeaderCodec.authorization.transformOrFail { - case Header.Authorization.Basic(_, _) => Right(()) - case _ => Left("Basic auth required") - } { case () => - Left("Unsupported") - } - case AuthType.Bearer => - HeaderCodec.authorization.transformOrFail { - case Header.Authorization.Bearer(_) => Right(()) - case _ => Left("Bearer auth required") - } { case () => - Left("Unsupported") - } - case AuthType.Digest => - HeaderCodec.authorization.transformOrFail { - case _: Header.Authorization.Digest => Right(()) - case _ => Left("Digest auth required") - } { case () => - Left("Unsupported") - } - case AuthType.Custom(codec) => - codec.transformOrFailRight[Unit](_ => ())(_ => Left("Unsupported")) - case AuthType.Or(auth1, auth2, _) => - authCodec(auth1).orElseEither(authCodec(auth2))(Alternator.leftRightEqual[Unit]) - } + def authCodec(authType: AuthType): HttpCodec[HttpCodecType.RequestType, Unit] = + authType match { + case AuthType.None => HttpCodec.empty + case AuthType.Basic => + HeaderCodec.authorization.transformOrFail { + case Header.Authorization.Basic(_, _) => Right(()) + case _ => Left("Basic auth required") + } { case () => + Left("Unsupported") + } + case AuthType.Bearer => + HeaderCodec.authorization.transformOrFail { + case Header.Authorization.Bearer(_) => Right(()) + case _ => Left("Bearer auth required") + } { case () => + Left("Unsupported") + } + case AuthType.Digest => + HeaderCodec.authorization.transformOrFail { + case _: Header.Authorization.Digest => Right(()) + case _ => Left("Digest auth required") + } { case () => + Left("Unsupported") + } + case AuthType.Custom(codec) => + codec.transformOrFailRight[Unit](_ => ())(_ => Left("Unsupported")) + case AuthType.Or(auth1, auth2, _) => + authCodec(auth1).orElseEither(authCodec(auth2))(Alternator.leftRightEqual[Unit]) + } val maybeUnauthedResponse = authType.asInstanceOf[AuthType] match { case AuthType.None => None @@ -295,13 +296,11 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( self.alternatives.map { case (endpoint, condition) => Handler.fromFunctionZIO { (request: zio.http.Request) => val outputMediaTypes = - NonEmptyChunk - .fromChunk( - request.headers - .getAll(Header.Accept) - .flatMap(_.mimeTypes), - ) - .getOrElse(defaultMediaTypes) + request.headers + .getAll(Header.Accept) + .flatMap(_.mimeTypes) + .nonEmptyOrElse(defaultMediaTypes)(identity) + (endpoint.input ++ authCodec(endpoint.authType)).decodeRequest(request, config).orDie.flatMap { value => original(value).map(endpoint.output.encodeResponse(_, outputMediaTypes, config)).catchAll { error => ZIO.succeed(endpoint.error.encodeResponse(error, outputMediaTypes, config)) @@ -311,15 +310,15 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( } // TODO: What to do if there are no endpoints?? - def handlers2(handlers: Chunk[(Handler[Env, Nothing, Request, Response], HttpCodec.Fallback.Condition)]) = - NonEmptyChunk - .fromChunk(handlers) - .getOrElse( - NonEmptyChunk( - Handler.fail(zio.http.Response(status = Status.NotFound)) -> HttpCodec.Fallback.Condition.IsHttpCodecError, - ), + def handlers2(handlers: Chunk[(Handler[Env, Nothing, Request, Response], HttpCodec.Fallback.Condition)]) = { + def noFound = + NonEmptyChunk( + Handler.fail(zio.http.Response(status = Status.NotFound)) -> HttpCodec.Fallback.Condition.IsHttpCodecError, ) + handlers.nonEmptyOrElse(ifEmpty = noFound)(identity) + } + val handler = Handler.fromZIO(CodecConfig.codecRef.get).flatMap { config => val hdlrs = handlers(config) @@ -343,13 +342,12 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( val error = cause.defects.head.asInstanceOf[HttpCodecError] val response = { val outputMediaTypes = - NonEmptyChunk - .fromChunk( - request.headers - .getAll(Header.Accept) - .flatMap(_.mimeTypes) :+ MediaTypeWithQFactor(MediaType.application.`json`, Some(0.0)), - ) - .getOrElse(defaultMediaTypes) + ( + request.headers + .getAll(Header.Accept) + .flatMap(_.mimeTypes) :+ MediaTypeWithQFactor(MediaType.application.`json`, Some(0.0)) + ).nonEmptyOrElse(defaultMediaTypes)(identity) + codecError.encodeResponse(error, outputMediaTypes, config) } ZIO.succeed(response) From ab6e100f9c73ea0a767ca6155a3c1b4caa86c275 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Tue, 26 Nov 2024 18:30:14 +1100 Subject: [PATCH 2/3] fix compilation && fmt --- .../main/scala/zio/http/endpoint/Endpoint.scala | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index c72963fd1..951e4b531 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -55,7 +55,8 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( codecError: HttpCodec[HttpCodecType.ResponseType, HttpCodecError], documentation: Doc, authType: Auth, -) { self => +) { + self => val authCombiner: Combiner[Input, authType.ClientRequirement] = implicitly[Combiner[Input, authType.ClientRequirement]] @@ -67,6 +68,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( ): HttpCodec[HttpCodecType.RequestType, AuthedInput] = { input ++ authCodec }.asInstanceOf[HttpCodec[HttpCodecType.RequestType, AuthedInput]] + type AuthedInput = authCombiner.Out /** @@ -299,7 +301,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( request.headers .getAll(Header.Accept) .flatMap(_.mimeTypes) - .nonEmptyOrElse(defaultMediaTypes)(identity) + .nonEmptyOrElse(defaultMediaTypes)(ZIO.identityFn) (endpoint.input ++ authCodec(endpoint.authType)).decodeRequest(request, config).orDie.flatMap { value => original(value).map(endpoint.output.encodeResponse(_, outputMediaTypes, config)).catchAll { error => @@ -310,13 +312,15 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( } // TODO: What to do if there are no endpoints?? - def handlers2(handlers: Chunk[(Handler[Env, Nothing, Request, Response], HttpCodec.Fallback.Condition)]) = { - def noFound = + def handlers2( + handlers: Chunk[(Handler[Env, Nothing, Request, Response], HttpCodec.Fallback.Condition)], + ): NonEmptyChunk[(Handler[Env, Response, Request, Response], HttpCodec.Fallback.Condition)] = { + def noFound: NonEmptyChunk[(Handler[Env, Response, Request, Response], HttpCodec.Fallback.Condition)] = NonEmptyChunk( Handler.fail(zio.http.Response(status = Status.NotFound)) -> HttpCodec.Fallback.Condition.IsHttpCodecError, ) - handlers.nonEmptyOrElse(ifEmpty = noFound)(identity) + handlers.nonEmptyOrElse(ifEmpty = noFound)(ZIO.identityFn) } val handler = @@ -346,7 +350,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( request.headers .getAll(Header.Accept) .flatMap(_.mimeTypes) :+ MediaTypeWithQFactor(MediaType.application.`json`, Some(0.0)) - ).nonEmptyOrElse(defaultMediaTypes)(identity) + ).nonEmptyOrElse(defaultMediaTypes)(ZIO.identityFn) codecError.encodeResponse(error, outputMediaTypes, config) } From e7b1afbccca1a7e507155c0e3f0d0d687ce9186f Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Fri, 6 Dec 2024 04:01:12 +1100 Subject: [PATCH 3/3] Review: fix formatting --- .../scala/zio/http/endpoint/Endpoint.scala | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 951e4b531..4d813512b 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -55,8 +55,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( codecError: HttpCodec[HttpCodecType.ResponseType, HttpCodecError], documentation: Doc, authType: Auth, -) { - self => +) { self => val authCombiner: Combiner[Input, authType.ClientRequirement] = implicitly[Combiner[Input, authType.ClientRequirement]] @@ -259,35 +258,34 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( def implementHandler[Env](original: Handler[Env, Err, Input, Output])(implicit trace: Trace): Route[Env, Nothing] = { import HttpCodecError.asHttpCodecError - def authCodec(authType: AuthType): HttpCodec[HttpCodecType.RequestType, Unit] = - authType match { - case AuthType.None => HttpCodec.empty - case AuthType.Basic => - HeaderCodec.authorization.transformOrFail { - case Header.Authorization.Basic(_, _) => Right(()) - case _ => Left("Basic auth required") - } { case () => - Left("Unsupported") - } - case AuthType.Bearer => - HeaderCodec.authorization.transformOrFail { - case Header.Authorization.Bearer(_) => Right(()) - case _ => Left("Bearer auth required") - } { case () => - Left("Unsupported") - } - case AuthType.Digest => - HeaderCodec.authorization.transformOrFail { - case _: Header.Authorization.Digest => Right(()) - case _ => Left("Digest auth required") - } { case () => - Left("Unsupported") - } - case AuthType.Custom(codec) => - codec.transformOrFailRight[Unit](_ => ())(_ => Left("Unsupported")) - case AuthType.Or(auth1, auth2, _) => - authCodec(auth1).orElseEither(authCodec(auth2))(Alternator.leftRightEqual[Unit]) - } + def authCodec(authType: AuthType): HttpCodec[HttpCodecType.RequestType, Unit] = authType match { + case AuthType.None => HttpCodec.empty + case AuthType.Basic => + HeaderCodec.authorization.transformOrFail { + case Header.Authorization.Basic(_, _) => Right(()) + case _ => Left("Basic auth required") + } { case () => + Left("Unsupported") + } + case AuthType.Bearer => + HeaderCodec.authorization.transformOrFail { + case Header.Authorization.Bearer(_) => Right(()) + case _ => Left("Bearer auth required") + } { case () => + Left("Unsupported") + } + case AuthType.Digest => + HeaderCodec.authorization.transformOrFail { + case _: Header.Authorization.Digest => Right(()) + case _ => Left("Digest auth required") + } { case () => + Left("Unsupported") + } + case AuthType.Custom(codec) => + codec.transformOrFailRight[Unit](_ => ())(_ => Left("Unsupported")) + case AuthType.Or(auth1, auth2, _) => + authCodec(auth1).orElseEither(authCodec(auth2))(Alternator.leftRightEqual[Unit]) + } val maybeUnauthedResponse = authType.asInstanceOf[AuthType] match { case AuthType.None => None