Skip to content

Commit

Permalink
feat: support :multipart options, modeled after clj-http [babashka#26]
Browse files Browse the repository at this point in the history
  • Loading branch information
johnchristopherjones committed Sep 19, 2020
1 parent eee04eb commit 4d48326
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 4 deletions.
26 changes: 24 additions & 2 deletions src/babashka/curl.clj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@
[^String unencoded]
(URLEncoder/encode unencoded "UTF-8"))

(defn- refer-file
"If the value is a file, translate into a curl file read."
[v]
(if (file? v) (str "@" (.getPath ^File v)) v))

(defn create-multipart-param
[{:keys [name part-name content mime-type] :as part}]
(if (vector? part)
["--form" (str/join "=" part)]
(let [k (or part-name name)
v (refer-file content)
t (when mime-type (str ";type=" mime-type))
headers-keys (remove #{:name :part-name :content :mime-type} part)]
["--form" (str k "=" v t)]))
)

(defn- create-multipart-params
"Take a multipart vector of maps and create a seq of curl form parameters."
[multipart]
(apply concat (map create-multipart-param multipart)))

(defn- curl-command [opts]
(let [body (:body opts)
opts (if body
Expand All @@ -82,10 +103,11 @@
kvs (seq form-params)]
(if kvs
(let [[k v] (first kvs)
v (if (file? v) (str "@" (.getPath ^File v)) v)
v (refer-file v)
param ["--data" (str (url-encode k) "=" (url-encode v))]]
(recur (reduce conj! params* param) (next kvs)))
(persistent! params*))))
multipart-params (create-multipart-params (:multipart opts))
;; TODO:
;; multipart-params (when-let [multipart (:multipart opts)]
;; (loop [params* (transient [])
Expand Down Expand Up @@ -138,7 +160,7 @@
stream? (identical? :stream (:as opts))]
[(conj (reduce into ["curl" "--silent" "--show-error" "--location" "--dump-header" header-file]
[method headers accept-header data-raw in-file in-stream basic-auth
form-params #_multipart-params
form-params multipart-params
;; tested with SSE server, e.g. https://github.com/enkot/SSE-Fake-Server
(when stream? ["-N"])
(:raw-args opts)])
Expand Down
38 changes: 36 additions & 2 deletions test/babashka/curl_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@
headers (:headers body)
content-type (:content-type headers)]
(is (= "application/x-www-form-urlencoded" content-type))))
;; TODO:
#_(testing "multipart"
(testing "multipart"
(testing "posting file"
(let [tmp-file (java.io.File/createTempFile "foo" "bar")
_ (spit tmp-file "Michiel Borkent")
Expand All @@ -81,6 +80,8 @@
(is (str/starts-with? content-type "multipart/form-data"))
(is (:files body))
(is (str/includes? (-> body :form :filename) "foo"))
#_(is (str/includes? (-> body :form :filename) "filename"))
#_(is (str/includes? (-> body :form :filename) "file2"))
(prn body)))))

(deftest patch-test
Expand Down Expand Up @@ -244,3 +245,36 @@
(let [response (curl/get "https://httpstat.us/404" {:throw false})]
(is (= 404 (:status response)))
(is (zero? (:exit response))))))


(deftest create-multipart-params-test
(testing "nil multipart"
(is (= nil (seq (#'curl/create-multipart-params nil)))))
(testing "string content"
(let [multipart [{:name "foo" :content "bar"}]]
(is (= ["--form" "foo=bar"]
(#'curl/create-multipart-params multipart)))))
(testing "with :mime-type"
(let [multipart [{:name "boo" :content "baz" :mime-type "text/plain"}]]
(is (= ["--form" "boo=baz;type=text/plain"]
(#'curl/create-multipart-params multipart)))))
(testing "with :part-name"
(let [multipart [{:name "boo" :content "baz" :mime-type "text/plain" :part-name "hotdog"}]]
(is (= ["--form" "hotdog=baz;type=text/plain"]
(#'curl/create-multipart-params multipart)))))
(testing "File content"
(let [multipart [{:name "myfile" :content (io/file "README.md")}]]
(is (= ["--form" "myfile=@README.md"]
(#'curl/create-multipart-params multipart)))))
#_(testing "vector part"
(let [multipart [["file4" (io/file "README.md")]]]
(is (= ["--form" "file4=@README.md"]
(#'curl/create-multipart-params multipart)))))
(testing "multiple fields"
(let [multipart [{:name "title" :content "awesome"}
{:name "foo" :part-name "eggplant" :content "zack" :mime-type "text/plain"}
{:name "myfile" :content (io/file "README.md")}]]
(is (= ["--form" "title=awesome"
"--form" "eggplant=zack;type=text/plain"
"--form" "myfile=@README.md"]
(#'curl/create-multipart-params multipart))))))

0 comments on commit 4d48326

Please sign in to comment.