aboutsummaryrefslogtreecommitdiff
path: root/json
diff options
context:
space:
mode:
authoradamw <adam@warski.org>2017-10-18 16:25:07 +0200
committeradamw <adam@warski.org>2017-10-18 16:25:07 +0200
commita3abbd502783df6df4de98c5dbf0c8eff81cb511 (patch)
tree5457480605483e197ffc658a3f1dcb589a910e99 /json
parent21c4700bbe8cf37d7b9feacc5afdf64357604d8f (diff)
downloadsttp-a3abbd502783df6df4de98c5dbf0c8eff81cb511.tar.gz
sttp-a3abbd502783df6df4de98c5dbf0c8eff81cb511.tar.bz2
sttp-a3abbd502783df6df4de98c5dbf0c8eff81cb511.zip
json4s support
Diffstat (limited to 'json')
-rw-r--r--json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala15
-rw-r--r--json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala105
-rw-r--r--json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala14
-rw-r--r--json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala75
4 files changed, 209 insertions, 0 deletions
diff --git a/json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala b/json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala
new file mode 100644
index 0000000..ddc9583
--- /dev/null
+++ b/json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala
@@ -0,0 +1,15 @@
+package com.softwaremill.sttp
+
+import io.circe.parser._
+import io.circe.{Decoder, Encoder}
+
+package object circe {
+
+ implicit def circeBodySerializer[B](
+ implicit encoder: Encoder[B]): BodySerializer[B] =
+ b => StringBody(encoder(b).noSpaces, Utf8, Some(ApplicationJsonContentType))
+
+ def asJson[B: Decoder]: ResponseAs[Either[io.circe.Error, B], Nothing] =
+ asString(Utf8).map(decode[B])
+
+}
diff --git a/json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala b/json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
new file mode 100644
index 0000000..1e7dddc
--- /dev/null
+++ b/json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
@@ -0,0 +1,105 @@
+package com.softwaremill.sttp
+
+import io.circe._
+import org.scalatest._
+
+import scala.language.higherKinds
+
+class CirceTests extends FlatSpec with Matchers with EitherValues {
+ import circe._
+
+ "The circe module" should "encode arbitrary bodies given an encoder" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val expected = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+
+ val req = sttp.body(body)
+
+ extractBody(req) shouldBe expected
+ }
+
+ it should "decode arbitrary bodies given a decoder" in {
+ val body = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+ val expected = Outer(Inner(42, true, "horses"), "cats")
+
+ val responseAs = asJson[Outer]
+
+ runJsonResponseAs(responseAs)(body).right.value shouldBe expected
+ }
+
+ it should "fail to decode invalid json" in {
+ val body = """not valid json"""
+
+ val responseAs = asJson[Outer]
+
+ runJsonResponseAs(responseAs)(body).left.value shouldBe an[io.circe.Error]
+ }
+
+ it should "encode and decode back to the same thing" in {
+ val outer = Outer(Inner(42, true, "horses"), "cats")
+
+ val encoded = extractBody(sttp.body(outer))
+ val decoded = runJsonResponseAs(asJson[Outer])(encoded)
+
+ decoded.right.value shouldBe outer
+ }
+
+ it should "set the content type" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val req = sttp.body(body)
+
+ val ct = req.headers.toMap.get("Content-Type")
+
+ ct shouldBe Some(contentTypeWithEncoding(ApplicationJsonContentType, Utf8))
+ }
+
+ it should "only set the content type if it was not set earlier" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val req = sttp.contentType("horses/cats").body(body)
+
+ val ct = req.headers.toMap.get("Content-Type")
+
+ ct shouldBe Some("horses/cats")
+ }
+
+ case class Inner(a: Int, b: Boolean, c: String)
+
+ object Inner {
+ implicit val encoder: Encoder[Inner] =
+ Encoder.forProduct3("a", "b", "c")(i => (i.a, i.b, i.c))
+ implicit val decoder: Decoder[Inner] =
+ Decoder.forProduct3("a", "b", "c")(Inner.apply)
+ }
+
+ case class Outer(foo: Inner, bar: String)
+
+ object Outer {
+ implicit val encoder: Encoder[Outer] =
+ Encoder.forProduct2("foo", "bar")(o => (o.foo, o.bar))
+ implicit val decoder: Decoder[Outer] =
+ Decoder.forProduct2("foo", "bar")(Outer.apply)
+ }
+
+ def extractBody[A[_], B, C](request: RequestT[A, B, C]): String =
+ request.body match {
+ case StringBody(body, "utf-8", Some(ApplicationJsonContentType)) =>
+ body
+ case wrongBody =>
+ fail(
+ s"Request body does not serialize to correct StringBody: $wrongBody")
+ }
+
+ def runJsonResponseAs[A](responseAs: ResponseAs[A, Nothing]): String => A =
+ responseAs match {
+ case responseAs: MappedResponseAs[_, A, Nothing] =>
+ responseAs.raw match {
+ case ResponseAsString("utf-8") =>
+ responseAs.g
+ case ResponseAsString(encoding) =>
+ fail(
+ s"MappedResponseAs wraps a ResponseAsString with wrong encoding: $encoding")
+ case _ =>
+ fail("MappedResponseAs does not wrap a ResponseAsString")
+ }
+ case _ => fail("ResponseAs is not a MappedResponseAs")
+ }
+}
diff --git a/json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala b/json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala
new file mode 100644
index 0000000..4c7aa36
--- /dev/null
+++ b/json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala
@@ -0,0 +1,14 @@
+package com.softwaremill.sttp
+
+import org.json4s._
+import org.json4s.native.Serialization.{read, write}
+
+package object json4s {
+ implicit def json4sBodySerializer[B <: AnyRef](
+ implicit formats: Formats = DefaultFormats): BodySerializer[B] =
+ b => StringBody(write(b), Utf8, Some(ApplicationJsonContentType))
+
+ def asJson[B: Manifest](
+ implicit formats: Formats = DefaultFormats): ResponseAs[B, Nothing] =
+ asString(Utf8).map(s => read[B](s))
+}
diff --git a/json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala b/json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala
new file mode 100644
index 0000000..bb4a774
--- /dev/null
+++ b/json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala
@@ -0,0 +1,75 @@
+package com.softwaremill.sttp
+
+import org.json4s.ParserUtil.ParseException
+import org.scalatest._
+
+import scala.language.higherKinds
+
+class Json4sTests extends FlatSpec with Matchers with EitherValues {
+ import json4s._
+ import Json4sTests._
+
+ "The json4s module" should "encode arbitrary json bodies" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val expected = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+
+ val req = sttp.body(body)
+
+ extractBody(req) shouldBe expected
+ }
+
+ it should "decode arbitrary bodies" in {
+ val body = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+ val expected = Outer(Inner(42, true, "horses"), "cats")
+
+ val responseAs = asJson[Outer]
+
+ runJsonResponseAs(responseAs)(body) shouldBe expected
+ }
+
+ it should "fail to decode invalid json" in {
+ val body = """not valid json"""
+
+ val responseAs = asJson[Outer]
+
+ an[ParseException] should be thrownBy runJsonResponseAs(responseAs)(body)
+ }
+
+ it should "set the content type" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val req = sttp.body(body)
+
+ val ct = req.headers.toMap.get("Content-Type")
+
+ ct shouldBe Some(contentTypeWithEncoding(ApplicationJsonContentType, Utf8))
+ }
+
+ def extractBody[A[_], B, C](request: RequestT[A, B, C]): String =
+ request.body match {
+ case StringBody(body, "utf-8", Some(ApplicationJsonContentType)) =>
+ body
+ case wrongBody =>
+ fail(
+ s"Request body does not serialize to correct StringBody: $wrongBody")
+ }
+
+ def runJsonResponseAs[A](responseAs: ResponseAs[A, Nothing]): String => A =
+ responseAs match {
+ case responseAs: MappedResponseAs[_, A, Nothing] =>
+ responseAs.raw match {
+ case ResponseAsString("utf-8") =>
+ responseAs.g
+ case ResponseAsString(encoding) =>
+ fail(
+ s"MappedResponseAs wraps a ResponseAsString with wrong encoding: $encoding")
+ case _ =>
+ fail("MappedResponseAs does not wrap a ResponseAsString")
+ }
+ case _ => fail("ResponseAs is not a MappedResponseAs")
+ }
+}
+
+object Json4sTests {
+ case class Inner(a: Int, b: Boolean, c: String)
+ case class Outer(foo: Inner, bar: String)
+}