*Local
things personally. It's even worse than implicits if you think about it. You should believe that someone somewhere put the required data into the magical box of *Local
before the moment you're going to use it
local
with a new tracing logger.
Hello everyone.
I faced one little problem using finch to build json REST API application.
In my application I have 2 entities User
and Pet
and they both have CRUD like operations.
Both API groups have their own encoders and decoders (I'm using circe).
class UserResources[F[_]](userRepo: UserRepo[F]) extends Endpoint.Module[F] with UserCodecs {
val create: Endpoint[F, User] = post("user" :: jsonBody[User]) { user: User => userRepo.save(user).map(Ok(_)) }
val get: Endpoint[F, User] = get("user" :: path[Long]) { userId: Long => userRepo.findById(userId).map(Ok(_)) }
...
val endpoints = (create :+: get)
}
class PetResources[F[_]](petRepo: PetRepo[F]) extends Endpoint.Module[F] with PetCodecs {
val create: Endpoint[F, User] = post("pet" :: jsonBody[Pet]) { pet: Pet => petRepo.save(user).map(Ok(_)) }
val get: Endpoint[F, User] = get("pet" :: path[Long]) { userId: Long => petRepo.findById(userId).map(Ok(_)) }
...
val endpoints = (create :+: get)
}
Then I use them in this fashion:
val allEndpoints = (new UserResources(repo).endpoints :+: new PetResources(repo2).endpoints)
val api = Bootstrap.serve[Application.Json](allEndpoints).toService
But this call require same instances of Encoder/Decoder
which are defined in *Codecs
trait for .toService
call to materialise the service. I understand why we should have instances in both situations.
Could you please suggest to me how I can better organise code in similar fashion, but without codecs instances duplicate?
Compiler picks it up from there on its own without any imports.
If you need to describe it for types outside of your application (like library types), you might as well to keep it inside a package object (or just an object) and import those implicits from there
Hi @Igosuki I experimented with it once, don't remember the way but it's possible for certain
But to clarify, what do you mean exaclty by upon Finagle server termination
? Because usually you shut it down all together with the whole application, and Http.serve
returns forever-running Twitter Future
libraryDependencies ++= Seq(
"com.github.finagle" %% "finchx-circe" % "0.31.0",
"com.github.finagle" %% "finchx-generic" % "0.31.0"
)
F
and dependency injection in class constructor
IO[Output[Something]]
into a Future
by calling unsafeToFuture
. Since I had already and IO[...], I suppose this call to unsafeToFuture
is not necessary, if not undesirable. val productsQuery: Endpoint[IO, ProductListResponse] =
post( "products" :: jsonBody[SimpleRequest] ) { req: SimpleRequest =>
val origin = "austria"
val products: IO[Output[ProductListResponse]] =
productCache
.flatMap { cache => cache.ref.get(origin) }
.map { r => Ok(ProductListResponse(ctx.api.version, None, r.get.contents)) }
products.unsafeToFuture
} handle {
case e: IllegalArgumentException => handleProductList.BadRequest(e)
case e: Exception => handleProductList.InternalServerError(e)
}
Future
is required.[error] /home/rgomes/workspace/guided-repair-api/service/src/main/scala/Endpoints.scala:143:17: type mismatch;
[error] found : cats.effect.IO[io.finch.Output[api.model.ProductListResponse]]
[error] required: scala.concurrent.Future[?]
[error] products //XXX .unsafeToFuture //FIXME: investigate if there's a way to avoid this call
// These are magic imports which must survive IntelliJ attempts to "help us".
// @formatter: off
import cats.effect._
import cats.implicits._
import cats.syntax.apply._
import io.circe.generic.auto._
import io.finch._
import io.finch.circe._
// @formatter: on
MyApp
object into the Request
when the request is authorized.auth
filter only blocks not authorized requests, but does not pass MyApp
to compiled(req)
.trait Filters extends Whiteboard with StrictLogging {
import io.finch._
import cats.effect.IO
import cats.implicits._
import com.twitter.finagle.http.Status
import com.twitter.finagle.http.Response
def authorized(authorization: Option[String]): IO[MyApp] = tokenValidation.authorized(authorization)
val auth: Endpoint.Compiled[IO] => Endpoint.Compiled[IO] =
compiled => {
Endpoint.Compiled[IO] { req =>
authorized(req.authorization)
.redeemWith(
_ => (Trace.empty -> Right(Response(Status.Unauthorized))).pure[IO],
//FIXME: should pass MyApp object into the request
// See examples 2 and 3 of: https://finagle.github.io/finch/cookbook.html#defining-custom-endpoints
myapp => IO(println(myapp)) *> compiled(req))
}
}
...
}
@frgomes quick and dirty way would be to use Request#ctx
: https://twitter.github.io/finatra/user-guide/http/filters.html#using-c-t-finagle-http-request-ctx or Context
from Finagle that is roughly the same as ThreadLocal (but actually request local)
IMO, proper "functional" way would be to use a different monad instead and have an auth that would have a similar signature:
val auth: Endpoint.Compiled[ReaderT[IO, MyApp, *]] => Endpoint.Compiled[IO]
The idea here is to have underlying endpoints with effect ReaderT[IO, MyApp, *]
that runs inside of Endpoint.Compiled[IO]
where you have an access to request and can build the instance of MyApp
ReaderT
as your effect monad allows you to access environment MyApp
whenever you feel like it