diff --git a/src/malli/json_schema.cljc b/src/malli/json_schema.cljc index ceaed24cd..75042d578 100644 --- a/src/malli/json_schema.cljc +++ b/src/malli/json_schema.cljc @@ -1,5 +1,6 @@ (ns malli.json-schema (:require [clojure.set :as set] + [clojure.string :as str] [malli.core :as m])) (declare -transform) @@ -8,20 +9,16 @@ (-accept [this children options] "transforms schema to JSON Schema")) (defn -ref [schema {::keys [transform definitions] :as options}] - (let [x (m/-ref schema)] - (when-not (contains? @definitions x) + (let [ref (as-> (m/-ref schema) $ + (cond (var? $) (str (.toSymbol $)) + (qualified-ident? $) (str (namespace $) "/" (name $)) + :else (str $)))] + (when-not (contains? @definitions ref) (let [child (m/deref schema)] - (swap! definitions assoc x ::recursion-stopper) - (swap! definitions assoc x (transform child options)))) - {:$ref (apply str "#/definitions/" - (cond - ;; / must be encoded as ~1 in JSON Schema - ;; https://json-schema.org/draft/2019-09/relative-json-pointer.html - ;; https://www.rfc-editor.org/rfc/rfc6901 - (qualified-keyword? x) [(namespace x) "~1" - (name x)] - (keyword? x) [(name x)] - :else [x]))})) + (swap! definitions assoc ref ::recursion-stopper) + (swap! definitions assoc ref (transform child options)))) + ;; '/' must be encoded as '~1' in JSON Schema - https://www.rfc-editor.org/rfc/rfc6901 + {:$ref (apply str "#/definitions/" (str/replace ref #"/" "~1"))})) (defn -schema [schema {::keys [transform] :as options}] (if (m/-ref schema) diff --git a/test/malli/experimental/time/json_schema_test.cljc b/test/malli/experimental/time/json_schema_test.cljc index 7933b964f..88af699e3 100644 --- a/test/malli/experimental/time/json_schema_test.cljc +++ b/test/malli/experimental/time/json_schema_test.cljc @@ -7,17 +7,15 @@ (t/deftest time-formats (t/is (= {:type "object", - :properties - {:date {:$ref "#/definitions/time~1local-date"}, - :time {:$ref "#/definitions/time~1offset-time"}, - :date-time {:$ref "#/definitions/time~1offset-date-time"}, - :duration {:$ref "#/definitions/time~1duration"}}, + :properties {:date {:$ref "#/definitions/time~1local-date"}, + :time {:$ref "#/definitions/time~1offset-time"}, + :date-time {:$ref "#/definitions/time~1offset-date-time"}, + :duration {:$ref "#/definitions/time~1duration"}}, :required [:date :time :date-time :duration], - :definitions - #:time{:local-date {:type "string", :format "date"}, - :offset-time {:type "string", :format "time"}, - :offset-date-time {:type "string", :format "date-time"}, - :duration {:type "string", :format "duration"}}} + :definitions {"time/local-date" {:type "string", :format "date"}, + "time/offset-time" {:type "string", :format "time"}, + "time/offset-date-time" {:type "string", :format "date-time"}, + "time/duration" {:type "string", :format "duration"}}} (json/transform [:map [:date :time/local-date] diff --git a/test/malli/swagger_test.cljc b/test/malli/swagger_test.cljc index 6d8312b55..d0513fb76 100644 --- a/test/malli/swagger_test.cljc +++ b/test/malli/swagger_test.cljc @@ -313,7 +313,7 @@ (testing "generates swagger for ::parameters w/ basic schema + registry" (let [registry (merge (m/type-schemas) {::body [:string {:min 1}]})] - (is (= {:definitions {::body {:minLength 1, :type "string"}} + (is (= {:definitions {"malli.swagger-test/body" {:minLength 1, :type "string"}} :parameters [{:description "" :in "body" :name "body" @@ -327,9 +327,9 @@ (let [registry (merge (m/base-schemas) (m/type-schemas) {::success [:map-of :keyword :string] ::error [:string {:min 1}]})] - (is (= {:definitions {::error {:minLength 1, :type "string"} - ::success {:additionalProperties {:type "string"} - :type "object"}} + (is (= {:definitions {"malli.swagger-test/error" {:minLength 1, :type "string"} + "malli.swagger-test/success" {:additionalProperties {:type "string"} + :type "object"}} :responses {200 {:description "" :schema {:$ref "#/definitions/malli.swagger-test~1success"}} 400 {:description "" @@ -345,11 +345,11 @@ {::req-body [:map-of :keyword :any] ::success-resp [:map [:it [:= "worked"]]] ::error-resp [:string {:min 1}]})] - (is (= {:definitions {::error-resp {:minLength 1, :type "string"} - ::req-body {:additionalProperties {}, :type "object"} - ::success-resp {:properties {:it {:const "worked"}} - :required [:it] - :type "object"}} + (is (= {:definitions {"malli.swagger-test/error-resp" {:minLength 1, :type "string"} + "malli.swagger-test/req-body" {:additionalProperties {}, :type "object"} + "malli.swagger-test/success-resp" {:properties {:it {:const "worked"}} + :required [:it] + :type "object"}} :parameters [{:description "" :in "body" :name "body" @@ -391,21 +391,21 @@ ::req-body [:map [:a ::a]] ::success-resp [:map-of :keyword :string] ::error-resp :string})] - (is (= {:definitions {::a {:type "string" - :x-anyOf [{:type "string"} - {:$ref "#/definitions/malli.swagger-test~1b"}]} - ::b {:type "string" - :x-anyOf [{:type "string"} - {:$ref "#/definitions/malli.swagger-test~1c"}]} - ::c {:type "string" - :x-anyOf [{:type "string"} - {:$ref "#/definitions/malli.swagger-test~1a"}]} - ::error-resp {:type "string"} - ::req-body {:properties {:a {:$ref "#/definitions/malli.swagger-test~1a"}} - :required [:a] - :type "object"} - ::success-resp {:additionalProperties {:type "string"} - :type "object"}} + (is (= {:definitions {"malli.swagger-test/a" {:type "string" + :x-anyOf [{:type "string"} + {:$ref "#/definitions/malli.swagger-test~1b"}]} + "malli.swagger-test/b" {:type "string" + :x-anyOf [{:type "string"} + {:$ref "#/definitions/malli.swagger-test~1c"}]} + "malli.swagger-test/c" {:type "string" + :x-anyOf [{:type "string"} + {:$ref "#/definitions/malli.swagger-test~1a"}]} + "malli.swagger-test/error-resp" {:type "string"} + "malli.swagger-test/req-body" {:properties {:a {:$ref "#/definitions/malli.swagger-test~1a"}} + :required [:a] + :type "object"} + "malli.swagger-test/success-resp" {:additionalProperties {:type "string"} + :type "object"}} :parameters [{:description "" :in "body" :name "body"