diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 1679a061..847da761 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -3,6 +3,7 @@ [meta-merge.core :refer [meta-merge]] [clojure.spec.alpha :as s] [clojure.set :as set] + [clojure.string :as string] [reitit.coercion :as coercion])) (s/def ::id (s/or :keyword keyword? :set (s/coll-of keyword? :into #{}))) @@ -62,6 +63,21 @@ {:name ::swagger :spec ::spec}) +(defn- path->template [path endpoint] + (let [path-parameters (filter (fn [{:keys [in]}] + (= in "path")) + (mapcat (fn [[_ {:keys [parameters]}]] + parameters) + endpoint))] + (loop [{:keys [name] :as path-parameter} (first path-parameters) + path-parameters (rest path-parameters) + path path] + (if path-parameter + (recur (first path-parameters) + (rest path-parameters) + (string/replace path (re-pattern (str ":" name)) (str "{" name "}"))) + path)))) + (defn create-swagger-handler [] "Create a ring handler to emit swagger spec. Collects all routes from router which have an intersecting `[:swagger :id]` and which are not marked with `:no-doc` route data." @@ -83,7 +99,7 @@ (dissoc swagger :id))])) transform-path (fn [[p _ c]] (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] - [p endpoint]))] + [(path->template p endpoint) endpoint]))] (if id (let [paths (->> router (r/routes) (filter accept-route) (map transform-path) (into {}))] {:status 200 diff --git a/test/cljc/reitit/swagger_test.clj b/test/cljc/reitit/swagger_test.clj index 6745be01..3437df4f 100644 --- a/test/cljc/reitit/swagger_test.clj +++ b/test/cljc/reitit/swagger_test.clj @@ -19,20 +19,24 @@ :handler (swagger/create-swagger-handler)}}] ["/spec" {:coercion spec/coercion} - ["/plus" + ["/plus/:z" {:get {:summary "plus" - :parameters {:query {:x int?, :y int?}} + :parameters {:query {:x int?, :y int?} + :path {:z int?}} :responses {200 {:body {:total int?}}} - :handler (fn [{{{:keys [x y]} :query} :parameters}] - {:status 200, :body {:total (+ x y)}})}}]] + :handler (fn [{{{:keys [x y]} :query + {:keys [z]} :path} :parameters}] + {:status 200, :body {:total (+ x y z)}})}}]] ["/schema" {:coercion schema/coercion} - ["/plus" + ["/plus/:z" {:get {:summary "plus" - :parameters {:query {:x Int, :y Int}} + :parameters {:query {:x Int, :y Int} + :path {:z Int}} :responses {200 {:body {:total Int}}} - :handler (fn [{{{:keys [x y]} :query} :parameters}] - {:status 200, :body {:total (+ x y)}})}}]]] + :handler (fn [{{{:keys [x y]} :query + {:keys [z]} :path} :parameters}] + {:status 200, :body {:total (+ x y z)}})}}]]] {:data {:middleware [swagger/swagger-feature rrc/coerce-exceptions-middleware @@ -42,16 +46,16 @@ (deftest swagger-test (testing "endpoints work" (testing "spec" - (is (= {:body {:total 3}, :status 200} + (is (= {:body {:total 6}, :status 200} (app {:request-method :get - :uri "/api/spec/plus" + :uri "/api/spec/plus/3" :query-params {:x "2", :y "1"}})))) (testing "schema" - (is (= {:body {:total 3}, :status 200} + (is (= {:body {:total 6}, :status 200} (app {:request-method :get - :uri "/api/schema/plus" + :uri "/api/schema/plus/3" :query-params {:x "2", :y "1"}}))))) (testing "swagger-spec" (let [spec (:body (app @@ -60,43 +64,55 @@ (is (= {:x-id #{::math} :swagger "2.0" :info {:title "my-api"} - :paths {"/api/schema/plus" {:get {:parameters [{:description "" - :format "int32" - :in "query" - :name "x" - :required true - :type "integer"} - {:description "" - :format "int32" - :in "query" - :name "y" - :required true - :type "integer"}] - :responses {200 {:description "" - :schema {:additionalProperties false - :properties {"total" {:format "int32" - :type "integer"}} - :required ["total"] - :type "object"}}} - :summary "plus"}} - "/api/spec/plus" {:get {:parameters [{:description "" - :format "int64" - :in "query" - :name "x" - :required true - :type "integer"} - {:description "" - :format "int64" - :in "query" - :name "y" - :required true - :type "integer"}] - :responses {200 {:description "" - :schema {:properties {"total" {:format "int64" - :type "integer"}} - :required ["total"] - :type "object"}}} - :summary "plus"}}}} + :paths {"/api/schema/plus/{z}" {:get {:parameters [{:description "" + :format "int32" + :in "query" + :name "x" + :required true + :type "integer"} + {:description "" + :format "int32" + :in "query" + :name "y" + :required true + :type "integer"} + {:in "path" + :name "z" + :description "" + :type "integer" + :required true + :format "int32"}] + :responses {200 {:description "" + :schema {:additionalProperties false + :properties {"total" {:format "int32" + :type "integer"}} + :required ["total"] + :type "object"}}} + :summary "plus"}} + "/api/spec/plus/{z}" {:get {:parameters [{:description "" + :format "int64" + :in "query" + :name "x" + :required true + :type "integer"} + {:description "" + :format "int64" + :in "query" + :name "y" + :required true + :type "integer"} + {:in "path" + :name "z" + :description "" + :type "integer" + :required true + :format "int64"}] + :responses {200 {:description "" + :schema {:properties {"total" {:format "int64" + :type "integer"}} + :required ["total"] + :type "object"}}} + :summary "plus"}}}} spec))))) (deftest multiple-swagger-apis-test