aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Madsen <bm@aeons.dk>2017-08-08 08:16:36 +0200
committerBjørn Madsen <bm@aeons.dk>2017-08-08 08:16:36 +0200
commit18db8e9d1ee240d11b558cdb9b5c850c5b063080 (patch)
treeda599b595872cbf26813529fec022b1c0bb99b39
parente79d090ed707942f534db98abb1a1754c3584329 (diff)
downloadsttp-18db8e9d1ee240d11b558cdb9b5c850c5b063080.tar.gz
sttp-18db8e9d1ee240d11b558cdb9b5c850c5b063080.tar.bz2
sttp-18db8e9d1ee240d11b558cdb9b5c850c5b063080.zip
Expand BodySerializer to carry a default content type
And update circe module to use this
-rw-r--r--README.md2
-rw-r--r--circe/src/main/scala/com/softwaremill/sttp/circe.scala22
-rw-r--r--circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala8
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/BodySerializer.scala29
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/RequestT.scala8
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/package.scala6
6 files changed, 44 insertions, 31 deletions
diff --git a/README.md b/README.md
index 084d27d..99135ee 100644
--- a/README.md
+++ b/README.md
@@ -400,7 +400,7 @@ val requestPayload: Payload = ???
val response: Either[io.circe.Error, Response] =
sttp
.post(uri"...")
- .jsonBody(requestPayload)
+ .body(requestPayload)
.response(asJson[Response])
.send()
```
diff --git a/circe/src/main/scala/com/softwaremill/sttp/circe.scala b/circe/src/main/scala/com/softwaremill/sttp/circe.scala
index 0233f08..f273e42 100644
--- a/circe/src/main/scala/com/softwaremill/sttp/circe.scala
+++ b/circe/src/main/scala/com/softwaremill/sttp/circe.scala
@@ -1,11 +1,6 @@
package com.softwaremill.sttp
-import com.softwaremill.sttp.model.{
- RequestBody,
- ResponseAs,
- SerializableBody,
- StringBody
-}
+import com.softwaremill.sttp.model.{ResponseAs, StringBody}
import io.circe.parser._
import io.circe.{Decoder, Encoder}
@@ -14,17 +9,10 @@ import scala.language.higherKinds
package object circe {
private[sttp] val ApplicationJsonContentType = "application/json"
- private def serializeBody[B](body: B)(
- implicit encoder: Encoder[B]): RequestBody[Nothing] =
- SerializableBody((b: B) => StringBody(encoder(b).noSpaces, Utf8), body)
-
- implicit final class CirceRequestTOps[U[_], T, +S](val req: RequestT[U, T, S])
- extends AnyVal {
- def jsonBody[B: Encoder](body: B): RequestT[U, T, S] =
- req
- .setContentTypeIfMissing(ApplicationJsonContentType)
- .copy(body = serializeBody(body))
- }
+ implicit def circeBodySerializer[B: Encoder]: BodySerializer[B] =
+ BodySerializer.instance(
+ body => StringBody(Encoder[B].apply(body).noSpaces, Utf8),
+ ApplicationJsonContentType)
def asJson[B: Decoder]: ResponseAs[Either[io.circe.Error, B], Nothing] =
asString(Utf8).map(decode[B])
diff --git a/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala b/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
index abd20ef..81b90e6 100644
--- a/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
+++ b/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
@@ -13,7 +13,7 @@ class CirceTests extends FlatSpec with Matchers with EitherValues {
val body = Outer(Inner(42, true, "horses"), "cats")
val expected = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
- val req = sttp.jsonBody(body)
+ val req = sttp.body(body)
extractBody(req) shouldBe expected
}
@@ -38,7 +38,7 @@ class CirceTests extends FlatSpec with Matchers with EitherValues {
it should "should encode and decode back to the same thing" in {
val outer = Outer(Inner(42, true, "horses"), "cats")
- val encoded = extractBody(sttp.jsonBody(outer))
+ val encoded = extractBody(sttp.body(outer))
val decoded = runJsonResponseAs(asJson[Outer])(encoded)
decoded.right.value shouldBe outer
@@ -46,7 +46,7 @@ class CirceTests extends FlatSpec with Matchers with EitherValues {
it should "set the content type" in {
val body = Outer(Inner(42, true, "horses"), "cats")
- val req = sttp.jsonBody(body)
+ val req = sttp.body(body)
val ct = req.headers.toMap.get("Content-Type")
@@ -55,7 +55,7 @@ class CirceTests extends FlatSpec with Matchers with EitherValues {
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").jsonBody(body)
+ val req = sttp.contentType("horses/cats").body(body)
val ct = req.headers.toMap.get("Content-Type")
diff --git a/core/src/main/scala/com/softwaremill/sttp/BodySerializer.scala b/core/src/main/scala/com/softwaremill/sttp/BodySerializer.scala
new file mode 100644
index 0000000..040c563
--- /dev/null
+++ b/core/src/main/scala/com/softwaremill/sttp/BodySerializer.scala
@@ -0,0 +1,29 @@
+package com.softwaremill.sttp
+
+import com.softwaremill.sttp.model.BasicRequestBody
+
+/**
+ * Provide an implicit value of this type to serialize arbitrary classes into a request body.
+ * Handlers might also provide special logic for serializer instances which they define (e.g. to handle streaming).
+ */
+trait BodySerializer[B] {
+ def apply(body: B): BasicRequestBody
+ def defaultContentType: Option[String]
+}
+
+object BodySerializer {
+ final def apply[B](implicit instance: BodySerializer[B]): BodySerializer[B] =
+ instance
+
+ final def instance[B](f: B => BasicRequestBody) =
+ new BodySerializer[B] {
+ def apply(body: B): BasicRequestBody = f(body)
+ val defaultContentType: Option[String] = None
+ }
+
+ final def instance[B](f: B => BasicRequestBody, contentType: String) =
+ new BodySerializer[B] {
+ def apply(body: B): BasicRequestBody = f(body)
+ val defaultContentType: Option[String] = Option(contentType)
+ }
+}
diff --git a/core/src/main/scala/com/softwaremill/sttp/RequestT.scala b/core/src/main/scala/com/softwaremill/sttp/RequestT.scala
index 4e38662..c17be59 100644
--- a/core/src/main/scala/com/softwaremill/sttp/RequestT.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/RequestT.scala
@@ -203,9 +203,11 @@ case class RequestT[U[_], T, +S](
* If content type is not yet specified, will be set to
* `application/octet-stream`.
*/
- def body[B: BodySerializer](b: B): RequestT[U, T, S] =
- setContentTypeIfMissing(ApplicationOctetStreamContentType).copy(
- body = SerializableBody(implicitly[BodySerializer[B]], b))
+ def body[B](b: B)(implicit serializer: BodySerializer[B]): RequestT[U, T, S] =
+ setContentTypeIfMissing(
+ serializer.defaultContentType.getOrElse(
+ ApplicationOctetStreamContentType))
+ .copy(body = SerializableBody(serializer, b))
//def multipartData(parts: MultiPart*): RequestTemplate[U] = ???
diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala
index 884d2f9..cca86dd 100644
--- a/core/src/main/scala/com/softwaremill/sttp/package.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/package.scala
@@ -22,12 +22,6 @@ package object sttp {
".get(...), .post(...) etc. to obtain a non-partial request.")
private[sttp] type IsIdInRequest[U[_]] = U[Unit] =:= Id[Unit]
- /**
- * Provide an implicit value of this type to serialize arbitrary classes into a request body.
- * Handlers might also provide special logic for serializer instances which they define (e.g. to handle streaming).
- */
- type BodySerializer[B] = B => BasicRequestBody
-
// constants
private[sttp] val ContentTypeHeader = "Content-Type"