Skip to content

Commit

Permalink
Merge pull request #877 from disneystreaming/middleware-error-handling
Browse files Browse the repository at this point in the history
[Http4s:middleware] pre and post error handling
  • Loading branch information
Baccata authored May 30, 2023
2 parents 3d8e5e0 + de736e1 commit 3816064
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ private[http4s] class SmithyHttp4sServerEndpointImpl[F[_], Op[_, _, _, _, _], I,
httpEndpoint.matches(path)
}

private val applyMiddleware = middleware(endpoint)
private val applyMiddleware: HttpApp[F] => HttpApp[F] = middleware(endpoint)

override val httpApp: HttpApp[F] =
applyMiddleware(HttpApp[F] { req =>
httpAppErrorHandle(applyMiddleware(HttpApp[F] { req =>
val pathParams = req.attributes.lookup(pathParamsKey).getOrElse(Map.empty)

val run: F[O] = for {
Expand All @@ -122,7 +122,17 @@ private[http4s] class SmithyHttp4sServerEndpointImpl[F[_], Op[_, _, _, _, _], I,
run
.recoverWith(transformError)
.flatMap(successResponse)
}).handleErrorWith(error => Kleisli.liftF(errorResponse(error)))
.handleErrorWith(errorResponse)
}))

private def httpAppErrorHandle(app: HttpApp[F]): HttpApp[F] = {
app
.recoverWith {
case error if errorTransformation.isDefinedAt(error) =>
Kleisli.liftF(errorTransformation.apply(error).flatMap(errorResponse))
}
.handleErrorWith { error => Kleisli.liftF(errorResponse(error)) }
}

private val inputSchema: Schema[I] = endpoint.input
private val outputSchema: Schema[O] = endpoint.output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,69 @@
package smithy4s
package http4s

import weaver._
import smithy4s.hello._
import org.http4s.HttpApp
import cats.effect.IO
import cats.data.OptionT
import org.http4s.Uri
import org.http4s._
import cats.effect.IO
import cats.effect.Resource
import cats.Eq
import cats.implicits._
import fs2.Collector
import org.http4s._
import org.http4s.client.Client
import cats.Eq
import cats.effect.Resource
import org.http4s.HttpApp
import org.http4s.Uri
import smithy4s.hello._
import weaver._

object ServerEndpointMiddlewareSpec extends SimpleIOSuite {

private implicit val greetingEq: Eq[Greeting] = Eq.fromUniversalEquals
private implicit val throwableEq: Eq[Throwable] = Eq.fromUniversalEquals

final class MiddlewareException
extends RuntimeException(
"Expected to recover via flatmapError or mapError"
)
test("server - middleware can throw and mapped / flatmapped") {
val middleware = new ServerEndpointMiddleware.Simple[IO]() {
def prepareWithHints(
serviceHints: Hints,
endpointHints: Hints
): HttpApp[IO] => HttpApp[IO] = { inputApp =>
HttpApp[IO] { _ => IO.raiseError(new MiddlewareException) }
}
}
def runOnService(service: HttpRoutes[IO]): IO[Expectations] =
service(Request[IO](Method.POST, Uri.unsafeFromString("/bob")))
.flatMap(res => OptionT.pure(expect.eql(res.status.code, 599)))
.getOrElse(
failure("unable to run request")
)

val pureCheck = runOnService(
SimpleRestJsonBuilder
.routes(HelloImpl)
.middleware(middleware)
.flatMapErrors { case _: MiddlewareException =>
IO.pure(SpecificServerError())
}
.make
.toOption
.get
)
val throwCheck = runOnService(
SimpleRestJsonBuilder
.routes(HelloImpl)
.middleware(middleware)
.mapErrors { case _: MiddlewareException =>
throw SpecificServerError()
}
.make
.toOption
.get
)
List(throwCheck, pureCheck).combineAll
}

test("server - middleware is applied") {
serverMiddlewareTest(
shouldFailInMiddleware = true,
Expand Down
8 changes: 7 additions & 1 deletion sampleSpecs/hello.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ service HelloWorldService {
version: "1.0.0",
// Indicates that all operations in `HelloWorldService`,
// here limited to Hello, can return `GenericServerError`.
errors: [GenericServerError],
errors: [GenericServerError, SpecificServerError],
operations: [Hello]
}

Expand All @@ -18,6 +18,12 @@ structure GenericServerError {
message: String
}

@error("server")
@httpError(599)
structure SpecificServerError {
message: String
}

@http(method: "POST", uri: "/{name}", code: 200)
@tags(["testOperationTag"])
operation Hello {
Expand Down

0 comments on commit 3816064

Please sign in to comment.