Skip to content

Commit

Permalink
Merge pull request #967 from phdoerfler/toCurl
Browse files Browse the repository at this point in the history
Changed .toCurl output in an attempt to make it easier to read and purely out of personal preference
  • Loading branch information
adamw authored May 19, 2021
2 parents b17ea81 + 1f6d952 commit 8528a44
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 25 deletions.
24 changes: 15 additions & 9 deletions core/src/main/scala/sttp/client3/internal/ToCurlConverter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,30 @@ import sttp.model.MediaType

class ToCurlConverter[R <: RequestT[Identity, _, _]] {
def apply(request: R): String = {
val params = List(extractOptions(_), extractMethod(_), extractHeaders(_), extractBody(_))
val params = List(extractMethod(_), extractUrl(_), extractHeaders(_), extractBody(_), extractOptions(_))
.map(addSpaceIfNotEmpty)
.reduce((acc, item) => r => acc(r) + item(r))
.apply(request)

s"""curl$params '${request.uri}'"""
s"""curl$params"""
}

private def extractMethod(r: R): String = {
s"-X ${r.method.method}"
s"--request ${r.method.method}"
}

private def extractUrl(r: R): String = {
s"--url '${r.uri}'"
}

private def extractHeaders(r: R): String = {
r.headers
// filtering out compression headers so that the results are human-readable, if possible
.filterNot(_.name.equalsIgnoreCase(HeaderNames.AcceptEncoding))
.collect { case Header(k, v) =>
s"""-H '$k: $v'"""
s"""--header '$k: $v'"""
}
.mkString(" ")
.mkString(newline)
}

private def extractBody(r: R): String = {
Expand All @@ -37,7 +41,7 @@ class ToCurlConverter[R <: RequestT[Identity, _, _]] {
.toMap
.get(HeaderNames.ContentType)
.forall(_ == MediaType.ApplicationXWwwFormUrlencoded.toString) =>
s"""-F '${text.replace("'", "\\'")}'"""
s"""--form '${text.replace("'", "\\'")}'"""
case StringBody(text, _, _) => s"""--data '${text.replace("'", "\\'")}'"""
case ByteArrayBody(_, _) => s"--data-binary <PLACEHOLDER>"
case ByteBufferBody(_, _) => s"--data-binary <PLACEHOLDER>"
Expand All @@ -58,19 +62,21 @@ class ToCurlConverter[R <: RequestT[Identity, _, _]] {
case _ => s"--data-binary <PLACEHOLDER>"
}
}
.mkString(" ")
.mkString(newline)
}

private def extractOptions(r: R): String = {
if (r.options.followRedirects) {
s"-L --max-redirs ${r.options.maxRedirects}"
s"--location${newline}--max-redirs ${r.options.maxRedirects}"
} else {
""
}
}

private def addSpaceIfNotEmpty(fInput: R => String): R => String =
t => if (fInput(t).isEmpty) "" else s" ${fInput(t)}"
t => if (fInput(t).isEmpty) "" else s"${newline}${fInput(t)}"

private def newline: String = " \\\n "
}

object ToCurlConverter {
Expand Down
32 changes: 16 additions & 16 deletions core/src/test/scala/sttp/client3/ToCurlConverterTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,45 @@ class ToCurlConverterTest extends AnyFlatSpec with Matchers with ToCurlConverter
it should "convert base request" in {
basicRequest
.get(uri"$localhost")
.toCurl shouldBe """curl -L --max-redirs 32 -X GET 'http://localhost'"""
.toCurl shouldBe "curl \\\n --request GET \\\n --url 'http://localhost' \\\n --location \\\n --max-redirs 32"
}

it should "convert request with method to curl" in {
basicRequest.get(localhost).toCurl should include("-X GET")
basicRequest.post(localhost).toCurl should include("-X POST")
basicRequest.put(localhost).toCurl should include("-X PUT")
basicRequest.delete(localhost).toCurl should include("-X DELETE")
basicRequest.patch(localhost).toCurl should include("-X PATCH")
basicRequest.head(localhost).toCurl should include("-X HEAD")
basicRequest.options(localhost).toCurl should include("-X OPTIONS")
basicRequest.get(localhost).toCurl should include("--request GET")
basicRequest.post(localhost).toCurl should include("--request POST")
basicRequest.put(localhost).toCurl should include("--request PUT")
basicRequest.delete(localhost).toCurl should include("--request DELETE")
basicRequest.patch(localhost).toCurl should include("--request PATCH")
basicRequest.head(localhost).toCurl should include("--request HEAD")
basicRequest.options(localhost).toCurl should include("--request OPTIONS")
}

it should "convert request with header" in {
basicRequest.header("User-Agent", "myapp").get(localhost).toCurl should include(
"""-H 'User-Agent: myapp'"""
"""--header 'User-Agent: myapp'"""
)
}

it should "convert request with body" in {
basicRequest.body(Map("name" -> "john", "org" -> "sml")).post(localhost).toCurl should include(
"""-H 'Content-Type: application/x-www-form-urlencoded' -H 'Content-Length: 17' -F 'name=john&org=sml'"""
"--header 'Content-Type: application/x-www-form-urlencoded' \\\n --header 'Content-Length: 17' \\\n --form 'name=john&org=sml'"
)
basicRequest.body("name=john").post(localhost).toCurl should include(
"""-H 'Content-Type: text/plain; charset=utf-8' -H 'Content-Length: 9' --data 'name=john'"""
"--header 'Content-Type: text/plain; charset=utf-8' \\\n --header 'Content-Length: 9' \\\n --data 'name=john'"
)
basicRequest.body("name=john", StandardCharsets.ISO_8859_1.name()).post(localhost).toCurl should include(
"""-H 'Content-Type: text/plain; charset=ISO-8859-1' -H 'Content-Length: 9' --data 'name=john'"""
"--header 'Content-Type: text/plain; charset=ISO-8859-1' \\\n --header 'Content-Length: 9' \\\n --data 'name=john'"
)
basicRequest.body("name='john'").post(localhost).toCurl should include(
"""-H 'Content-Type: text/plain; charset=utf-8' -H 'Content-Length: 11' --data 'name=\'john\''"""
"--header 'Content-Type: text/plain; charset=utf-8' \\\n --header 'Content-Length: 11' \\\n --data 'name=\\'john\\''"
)
basicRequest.body("name=\"john\"").post(localhost).toCurl should include(
"""-H 'Content-Type: text/plain; charset=utf-8' -H 'Content-Length: 11' --data 'name="john"'"""
"--header 'Content-Type: text/plain; charset=utf-8' \\\n --header 'Content-Length: 11' \\\n --data 'name=\"john\"'"
)
}

it should "convert request with options" in {
basicRequest.followRedirects(false).get(localhost).toCurl should not include "-L"
basicRequest.followRedirects(false).get(localhost).toCurl should not include "--location"
basicRequest.maxRedirects(11).get(localhost).toCurl should include("--max-redirs 11")
}

Expand All @@ -66,7 +66,7 @@ class ToCurlConverterTest extends AnyFlatSpec with Matchers with ToCurlConverter

it should "render multipart form data if content is a plain string" in {
basicRequest.multipartBody(multipart("k1", "v1"), multipart("k2", "v2")).post(localhost).toCurl should include(
"""--form 'k1=v1' --form 'k2=v2'"""
"--form 'k1=v1' \\\n --form 'k2=v2'"
)
}
}

0 comments on commit 8528a44

Please sign in to comment.