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

muffin.interop.http.sttp.SttpClient.scala Maven / Gradle / Ivy

The newest version!
package muffin.interop.http.sttp

import cats.MonadThrow
import cats.effect.Sync
import cats.syntax.all.given

import sttp.client3.*
import sttp.model.{Method as SMethod, Uri}

import muffin.codec.*
import muffin.error.MuffinError
import muffin.http.*
import muffin.internal.syntax.*

class SttpClient[F[_]: MonadThrow, To[_], From[_]](backend: SttpBackend[F, Any], codec: CodecSupport[To, From])
  extends HttpClient[F, To, From] {

  import codec.given

  def request[In: To, Out: From](
      url: String,
      method: Method,
      body: Body[In],
      headers: Map[String, String],
      params: Params => Params
  ): F[Out] = {
    val req = basicRequest
      .method(
        method match {
          case Method.Get    => SMethod.GET
          case Method.Post   => SMethod.POST
          case Method.Put    => SMethod.PUT
          case Method.Delete => SMethod.DELETE
          case Method.Patch  => SMethod.PATCH
        },
        Uri.unsafeParse(url + params(Params.Empty).mkString)
      )
      .headers(headers)
      .tap {
        req =>
          body match {
            case Body.Empty            => req
            case Body.Json(value)      =>
              req
                .body(Encode[In].apply(value), "UTF-8")
                .header("Content-Type", "application/json")
            case Body.RawJson(value)   =>
              req
                .body(value, "UTF-8")
                .header("Content-Type", "application/json")
            case Body.Multipart(parts) =>
              req
                .multipartBody(
                  parts.map {
                    case MultipartElement.StringElement(name, value) => multipart(name, value)
                    case MultipartElement.FileElement(name, value)   => multipart(name, value)
                  }
                )
                .header("Content-Type", "multipart/form-data")
          }
      }
      .response(asString.mapLeft(MuffinError.Http.apply))
      .mapResponse(_.flatMap(Decode[Out].apply))

    backend.send(req)
      .map(_.body)
      .flatMap {
        case Left(error)  => MonadThrow[F].raiseError(error)
        case Right(value) => value.pure[F]
      }
  }

}

object SttpClient {

  def apply[I[_]: Sync, F[_]: MonadThrow, To[_], From[_]](
      backend: SttpBackend[F, Any],
      codec: CodecSupport[To, From]
  ): I[SttpClient[F, To, From]] = Sync[I].delay(new SttpClient[F, To, From](backend, codec))

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy