From 473aab2d8e29138956d801b33a9bbc394ac18ca0 Mon Sep 17 00:00:00 2001 From: xhh Date: Wed, 18 Nov 2015 17:21:32 +0800 Subject: [PATCH] Support file uploading in Clojure client --- .../src/main/resources/clojure/api.mustache | 7 ++-- .../src/main/resources/clojure/core.mustache | 39 ++++++++++++++----- .../petstore/clojure/resources/hello.txt | 1 + .../clojure/src/swagger_petstore/api/pet.clj | 5 ++- .../src/swagger_petstore/api/store.clj | 3 +- .../clojure/src/swagger_petstore/api/user.clj | 3 +- .../clojure/src/swagger_petstore/core.clj | 39 ++++++++++++++----- .../test/swagger_petstore/api/pet_test.clj | 14 +++---- .../test/swagger_petstore/core_test.clj | 14 ++++++- 9 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 samples/client/petstore/clojure/resources/hello.txt diff --git a/modules/swagger-codegen/src/main/resources/clojure/api.mustache b/modules/swagger-codegen/src/main/resources/clojure/api.mustache index 3d314531cdc..a4425280f1b 100644 --- a/modules/swagger-codegen/src/main/resources/clojure/api.mustache +++ b/modules/swagger-codegen/src/main/resources/clojure/api.mustache @@ -1,11 +1,12 @@ {{=< >=}}(ns . - (:require [.core :refer [call-api check-required-params]])) + (:require [.core :refer [call-api check-required-params]]) + (:import (java.io File))) <#operations><#operation> (defn "<&summary><#notes> <¬es>"<#hasOptionalParams> - ([<#allParams><#required> ] (<#allParams><#required> nil)) - <#hasOptionalParams>([<#allParams><#required> <#hasOptionalParams>{:keys [<#allParams><^required> ]}]<#hasRequiredParams> + ([<#allParams><#required><#isFile>^File ] (<#allParams><#required> nil)) + <#hasOptionalParams>([<#allParams><#required><#isFile>^File <#hasOptionalParams>{:keys [<#allParams><^required><#isFile>^File ]}]<#hasRequiredParams> <#hasOptionalParams> (check-required-params<#allParams><#required> ) <#hasOptionalParams> (call-api "" : <#hasOptionalParams> {:path-params {<#pathParams>"" } diff --git a/modules/swagger-codegen/src/main/resources/clojure/core.mustache b/modules/swagger-codegen/src/main/resources/clojure/core.mustache index 50d23a6a740..32aa8a6bc0d 100644 --- a/modules/swagger-codegen/src/main/resources/clojure/core.mustache +++ b/modules/swagger-codegen/src/main/resources/clojure/core.mustache @@ -3,6 +3,7 @@ [clojure.string :as str] [clj-http.client :as client]) (:import (com.fasterxml.jackson.core JsonParseException) + (java.io File) (java.util Date TimeZone) (java.text SimpleDateFormat))) @@ -90,17 +91,24 @@ path-params)] (str (:base-url *api-context*) path))) +(defn normalize-param + "Normalize parameter value, handling three cases: + for sequential value, normalize each elements of it; + for File value, do nothing with it; + otherwise, call `param-to-string`." + [param] + (cond + (sequential? param) (map normalize-param param) + (instance? File param) param + :else (param-to-str param))) + (defn normalize-params "Normalize parameters values: remove nils, format to string with `param-to-str`." [params] - (reduce (fn [result [k v]] - (if (nil? v) - result - (assoc result k (if (sequential? v) - (map param-to-str v) - (param-to-str v))))) - {} - params)) + (->> params + (remove (comp nil? second)) + (map (fn [[k v]] [k (normalize-param v)])) + (into {}))) (defn json-mime? [mime] "Check if the given MIME is a standard JSON MIME or :json." @@ -136,6 +144,13 @@ ;; for non-JSON response, return the body string directly :else body)) +(defn form-params-to-multipart + "Convert the given form parameters map into a vector as clj-http's :multipart option." + [form-params] + (->> form-params + (map (fn [[k v]] (array-map :name k :content v))) + vec)) + (defn call-api "Call an API by making HTTP request and return its response." [path method {:keys [path-params query-params header-params form-params body-param content-types accepts]}] @@ -144,12 +159,16 @@ content-type (or (json-preferred-mime content-types) (and body-param :json)) accept (or (json-preferred-mime accepts) :json) + multipart? (= "multipart/form-data" content-type) opts (cond-> {:url url :method method} - content-type (assoc :content-type content-type) accept (assoc :accept accept) (seq query-params) (assoc :query-params (normalize-params query-params)) (seq header-params) (assoc :header-params (normalize-params header-params)) - (seq form-params) (assoc :form-params (normalize-params form-params)) + (and content-type (not multipart?)) (assoc :content-type content-type) + multipart? (assoc :multipart (-> form-params + normalize-params + form-params-to-multipart)) + (and (not multipart?) (seq form-params)) (assoc :form-params (normalize-params form-params)) body-param (assoc :body (serialize body-param content-type)) debug (assoc :debug true :debug-body true)) resp (client/request opts)] diff --git a/samples/client/petstore/clojure/resources/hello.txt b/samples/client/petstore/clojure/resources/hello.txt new file mode 100644 index 00000000000..6769dd60bdf --- /dev/null +++ b/samples/client/petstore/clojure/resources/hello.txt @@ -0,0 +1 @@ +Hello world! \ No newline at end of file diff --git a/samples/client/petstore/clojure/src/swagger_petstore/api/pet.clj b/samples/client/petstore/clojure/src/swagger_petstore/api/pet.clj index f73ef83893e..c331444c886 100644 --- a/samples/client/petstore/clojure/src/swagger_petstore/api/pet.clj +++ b/samples/client/petstore/clojure/src/swagger_petstore/api/pet.clj @@ -1,5 +1,6 @@ (ns swagger-petstore.api.pet - (:require [swagger-petstore.core :refer [call-api check-required-params]])) + (:require [swagger-petstore.core :refer [call-api check-required-params]]) + (:import (java.io File))) (defn update-pet "Update an existing pet @@ -97,7 +98,7 @@ "uploads an image " ([pet-id ] (upload-file pet-id nil)) - ([pet-id {:keys [additional-metadata file ]}] + ([pet-id {:keys [additional-metadata ^File file ]}] (call-api "/pet/{petId}/uploadImage" :post {:path-params {"petId" pet-id } :header-params {} diff --git a/samples/client/petstore/clojure/src/swagger_petstore/api/store.clj b/samples/client/petstore/clojure/src/swagger_petstore/api/store.clj index 4d5ac3ca325..f2a18cea2e8 100644 --- a/samples/client/petstore/clojure/src/swagger_petstore/api/store.clj +++ b/samples/client/petstore/clojure/src/swagger_petstore/api/store.clj @@ -1,5 +1,6 @@ (ns swagger-petstore.api.store - (:require [swagger-petstore.core :refer [call-api check-required-params]])) + (:require [swagger-petstore.core :refer [call-api check-required-params]]) + (:import (java.io File))) (defn get-inventory "Returns pet inventories by status diff --git a/samples/client/petstore/clojure/src/swagger_petstore/api/user.clj b/samples/client/petstore/clojure/src/swagger_petstore/api/user.clj index 9f34a1be537..15d41515ebb 100644 --- a/samples/client/petstore/clojure/src/swagger_petstore/api/user.clj +++ b/samples/client/petstore/clojure/src/swagger_petstore/api/user.clj @@ -1,5 +1,6 @@ (ns swagger-petstore.api.user - (:require [swagger-petstore.core :refer [call-api check-required-params]])) + (:require [swagger-petstore.core :refer [call-api check-required-params]]) + (:import (java.io File))) (defn create-user "Create user diff --git a/samples/client/petstore/clojure/src/swagger_petstore/core.clj b/samples/client/petstore/clojure/src/swagger_petstore/core.clj index 85856674a19..e7c55258e79 100644 --- a/samples/client/petstore/clojure/src/swagger_petstore/core.clj +++ b/samples/client/petstore/clojure/src/swagger_petstore/core.clj @@ -3,6 +3,7 @@ [clojure.string :as str] [clj-http.client :as client]) (:import (com.fasterxml.jackson.core JsonParseException) + (java.io File) (java.util Date TimeZone) (java.text SimpleDateFormat))) @@ -90,17 +91,24 @@ path-params)] (str (:base-url *api-context*) path))) +(defn normalize-param + "Normalize parameter value, handling three cases: + for sequential value, normalize each elements of it; + for File value, do nothing with it; + otherwise, call `param-to-string`." + [param] + (cond + (sequential? param) (map normalize-param param) + (instance? File param) param + :else (param-to-str param))) + (defn normalize-params "Normalize parameters values: remove nils, format to string with `param-to-str`." [params] - (reduce (fn [result [k v]] - (if (nil? v) - result - (assoc result k (if (sequential? v) - (map param-to-str v) - (param-to-str v))))) - {} - params)) + (->> params + (remove (comp nil? second)) + (map (fn [[k v]] [k (normalize-param v)])) + (into {}))) (defn json-mime? [mime] "Check if the given MIME is a standard JSON MIME or :json." @@ -136,6 +144,13 @@ ;; for non-JSON response, return the body string directly :else body)) +(defn form-params-to-multipart + "Convert the given form parameters map into a vector as clj-http's :multipart option." + [form-params] + (->> form-params + (map (fn [[k v]] (array-map :name k :content v))) + vec)) + (defn call-api "Call an API by making HTTP request and return its response." [path method {:keys [path-params query-params header-params form-params body-param content-types accepts]}] @@ -144,12 +159,16 @@ content-type (or (json-preferred-mime content-types) (and body-param :json)) accept (or (json-preferred-mime accepts) :json) + multipart? (= "multipart/form-data" content-type) opts (cond-> {:url url :method method} - content-type (assoc :content-type content-type) accept (assoc :accept accept) (seq query-params) (assoc :query-params (normalize-params query-params)) (seq header-params) (assoc :header-params (normalize-params header-params)) - (seq form-params) (assoc :form-params (normalize-params form-params)) + (and content-type (not multipart?)) (assoc :content-type content-type) + multipart? (assoc :multipart (-> form-params + normalize-params + form-params-to-multipart)) + (and (not multipart?) (seq form-params)) (assoc :form-params (normalize-params form-params)) body-param (assoc :body (serialize body-param content-type)) debug (assoc :debug true :debug-body true)) resp (client/request opts)] diff --git a/samples/client/petstore/clojure/test/swagger_petstore/api/pet_test.clj b/samples/client/petstore/clojure/test/swagger_petstore/api/pet_test.clj index 8acd528c5a5..7327b44c45d 100644 --- a/samples/client/petstore/clojure/test/swagger_petstore/api/pet_test.clj +++ b/samples/client/petstore/clojure/test/swagger_petstore/api/pet_test.clj @@ -77,11 +77,9 @@ (delete-pet id) (is (thrown? RuntimeException (get-pet-by-id id))))) -(comment - ;; TODO support file uploading - (deftest test-upload-file - (let [{:keys [id] :as pet} (make-random-pet) - _ (add-pet {:body pet}) - file (io/file (io/resource "hello.txt"))] - ;; no errors - (upload-file id {:file file :additionalMetadata "uploading file with clojure client"})))) +(deftest test-upload-file + (let [{:keys [id] :as pet} (make-random-pet) + _ (add-pet {:body pet}) + file (io/file (io/resource "hello.txt"))] + ;; no errors with upload-file + (upload-file id {:file file :additional-metadata "uploading file with clojure client"}))) diff --git a/samples/client/petstore/clojure/test/swagger_petstore/core_test.clj b/samples/client/petstore/clojure/test/swagger_petstore/core_test.clj index ac39e8a503f..394824aa4a9 100644 --- a/samples/client/petstore/clojure/test/swagger_petstore/core_test.clj +++ b/samples/client/petstore/clojure/test/swagger_petstore/core_test.clj @@ -1,5 +1,6 @@ (ns swagger-petstore.core-test - (:require [clojure.test :refer :all] + (:require [clojure.java.io :as io] + [clojure.test :refer :all] [swagger-petstore.core :refer :all]) (:import (java.text ParseException))) @@ -87,8 +88,17 @@ "/pet" {"id" 1} "http://petstore.swagger.io/v2/pet" "/pet/{id}" nil "http://petstore.swagger.io/v2/pet/{id}")) +(deftest test-normalize-param + (let [file (-> "hello.txt" io/resource io/file)] + (are [param expected] + (is (= expected (normalize-param param))) + [12 "34"] ["12" "34"] + file file + "abc" "abc" + [[12 "34"] file "abc"] [["12" "34"] file "abc"]))) + (deftest test-normalize-params - (is (= {:a "123" :b ["4" "5,6"]} + (is (= {:a "123" :b ["4" ["5" "6"]]} (normalize-params {:a 123 :b [4 [5 "6"]] :c nil})))) (deftest test-json-mime?