http4s-steward[bot] on http4s-scala-xml-0.23.11
http4s-steward[bot] on http4s-scala-xml-0.23.12
Update http4s-scala-xml to 0.23… (compare)
http4s-steward[bot] on http4s-server-0.23.13
Update http4s-circe, http4s-dsl… (compare)
http4s-steward[bot] on scala-library-2.12.15
http4s-steward[bot] on scala-library-2.12.16
Update scala-library, scala-ref… (compare)
http4s-steward[bot] on silencer-lib-1.7.8
http4s-steward[bot] on silencer-lib-1.7.9
Update silencer-lib, silencer-p… (compare)
typelevel-steward[bot] on swagger-ui-4.11.1
Update swagger-ui to 4.11.1 (compare)
Hello there.
I've been playing around with Rho lately and have run into a couple of issues:
My responses are an ADT that have encoders, decoders and entityencoders defined:
@deriving(Encoder, Decoder, Eq)
sealed trait ValidationServiceResult
object ValidationServiceResult {
implicit def validationServiceResultEntityEncoder[
F[_]: Applicative
]: EntityEncoder[F, ValidationServiceResult] = jsonEncoderOf
implicit def listValidationServiceResultEntityEncoder[
F[_]: Applicative
]: EntityEncoder[F, List[ValidationServiceResult]] = jsonEncoderOf
}
@deriving(Encoder, Decoder, Eq)
case class UnknownTypeError(tpe: String Refined NonEmpty)
extends ValidationServiceResult
object UnknownTypeError {
implicit def unknownTypeErrorEntityEncoder[
F[_]: Applicative
]: EntityEncoder[F, UnknownTypeError] = jsonEncoderOf
}
@deriving(Encoder, Decoder, Eq)
case class JsonDecodeError(errors: NonEmptyList[DecodingFailure])
extends ValidationServiceResult
object JsonDecodeError {
implicit def jsonDecodeErrorEntityEncoder[
F[_]: Applicative
]: EntityEncoder[F, JsonDecodeError] = jsonEncoderOf
}
@deriving(Encoder, Decoder, Eq)
case object Success extends ValidationServiceResult {
implicit def successEntityEncoder[
F[_]: Applicative
]: EntityEncoder[F, Success.type] = jsonEncoderOf
}
And I return responses like so:
def response(
res: ValidationServiceResult
): F[Result.BaseResult[F]] = res match {
case x: Success.type => Ok(res)
case x: UnknownTypeError => BadRequest(res)
case x: JsonDecodeError => BadRequest(res)
I'm not quite sure what I'm doing wrong, and if all / any of these issues are expected.
This isn't in 0.19.0 :(
class SwaggerSpecEndpoint[F[+ _] : Effect](swaggerSpec: Swagger)(implicit F: Monad[F]) extends Http4sDsl[F] {
lazy val response: F[Response[F]] = {
val fOk = Ok.apply(
Json
.mapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(swaggerSpec.toJModel)
)
F.map(fOk) { ok =>
ok.copy(headers = ok.headers.put(`Content-Type`(MediaType.application.json)))
}
}
val service: HttpRoutes[F] = HttpRoutes.of[F] {
case _ @GET -> Root / "swagger.json" => response
}
}
object SwaggerSpecEndpoint {
def endpoint[F[+ _] : Effect](swagger: SwaggerSupport[F], swaggerFormats: SwaggerFormats)(
routes: Seq[RhoRoute[F, _]]
): SwaggerSpecEndpoint[F] = {
import swagger._
val swaggerSpec: Swagger = createSwagger(
swaggerFormats,
security = List(SecurityRequirement("JWT", Nil)),
securityDefinitions = Map("JWT" -> ApiKeyAuthDefinition(auth.JWT_AUTH_HEADER, In.HEADER, "JWT".some))
)(routes)
new SwaggerSpecEndpoint[F](swaggerSpec)
}
}
rootServices = Metrics[F](metricsOps)(
GZip(
CORS(authEndpoint.service, corsConfig) <+> snsService.routes <+>
authService.authenticated(authContext.toService(CORS(coreApi, corsConfig))) <+>
swaggerSpecEndpoint.service <+> CORS(home.service, corsConfig) <+> home.noop
)
)
val authenticated: AuthMiddleware[F, User] =
AuthMiddleware.withFallThrough(authUser)
val authUser: Kleisli[OptionT[F, ?], Request[F], User]
class Routes[F[+_]: Sync](service: FooService[F]) extends RhoRoutes[F] {
private def handleError(t: Throwable): F[BaseResult[F]] = t match {
case e: CustomError =>
BadGateway(e)
case _ =>
InternalServerError(UnexpectedError)
}
GET / "api" +? param[Int]("id") |>> { id: Int =>
service
.getFoo(id)
.flatMap { foo =>
Ok(foo).widen[BaseResult[F]]
}
.handleErrorWith(handleError)
}
}
widen
InternalServerError(someEmptyFooInstance)
but it wouldn't compile either
val authUser: Kleisli[IO, Request[IO], Either[String, User]] = Kleisli { req =>
IO(Right(User("Bob", UUID.randomUUID())))
}
val onFailure: AuthedRoutes[String, IO] = Kleisli(req => OptionT.liftF(Forbidden(req.authInfo)))
val middleware = AuthMiddleware(authUser, onFailure)
object Auth extends AuthedContext[IO, User]
object BobRoutes extends RhoRoutes[IO] {
GET +? param("foo", "bar") >>> Auth.auth |>> { (foo: String, user: User) =>
Ok(s"Bob with id ${user.id}, foo $foo")
}
}
val service = middleware.apply(Auth.toService(BobRoutes.toRoutes()))
This code is copied from AuthedContext.scala
. But the Forbidden
part is still "not found" after import bulk of things to make other part of code works... By the way, how to make service
available for BlazeServerBuilder
, or convert to HttpApp
...?
Many thx!
val jwtStatelessAuthenticator =
JWTAuthenticator.pstateless.inHeader[F, User, HMACSHA256](
signingKey = signingKey,
settings = TSecJWTSettings(JWT_AUTH_HEADER, FiniteDuration(24L * 3600L, TimeUnit.SECONDS), None)
)
val authUser: Kleisli[OptionT[F, ?], Request[F], User] = Kleisli({ req =>
if (userAuthentication) {
jwtStatelessAuthenticator
.extractAndValidate(req)
.map(_.identity)
} else {
OptionT.pure[F](User.dummy)
}
})
private[auth] val defaultNotAuthenticated: AuthedRequest[F, String] => OptionT[F, Response[F]] =
req => OptionT.liftF(Forbidden(req.authInfo))
val onAuthFailure: AuthedRoutes[String, F] = Kleisli(defaultNotAuthenticated)
val authenticated: AuthMiddleware[F, User] =
AuthMiddleware.withFallThrough(authUser)
F[_]
instead of IO
. Is RhoRoutes suitable for that? I guess it is, although all the example code / documentation uses IO
. The reason I'm asking for is I have a route that returns different status codes based on some intermediate results and it works fine with IO
but I can't get it to be of the required F[Result[...]]
type with F[_]:ConcurrentEffect
, instead it just defaults to Any
As far as Intellij's shw implicits feature goes I can't see any difference. My ConcurrentEffect
is being picked up instead of ioEffect
which seems to be fine.