All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sttp.client3.internal.BodyFromResponseAs.scala Maven / Gradle / Ivy

The newest version!
package sttp.client3.internal

import sttp.client3._
import sttp.client3.ws.{GotAWebSocketException, NotAWebSocketException}
import sttp.model.ResponseMetadata
import sttp.monad.MonadError
import sttp.monad.syntax._

abstract class BodyFromResponseAs[F[_], RegularResponse, WSResponse, Stream](implicit m: MonadError[F]) {
  def apply[T](
      responseAs: ResponseAs[T, _],
      meta: ResponseMetadata,
      response: Either[RegularResponse, WSResponse]
  ): F[T] = doApply(responseAs, meta, response).map(_._1)

  private def doApply[T](
      responseAs: ResponseAs[T, _],
      meta: ResponseMetadata,
      response: Either[RegularResponse, WSResponse]
  ): F[(T, ReplayableBody)] = {

    (responseAs, response) match {
      case (MappedResponseAs(raw, g, _), _) =>
        doApply(raw, meta, response).flatMap { case (result, replayableBody) =>
          m.eval(g(result, meta)).map((_, replayableBody))
        }

      case (rfm: ResponseAsFromMetadata[T, _], _) => doApply(rfm(meta), meta, response)

      case (ResponseAsBoth(l, r), _) =>
        doApply(l, meta, response).flatMap {
          case (leftResult, None) => ((leftResult, None): T, nonReplayableBody).unit
          case (leftResult, Some(rb)) =>
            (response match {
              case Left(rr)  => withReplayableBody(rr, rb).map(Left(_))
              case Right(ws) => Right(ws).unit
            }).flatMap { replayableResponse =>
              doApply(r, meta, replayableResponse).map { case (rightResult, _) =>
                ((leftResult, Some(rightResult)), Some(rb))
              }
            }
        }

      case (IgnoreResponse, Left(regular)) =>
        regularIgnore(regular).map(_ => ((), nonReplayableBody))

      case (ResponseAsByteArray, Left(regular)) =>
        regularAsByteArray(regular).map(b => (b, replayableBody(b)))

      case (ResponseAsStream(_, f), Left(regular)) =>
        regularAsStream(regular).flatMap { case (stream, cancel) =>
          m.suspend(f.asInstanceOf[(Stream, ResponseMetadata) => F[T]](stream, meta))
            .map((_, nonReplayableBody))
            .ensure(cancel())
        }

      case (ResponseAsStreamUnsafe(_), Left(regular)) =>
        regularAsStream(regular).map { case (stream, _) =>
          (stream.asInstanceOf[T], nonReplayableBody)
        }

      case (ResponseAsFile(file), Left(regular)) =>
        regularAsFile(regular, file).map(f => (f, replayableBody(f)))

      case (wsr: WebSocketResponseAs[_, _], Right(ws)) =>
        handleWS(wsr, meta, ws).asInstanceOf[F[T]].map(w => (w, nonReplayableBody))

      case (_: WebSocketResponseAs[_, _], Left(regular)) =>
        val e = new NotAWebSocketException(meta.code)
        cleanupWhenNotAWebSocket(regular, e).flatMap(_ => m.error(e))

      case (_, Right(ws)) =>
        val e = new GotAWebSocketException()
        cleanupWhenGotWebSocket(ws, e).flatMap(_ => m.error(e))
    }
  }

  protected def withReplayableBody(
      response: RegularResponse,
      replayableBody: Either[Array[Byte], SttpFile]
  ): F[RegularResponse]
  protected def regularIgnore(response: RegularResponse): F[Unit]
  protected def regularAsByteArray(response: RegularResponse): F[Array[Byte]]
  protected def regularAsFile(response: RegularResponse, file: SttpFile): F[SttpFile]
  protected def regularAsStream(response: RegularResponse): F[(Stream, () => F[Unit])]
  protected def handleWS[T](responseAs: WebSocketResponseAs[T, _], meta: ResponseMetadata, ws: WSResponse): F[T]
  protected def cleanupWhenNotAWebSocket(response: RegularResponse, e: NotAWebSocketException): F[Unit]
  protected def cleanupWhenGotWebSocket(response: WSResponse, e: GotAWebSocketException): F[Unit]
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy