From 05fbaa11105e2cb33ab704857fb87c04cd7542c0 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 7 May 2018 08:59:21 +0300 Subject: [PATCH] Initial swagger-ui integration --- examples/ring-swagger/project.clj | 1 - examples/ring-swagger/src/example/server.clj | 28 +++++++------ modules/reitit-ring/src/reitit/ring.cljc | 11 +++-- modules/reitit-swagger/project.clj | 4 +- .../reitit-swagger/src/reitit/swagger.cljc | 42 ++++++++++++++++++- project.clj | 7 +++- 6 files changed, 71 insertions(+), 22 deletions(-) diff --git a/examples/ring-swagger/project.clj b/examples/ring-swagger/project.clj index a47a801f..f3133c0c 100644 --- a/examples/ring-swagger/project.clj +++ b/examples/ring-swagger/project.clj @@ -3,6 +3,5 @@ :dependencies [[org.clojure/clojure "1.9.0"] [ring "1.6.3"] [metosin/muuntaja "0.5.0"] - [org.webjars/swagger-ui "3.13.6"] [metosin/reitit "0.1.1-SNAPSHOT"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-swagger/src/example/server.clj b/examples/ring-swagger/src/example/server.clj index 0dbef160..96ab9fd4 100644 --- a/examples/ring-swagger/src/example/server.clj +++ b/examples/ring-swagger/src/example/server.clj @@ -21,26 +21,29 @@ :swagger {:info {:title "my-api"}} :handler (swagger/create-swagger-handler)}}] - ["/spec" {:coercion spec/coercion} + ["/spec" + {:coercion spec/coercion + :swagger {:tags ["spec"]}} + ["/plus" - {:get {:summary "plus" + {:get {:summary "plus with spec" :parameters {:query {:x int?, :y int?}} :responses {200 {:body {:total int?}}} :handler (fn [{{{:keys [x y]} :query} :parameters}] - {:status 200, :body {:total (+ x y)}})}}]] + {:status 200 + :body {:total (+ x y)}})}}]] + + ["/schema" + {:coercion schema/coercion + :swagger {:tags ["schema"]}} - ["/schema" {:coercion schema/coercion} ["/plus" - {:get {:summary "plus" + {:get {:summary "plus with schema" :parameters {:query {:x Int, :y Int}} :responses {200 {:body {:total Int}}} :handler (fn [{{{:keys [x y]} :query} :parameters}] - {:status 200, :body {:total (+ x y)}})}}]]] - - ["/api-docs/*" - {:no-doc true - :handler (ring/create-resource-handler - {:root "META-INF/resources/webjars/swagger-ui/3.13.6"})}]] + {:status 200 + :body {:total (+ x y)}})}}]]]] {:data {:middleware [ring.middleware.params/wrap-params muuntaja.middleware/wrap-format @@ -49,7 +52,8 @@ rrc/coerce-request-middleware rrc/coerce-response-middleware]}}) (ring/routes - (ring/create-resource-handler {:path "/"}) + (swagger/create-swagger-ui-handler + {:path "", :url "/api/swagger.json"}) (ring/create-default-handler)))) (defn start [] diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 2fd09a98..9d5f830e 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -69,6 +69,8 @@ #?(:clj ;; TODO: optimize for perf + ;; TODO: ring.middleware.not-modified/wrap-not-modified + ;; TODO: ring.middleware.head/wrap-head (defn create-resource-handler "A ring handler for serving classpath resources, configured via options: @@ -82,19 +84,20 @@ | :allow-symlinks? | allow symlinks that lead to paths outside the root classpath directories, defaults to `false`" ([] (create-resource-handler nil)) - ([{:keys [parameter root path loader allow-symlinks? index-files] + ([{:keys [parameter root path loader allow-symlinks? index-files paths] :or {parameter (keyword "") root "public" - index-files ["index.html"]}}] + index-files ["index.html"] + paths (constantly nil)}}] (let [options {:root root, :loader loader, :allow-symlinks? allow-symlinks?} - path-size (count path) + path-size (inc (count path)) create (fn [handler] (fn ([request] (handler request)) ([request respond _] (respond (handler request))))) resource-response (fn [path accept] (if-let [path (accept path)] - (if-let [response (response/resource-response path options)] + (if-let [response (or (paths path) (response/resource-response path options))] (response/content-type response (mime-type/ext-mime-type path))))) path-or-index-response (fn [path accept] (or (resource-response path accept) diff --git a/modules/reitit-swagger/project.clj b/modules/reitit-swagger/project.clj index 384e6c5f..34c5613a 100644 --- a/modules/reitit-swagger/project.clj +++ b/modules/reitit-swagger/project.clj @@ -6,4 +6,6 @@ :plugins [[lein-parent "0.3.2"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} - :dependencies [[metosin/reitit-core]]) + :dependencies [[metosin/reitit-ring] + [metosin/jsonista] + [metosin/ring-swagger-ui]]) diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 0a296dac..41d2a33f 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -3,7 +3,11 @@ [meta-merge.core :refer [meta-merge]] [clojure.spec.alpha :as s] [clojure.set :as set] - [reitit.coercion :as coercion])) + [clojure.string :as str] + [reitit.ring :as ring] + [reitit.coercion :as coercion] + #?@(:clj [ + [jsonista.core :as j]]))) (s/def ::id keyword?) (s/def ::no-doc boolean?) @@ -69,7 +73,8 @@ "Create a ring handler to emit swagger spec." (fn [{:keys [::r/router ::r/match :request-method]}] (let [{:keys [id] :as swagger} (-> match :result request-method :data :swagger) - swagger (set/rename-keys swagger {:id :x-id}) + swagger (->> (set/rename-keys swagger {:id :x-id}) + (merge {:swagger "2.0"})) accept-route #(-> % second :swagger :id (= id)) transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data}]] (if (and data (not no-doc)) @@ -86,3 +91,36 @@ (let [paths (->> router (r/routes) (filter accept-route) (map transform-path) (into {}))] {:status 200 :body (meta-merge swagger {:paths paths})}))))) + +#?(:clj + (defn create-swagger-ui-handler + "Creates a ring handler which can be used to serve swagger-ui. + + | key | description | + | -----------------|-------------| + | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` + | :root | optional resource root, defaults to `\"swagger-ui\"` + | :url | path to swagger endpoint, defaults to `/swagger.json` + | :path | optional path to mount the handler to. Works only if mounted outside of a router. + | :config | parameters passed to swaggger-ui, keys transformed into camelCase." + ([] + (create-swagger-ui-handler nil)) + ([options] + (let [mixed-case (fn [k] + (let [[f & rest] (str/split (name k) #"-")] + (apply str (str/lower-case f) (map str/capitalize rest)))) + mixed-case-key (fn [[k v]] [(mixed-case k) v]) + config-json (fn [{:keys [url config]}] (j/write-value-as-string (merge config {:url url}))) + conf-js (fn [opts] (str "window.API_CONF = " (config-json opts) ";")) + options (as-> options $ + (update $ :root (fnil identity "swagger-ui")) + (update $ :url (fnil identity "/swagger.json")) + (update $ :config #(->> % (map mixed-case-key) (into {}))) + (assoc $ :paths {"conf.js" {:headers {"Content-Type" "application/javascript"} + :status 200 + :body (conf-js $)} + "config.json" {:headers {"Content-Type" "application/json"} + :status 200 + :body (config-json $)}}))] + (ring/routes + (ring/create-resource-handler options)))))) diff --git a/project.clj b/project.clj index 257c8415..478410a3 100644 --- a/project.clj +++ b/project.clj @@ -19,7 +19,9 @@ [meta-merge "1.0.0"] [ring/ring-core "1.6.3"] [metosin/spec-tools "0.6.2-SNAPSHOT"] - [metosin/schema-tools "0.10.2-SNAPSHOT"]] + [metosin/schema-tools "0.10.2-SNAPSHOT"] + [metosin/ring-swagger-ui "2.2.10"] + [metosin/jsonista "0.2.0"]] :plugins [[jonase/eastwood "0.2.5"] [lein-doo "0.1.10"] @@ -50,7 +52,8 @@ [ring "1.6.3"] [metosin/muuntaja "0.5.0"] - [metosin/jsonista "0.1.1"] + [metosin/jsonista "0.2.0"] + [metosin/ring-swagger-ui "2.2.10"] [criterium "0.4.4"] [org.clojure/test.check "0.9.0"]