Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: ResponseAs instances with error rethrown into F[_] #451

Closed
xicmiah opened this issue Mar 2, 2020 · 5 comments
Closed
Labels

Comments

@xicmiah
Copy link

xicmiah commented Mar 2, 2020

My usecase:

  • I don't have different handling for response errors, be it connection error, non-2xx response, or unparsable body.
  • End result just needs to have a good error message, it's going to be lifted into F[_] anyway.
  • In case of non-2xx, having both response code and error body is really helpful. Calling .map(_.unsafeBody) in sttp1 worked for that purpose (impure, but works for Id, Future, or monix Task).

I can suggest having a group of ResponseAs[F[T], _] instances, for example

def asStringF[F[_]](implicit backend: SttpBackend[F, Nothing, NothingT]): ResponseAs[F[String], Nothing] = ???

Usage becomes

request
  .response(asStringF)
  .send() // F[Response[F[String]]]
  .flatMap(_.body) // F[String]
  .flatMap(parseBody) // F[Domain]; for example, io.circe.parser.decode[T] with some lifting

Sample implementation for asStringF: https://gist.github.com/xicmiah/0170570ba85c6d8d9cabec732c3cb80a
Notes:

  • Implicit backend should probably be implicit MonadError instead, the latter isn't in implicit scope yet.
  • FullHttpError is only used for response body, it can be replaced with existing HttpError
@adamw
Copy link
Member

adamw commented Mar 10, 2020

Makes sense. There are two options with the backend:

  • accept an implicit backend
  • add an implicit on MonadError which returns an implicit monad given an implicit backend.

The former is more straightforward, but the latter seems closer to the true needs of the asXxxF description.

Could you maybe prepare a PR?

@xicmiah
Copy link
Author

xicmiah commented Mar 10, 2020

Sure, I'll try to make a PR this week.

@frekw
Copy link

frekw commented Apr 1, 2020

For ZIO, something like this works fine:

import sttp.client._
import sttp.client.circe.
import io.circe._
import zio._

def asJsonF[T: Decoder: IsOption]: ResponseAs[Task[T], Nothing] = asJson[T].map {
  case Left(error)  => Task.fail(error)
  case Right(value) => Task.succeed(value)
}

sttpClient.send(req.response(asJsonF[A]).flatMap(_.body) // => ZIO[Any, Throwable, A]

@ghostbuster91
Copy link
Contributor

ghostbuster91 commented Jul 20, 2020

As we are extending response handling possibilities in #620 I'm not sure if it is a good idea to duplicate every possible responseAs handling with it's F equivalent. Maybe we could just add a liftF method to the ResponseAs class?

Since all responses can be read in a safe way into Either[ResponseErrorTyped[E1,E2]], T] we can convert errors into F by transalting from MonadError for Either into MonadError for F

When it comes to zio if think that it will make sense to enable converting it into IO from which erros can be elimiated by using absorb method.

@adamw
Copy link
Member

adamw commented Aug 18, 2020

In v3, this is now implemented using failLeft, which for response specifications of the shape Either[A, B] throws the A exception (or wraps it with HttpError, if it's not an exception), or return B.

So the original use-case would become: asString.failLeft: ResponseAs[String, Any]. The resulting exception will contain the original request (due to wrapping with SttpClientException), and the status code in HttpError.

Similarly, asJson.failLeft implements the json case.

@adamw adamw closed this as completed Aug 18, 2020
@adamw adamw added the v3 label Aug 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants