Skip to content

Instantly share code, notes, and snippets.

@afsalthaj
Created January 4, 2019 02:31
Show Gist options
  • Save afsalthaj/8a7a256884e1f269091e06fbc1dce44f to your computer and use it in GitHub Desktop.
Save afsalthaj/8a7a256884e1f269091e06fbc1dce44f to your computer and use it in GitHub Desktop.
import cats.effect.{ Effect, Sync }
import org.http4s.MediaType.`application/json`
import org.http4s.{ EntityDecoder, EntityEncoder, Headers, Method, Request, Status, Uri }
import org.http4s.client.Client
import org.http4s.headers.{ Accept, `Content-Type` }
import scalaz.{ EitherT, \/ }
import cats.syntax.applicative._
import cats.syntax.functor._
import org.http4s.client.blaze.Http1Client
import scalaz.syntax.either._
final case class Http4sClient[F[_]: Sync] private (client: Client[F])(implicit M: cats.Monad[F]) {
implicit def scalazMonad: scalaz.Monad[F] = new scalaz.Monad[F] {
override def bind[A, B](fa: F[A])(f: A => F[B]): F[B] = M.flatMap(fa)(f)
override def point[A](a: => A): F[A] = M.pure(a)
}
def put[A, B](uri: String, body: A)(implicit E: EntityEncoder[F, A], D: EntityDecoder[F, B]): EitherT[F, String, B] =
for {
uri <- EitherT { parseUri(uri) }
req <- EitherT {
Request[F](
method = Method.PUT,
uri = uri,
headers = Headers(Accept(`application/json`), `Content-Type`(`application/json`))
).withBody[A](body).map(_.right[String])
}
r <- EitherT {
fetch[B](req)
}
} yield r
def delete(uri: String): EitherT[F, String, Unit] =
for {
uri <- EitherT { parseUri(uri) }
req = Request[F](method = Method.DELETE, uri = uri)
r <- EitherT { fetch[Unit](req) }
} yield r
def get[B](uri: String)(implicit D: EntityDecoder[F, B]): EitherT[F, String, B] =
for {
uri <- EitherT { parseUri(uri) }
req = Request[F](method = Method.GET, uri = uri)
r <- EitherT { fetch[B](req) }
} yield r
private def parseUri(uri: String): F[String \/ Uri] =
\/.fromEither(
Uri.fromString(uri)
).leftMap(c => s"parse failure of uri. $c").pure[F]
private def fetch[B](req: Request[F])(implicit D: EntityDecoder[F, B]): F[String \/ B] =
client.fetch[Either[String, B]](req) {
case Status.Successful(r) => r.attemptAs[B].leftMap(_.message).value
case r => r.attemptAs[String].fold(t => Left(s"Failed to find the reason for failure in decoding. ${t.message}"),
t => Left(s"Request $req failed with status ${r.status.code} and body $t")
)
}.map(\/.fromEither)
}
object Http4sClient {
def mk[F[_]: Effect]: F[Http4sClient[F]] =
Http1Client[F]().map(Http4sClient(_))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment