Repository: oliyh/martian
Pull request #65 (2019-05-07 12:44:36)

Extract keywordize-keys on params to interceptors
clojure.walk/keywordize-keys recursively clobbers map-like data
structures. This commit pulls out the call to keywordize-keys on the
params map into the `default-interceptors`. It retains the default
behaviour, but allows the user to optionally customize the behaviour.

Fix: #62


Patch:
diff --git a/core/src/martian/core.cljc b/core/src/martian/core.cljc
index 849767b..a9d84ba 100644
--- a/core/src/martian/core.cljc
+++ b/core/src/martian/core.cljc
@@ -9,7 +9,8 @@
             [schema.core :as s]))
 
 (def default-interceptors
-  [interceptors/set-method
+  [interceptors/keywordize-params
+   interceptors/set-method
    interceptors/set-url
    interceptors/set-query-params
    interceptors/set-body-params
@@ -63,8 +64,7 @@
   ([martian route-name] (request-for martian route-name {}))
   ([{:keys [handlers interceptors] :as martian} route-name params]
    (when-let [handler (find-handler handlers route-name)]
-     (let [params (keywordize-keys params)
-           ctx (tc/enqueue* {} (-> (or interceptors default-interceptors) vec (conj interceptors/request-only-handler)))]
+     (let [ctx (tc/enqueue* {} (-> (or interceptors default-interceptors) vec (conj interceptors/request-only-handler)))]
        (:request (tc/execute
                   (assoc ctx
                          :url-for (partial url-for martian)
@@ -76,8 +76,7 @@
   ([martian route-name] (response-for martian route-name {}))
   ([{:keys [handlers interceptors] :as martian} route-name params]
    (when-let [handler (find-handler handlers route-name)]
-     (let [params (keywordize-keys params)
-           ctx (tc/enqueue* {} (or interceptors default-interceptors))]
+     (let [ctx (tc/enqueue* {} (or interceptors default-interceptors))]
        (:response (tc/execute
                    (assoc ctx
                           :url-for (partial url-for martian)
diff --git a/core/src/martian/interceptors.cljc b/core/src/martian/interceptors.cljc
index 2fec187..a87e8f4 100644
--- a/core/src/martian/interceptors.cljc
+++ b/core/src/martian/interceptors.cljc
@@ -1,6 +1,6 @@
 (ns martian.interceptors
   (:require [martian.schema :as schema]
-            [clojure.walk :refer [stringify-keys]]
+            [clojure.walk :refer [keywordize-keys stringify-keys]]
             [clojure.string :as string]
             [tripod.context :as tc]
             [schema.core :as s]
@@ -37,6 +37,10 @@
 (defn coerce-data [{:keys [parameter-aliases] :as handler} schema-key params]
   (schema/coerce-data (get handler schema-key) params parameter-aliases))
 
+(def keywordize-params
+  {:name ::keywordize-params
+   :enter (fn [ctx] (update ctx :params keywordize-keys))})
+
 (def set-query-params
   {:name ::query-params
    :enter (fn [{:keys [params handler] :as ctx}]
diff --git a/core/src/martian/schema.cljc b/core/src/martian/schema.cljc
index 774dffb..f9d330f 100644
--- a/core/src/martian/schema.cljc
+++ b/core/src/martian/schema.cljc
@@ -1,6 +1,7 @@
 (ns martian.schema
   (:require #?(:clj [schema.core :as s]
                :cljs [schema.core :as s :refer [AnythingSchema Maybe EnumSchema EqSchema]])
+            #?(:cljs [goog.Uri])
             [schema.coerce :as sc]
             [clojure.walk :refer [postwalk-replace]])
   #?(:clj (:import [schema.core AnythingSchema Maybe EnumSchema EqSchema])))
@@ -89,11 +90,16 @@
            keyword
            definitions))
 
+(def URI
+  #?(:clj java.net.URI
+     :cljs goog.Uri))
+
 (defn- schema-type [definitions {:keys [type enum format $ref] :as param}]
   (cond
     enum (apply s/enum enum)
-    (= "string" type) (if (= "uuid" format)
-                        (s/cond-pre s/Str s/Uuid)
+    (= "string" type) (case format
+                        "uuid" (s/cond-pre s/Str s/Uuid)
+                        "uri" (s/cond-pre s/Str URI)
                         s/Str)
     (= "integer" type) s/Int
     (= "boolean" type) s/Bool


Test patch:
diff --git a/core/test/martian/core_test.cljc b/core/test/martian/core_test.cljc
index 37bd4ea..521d4ad 100644
--- a/core/test/martian/core_test.cljc
+++ b/core/test/martian/core_test.cljc
@@ -396,3 +396,21 @@
        (is (= "https://api.org/pets/" (.urlFor m "all-pets")))
        (is (= "https://api.org/pets/" (.urlFor m "create-pet")))
        (is (= "https://api.org/users/123/orders/456" (.urlFor m "order" {"user-id" 123 "order-id" 456}))))))
+
+(defrecord TestRecord [x y z])
+
+(deftest keywordize-keys-test
+  (let [swagger-definition {:paths {"/pets" {:post {:operationId "create-pet"
+                                                    :parameters [{:name "record" :in "body"}]}}}}
+        default (martian/bootstrap-swagger "https://api.org" swagger-definition)
+        without (martian/bootstrap-swagger "https://api.org" swagger-definition {:interceptors (rest martian/default-interceptors)})
+        record (-> (->TestRecord 1 2 3) (assoc "thing" 4 :five 5))
+        params {:record record}
+        actual-default (martian/request-for default :create-pet params)
+        actual-without (martian/request-for without :create-pet params)]
+    (is (= actual-default
+           {:method :post, :url "https://api.org/pets", :body {:x 1, :y 2, :z 3, :thing 4, :five 5}}))
+    (is (not (instance? TestRecord (:body actual-default))))
+    (is (= actual-without
+           {:method :post, :url "https://api.org/pets", :body record}))
+    (is (instance? TestRecord (:body actual-without)))))
diff --git a/core/test/martian/schema_test.cljc b/core/test/martian/schema_test.cljc
index 62eaaed..dd81d41 100644
--- a/core/test/martian/schema_test.cljc
+++ b/core/test/martian/schema_test.cljc
@@ -42,6 +42,14 @@
                                  :type "string"
                                  :format "uuid"}))))
 
+(deftest uri-test
+  (is (= (s/cond-pre s/Str schema/URI)
+         (schema/make-schema {} {:name "uri"
+                                 :in "path"
+                                 :required true
+                                 :type "string"
+                                 :format "uri"}))))
+
 (deftest arrays-test
   (is (= [s/Str]
          (schema/make-schema {} {:name "tags"