http4s-steward[bot] on swagger-ui-4.13.2
Update swagger-ui to 4.13.2 (compare)
FrancescoSerra on scala-collection-compat-2.8.1
FrancescoSerra on main
Update scala-collection-compat … Merge pull request #718 from ht… (compare)
http4s-steward[bot] on swagger-ui-4.11.1
http4s-steward[bot] on munit-cats-effect-3-1.0.7
http4s-steward[bot] on scalafmt-core-3.5.8
http4s-steward[bot] on sbt-scalafmt-2.4.6
http4s-steward[bot] on munit-0.7.29
http4s-steward[bot] on sbt-1.5.8
http4s-steward[bot] on scala-xml-2.1.0
http4s-steward[bot] on scala-library-2.13.8
http4s-steward[bot] on scala-library-2.12.16
http4s-steward[bot] on http4s-blaze-server-0.23.12
Update http4s-blaze-server, ...… (compare)
http4s-steward[bot] on swagger-core-1.6.6
http4s-steward[bot] on sbt-tpolecat-0.1.22
http4s-steward[bot] on circe-core-0.14.2
http4s-steward[bot] on sbt-mima-plugin-1.1.0
Hey everyone. Don't know if this has been asked before: Is it possible to have a rho service, behind TLS and authentication and have generated swagger for it (not behind TLS & auth)
Current attempt giving me :{"schemaValidationMessages":[{"level":"error","message":"Can't read from file /api-docs/swagger.json"}]}
import org.http4s.rho.{RhoRoutes}
import com.me.Dao
import cats.effect.Effect
class AnEndpoint[F[+ _] : Effect](dao: Dao[F]) extends RhoRoutes[F] {
"Create an a" **
POST / “a" ^ EntityDecoder[F, A] |>> { a: A =>
dao.insert(a).flatMap {
case Left(err) => BadRequest(err)
case Right(created) => Created(created)
}
}
}
dao.insert(a) returns an F[Either[Error, A]]Error:(33, 57) diverging implicit expansion for type org.http4s.rho.bits.HListToFunc[F,com.a.A :: shapeless.HNil,com.a.A => F[org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,Nothing,com.a.A,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,com.adikteev.audience.persist.InsertionError,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing]]]
starting with lazy value unrelatedModelToAEncoder in trait ModelSerdes
POST / “a" ^ EntityDecoder[F, A] |>> { a: A =>
List[A]
just fine, because it’s just F[OK[A]]
F[NotInfered]
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)