aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md11
-rw-r--r--async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/CatsAsyncHttpClientHandler.scala64
-rw-r--r--build.sbt13
-rw-r--r--tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala4
-rw-r--r--tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala4
5 files changed, 94 insertions, 2 deletions
diff --git a/README.md b/README.md
index bf4d4d2..358c6ea 100644
--- a/README.md
+++ b/README.md
@@ -188,6 +188,7 @@ uri"$scheme://$subdomains.example.com?x=$vx&$params#$jumpTo"
| `FutureAsyncHttpClientHandler` | `scala.concurrent.Future` | - |
| `ScalazAsyncHttpClientHandler` | `scalaz.concurrent.Task` | - |
| `MonixAsyncHttpClientHandler` | `monix.eval.Task` | `monix.reactive.Observable[ByteBuffer]` |
+| `CatsAsyncHttpClientHandler` | `F[_]: cats.effect.Async` | - |
| `OkHttpSyncClientHandler` | None (`Id`) | - |
| `OkHttpFutureClientHandler` | `scala.concurrent.Future` | - |
@@ -271,6 +272,8 @@ To use, add the following dependency to your project:
"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.4"
// or
"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.4"
+// or
+"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.4"
```
This handler depends on [async-http-client](https://github.com/AsyncHttpClient/async-http-client).
@@ -284,6 +287,8 @@ The responses are wrapped depending on the dependency chosen in either a:
dependency on `scalaz-concurrent`.
* [Monix](https://monix.io) `Task`. There's a transitive dependency on
`monix-eval`.
+* Any type implementing the [Cats Effect](https://github.com/typelevel/cats-effect) `Async`
+typeclass, such as `cats.effect.IO`. There's a transitive dependency on `cats-effect`.
Next you'll need to add an implicit value:
@@ -296,6 +301,9 @@ implicit val sttpHandler = ScalazAsyncHttpClientHandler()
// or, if you're using the monix version:
implicit val sttpHandler = MonixAsyncHttpClientHandler()
+// or, if you're using the cats effect version:
+implicit val sttpHandler = CatsAsyncHttpClientHandler[cats.effect.IO]()
+
// or, if you'd like to use custom configuration:
implicit val sttpHandler = FutureAsyncHttpClientHandler.usingConfig(asyncHttpClientConfig)
@@ -433,4 +441,5 @@ and pick a task you'd like to work on!
* [Tomasz Szymański](https://github.com/szimano)
* [Adam Warski](https://github.com/adamw)
-* [Omar Alejandro Mainegra Sarduy](https://github.com/omainegra) \ No newline at end of file
+* [Omar Alejandro Mainegra Sarduy](https://github.com/omainegra)
+* [Bjørn Madsen](https://github.com/aeons)
diff --git a/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/CatsAsyncHttpClientHandler.scala b/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/CatsAsyncHttpClientHandler.scala
new file mode 100644
index 0000000..fab3b8b
--- /dev/null
+++ b/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/CatsAsyncHttpClientHandler.scala
@@ -0,0 +1,64 @@
+package com.softwaremill.sttp.asynchttpclient.cats
+
+import java.nio.ByteBuffer
+
+import cats.effect._
+import com.softwaremill.sttp.asynchttpclient.AsyncHttpClientHandler
+import com.softwaremill.sttp.{MonadAsyncError, SttpHandler}
+import org.asynchttpclient.{
+ AsyncHttpClient,
+ AsyncHttpClientConfig,
+ DefaultAsyncHttpClient
+}
+import org.reactivestreams.Publisher
+
+import scala.language.higherKinds
+
+class CatsAsyncHttpClientHandler[F[_]: Async] private (
+ asyncHttpClient: AsyncHttpClient,
+ closeClient: Boolean
+) extends AsyncHttpClientHandler[F, Nothing](
+ asyncHttpClient,
+ new AsyncMonad,
+ closeClient
+ ) {
+ override protected def streamBodyToPublisher(
+ s: Nothing): Publisher[ByteBuffer] = s // nothing is everything
+
+ override protected def publisherToStreamBody(
+ p: Publisher[ByteBuffer]): Nothing =
+ throw new IllegalStateException("This handler does not support streaming")
+}
+
+object CatsAsyncHttpClientHandler {
+
+ def apply[F[_]: Async](): SttpHandler[F, Nothing] =
+ new CatsAsyncHttpClientHandler(new DefaultAsyncHttpClient(),
+ closeClient = true)
+
+ def usingConfig[F[_]: Async](
+ cfg: AsyncHttpClientConfig): SttpHandler[F, Nothing] =
+ new CatsAsyncHttpClientHandler(new DefaultAsyncHttpClient(cfg),
+ closeClient = true)
+
+ def usingClient[F[_]: Async](
+ client: AsyncHttpClient): SttpHandler[F, Nothing] =
+ new CatsAsyncHttpClientHandler(client, closeClient = false)
+}
+
+private[cats] class AsyncMonad[F[_]](implicit F: Async[F])
+ extends MonadAsyncError[F] {
+
+ override def async[T](
+ register: ((Either[Throwable, T]) => Unit) => Unit): F[T] =
+ F.async(register)
+
+ override def unit[T](t: T): F[T] = F.pure(t)
+
+ override def map[T, T2](fa: F[T], f: (T) => T2): F[T2] = F.map(fa)(f)
+
+ override def flatMap[T, T2](fa: F[T], f: (T) => F[T2]): F[T2] =
+ F.flatMap(fa)(f)
+
+ override def error[T](t: Throwable): F[T] = F.raiseError(t)
+}
diff --git a/build.sbt b/build.sbt
index 10e1dd5..332e80b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -45,6 +45,7 @@ lazy val rootProject = (project in file("."))
futureAsyncHttpClientHandler,
scalazAsyncHttpClientHandler,
monixAsyncHttpClientHandler,
+ catsAsyncHttpClientHandler,
okhttpClientHandler,
tests
)
@@ -105,6 +106,16 @@ lazy val monixAsyncHttpClientHandler: Project = (project in file(
)
) dependsOn asyncHttpClientHandler
+lazy val catsAsyncHttpClientHandler: Project = (project in file(
+ "async-http-client-handler/cats"))
+ .settings(commonSettings: _*)
+ .settings(
+ name := "async-http-client-handler-cats",
+ libraryDependencies ++= Seq(
+ "org.typelevel" %% "cats-effect" % "0.4"
+ )
+ ) dependsOn asyncHttpClientHandler
+
lazy val okhttpClientHandler: Project = (project in file(
"okhttp-client-handler"))
.settings(commonSettings: _*)
@@ -129,4 +140,4 @@ lazy val tests: Project = (project in file("tests"))
).map(_ % "test"),
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % "test"
) dependsOn (core, akkaHttpHandler, futureAsyncHttpClientHandler, scalazAsyncHttpClientHandler,
-monixAsyncHttpClientHandler, okhttpClientHandler)
+monixAsyncHttpClientHandler, catsAsyncHttpClientHandler, okhttpClientHandler)
diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
index 5f17a7a..36c82ef 100644
--- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
+++ b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
@@ -16,6 +16,7 @@ import com.typesafe.scalalogging.StrictLogging
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
import better.files._
+import com.softwaremill.sttp.asynchttpclient.cats.CatsAsyncHttpClientHandler
import com.softwaremill.sttp.asynchttpclient.future.FutureAsyncHttpClientHandler
import com.softwaremill.sttp.asynchttpclient.monix.MonixAsyncHttpClientHandler
import com.softwaremill.sttp.asynchttpclient.scalaz.ScalazAsyncHttpClientHandler
@@ -130,6 +131,9 @@ class BasicTests
ForceWrappedValue.scalazTask)
runTests("Async Http Client - Monix")(MonixAsyncHttpClientHandler(),
ForceWrappedValue.monixTask)
+ runTests("Async Http Client - Cats Effect")(
+ CatsAsyncHttpClientHandler[cats.effect.IO](),
+ ForceWrappedValue.catsIo)
runTests("OkHttpSyncClientHandler")(OkHttpSyncClientHandler(),
ForceWrappedValue.id)
runTests("OkHttpSyncClientHandler - Future")(OkHttpFutureClientHandler(),
diff --git a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala
index 9bf68bb..558e9dd 100644
--- a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala
+++ b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala
@@ -53,6 +53,10 @@ trait ForceWrapped extends ScalaFutures { this: Suite =>
override def force[T](wrapped: monix.eval.Task[T]): T =
wrapped.runAsync.futureValue
}
+ val catsIo = new ForceWrappedValue[cats.effect.IO] {
+ override def force[T](wrapped: cats.effect.IO[T]): T =
+ wrapped.unsafeRunSync
+ }
}
implicit class ForceDecorator[R[_], T](wrapped: R[T]) {
def force()(implicit fwv: ForceWrappedValue[R]): T = fwv.force(wrapped)