Initial swagger-ui integration

This commit is contained in:
Tommi Reiman 2018-05-07 08:59:21 +03:00
parent 0b711b2560
commit 05fbaa1110
6 changed files with 71 additions and 22 deletions

View file

@ -3,6 +3,5 @@
:dependencies [[org.clojure/clojure "1.9.0"] :dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"] [ring "1.6.3"]
[metosin/muuntaja "0.5.0"] [metosin/muuntaja "0.5.0"]
[org.webjars/swagger-ui "3.13.6"]
[metosin/reitit "0.1.1-SNAPSHOT"]] [metosin/reitit "0.1.1-SNAPSHOT"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -21,26 +21,29 @@
:swagger {:info {:title "my-api"}} :swagger {:info {:title "my-api"}}
:handler (swagger/create-swagger-handler)}}] :handler (swagger/create-swagger-handler)}}]
["/spec" {:coercion spec/coercion} ["/spec"
{:coercion spec/coercion
:swagger {:tags ["spec"]}}
["/plus" ["/plus"
{:get {:summary "plus" {:get {:summary "plus with spec"
:parameters {:query {:x int?, :y int?}} :parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total int?}}} :responses {200 {:body {:total int?}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}] :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" ["/plus"
{:get {:summary "plus" {:get {:summary "plus with schema"
:parameters {:query {:x Int, :y Int}} :parameters {:query {:x Int, :y Int}}
:responses {200 {:body {:total Int}}} :responses {200 {:body {:total Int}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}] :handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200, :body {:total (+ x y)}})}}]]] {: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"})}]]
{:data {:middleware [ring.middleware.params/wrap-params {:data {:middleware [ring.middleware.params/wrap-params
muuntaja.middleware/wrap-format muuntaja.middleware/wrap-format
@ -49,7 +52,8 @@
rrc/coerce-request-middleware rrc/coerce-request-middleware
rrc/coerce-response-middleware]}}) rrc/coerce-response-middleware]}})
(ring/routes (ring/routes
(ring/create-resource-handler {:path "/"}) (swagger/create-swagger-ui-handler
{:path "", :url "/api/swagger.json"})
(ring/create-default-handler)))) (ring/create-default-handler))))
(defn start [] (defn start []

View file

@ -69,6 +69,8 @@
#?(:clj #?(:clj
;; TODO: optimize for perf ;; TODO: optimize for perf
;; TODO: ring.middleware.not-modified/wrap-not-modified
;; TODO: ring.middleware.head/wrap-head
(defn create-resource-handler (defn create-resource-handler
"A ring handler for serving classpath resources, configured via options: "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`" | :allow-symlinks? | allow symlinks that lead to paths outside the root classpath directories, defaults to `false`"
([] ([]
(create-resource-handler nil)) (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 "") :or {parameter (keyword "")
root "public" root "public"
index-files ["index.html"]}}] index-files ["index.html"]
paths (constantly nil)}}]
(let [options {:root root, :loader loader, :allow-symlinks? allow-symlinks?} (let [options {:root root, :loader loader, :allow-symlinks? allow-symlinks?}
path-size (count path) path-size (inc (count path))
create (fn [handler] create (fn [handler]
(fn (fn
([request] (handler request)) ([request] (handler request))
([request respond _] (respond (handler request))))) ([request respond _] (respond (handler request)))))
resource-response (fn [path accept] resource-response (fn [path accept]
(if-let [path (accept path)] (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))))) (response/content-type response (mime-type/ext-mime-type path)))))
path-or-index-response (fn [path accept] path-or-index-response (fn [path accept]
(or (resource-response path accept) (or (resource-response path accept)

View file

@ -6,4 +6,6 @@
:plugins [[lein-parent "0.3.2"]] :plugins [[lein-parent "0.3.2"]]
:parent-project {:path "../../project.clj" :parent-project {:path "../../project.clj"
:inherit [:deploy-repositories :managed-dependencies]} :inherit [:deploy-repositories :managed-dependencies]}
:dependencies [[metosin/reitit-core]]) :dependencies [[metosin/reitit-ring]
[metosin/jsonista]
[metosin/ring-swagger-ui]])

View file

@ -3,7 +3,11 @@
[meta-merge.core :refer [meta-merge]] [meta-merge.core :refer [meta-merge]]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[clojure.set :as set] [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 ::id keyword?)
(s/def ::no-doc boolean?) (s/def ::no-doc boolean?)
@ -69,7 +73,8 @@
"Create a ring handler to emit swagger spec." "Create a ring handler to emit swagger spec."
(fn [{:keys [::r/router ::r/match :request-method]}] (fn [{:keys [::r/router ::r/match :request-method]}]
(let [{:keys [id] :as swagger} (-> match :result request-method :data :swagger) (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)) accept-route #(-> % second :swagger :id (= id))
transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data}]] transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data}]]
(if (and data (not no-doc)) (if (and data (not no-doc))
@ -86,3 +91,36 @@
(let [paths (->> router (r/routes) (filter accept-route) (map transform-path) (into {}))] (let [paths (->> router (r/routes) (filter accept-route) (map transform-path) (into {}))]
{:status 200 {:status 200
:body (meta-merge swagger {:paths paths})}))))) :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))))))

View file

@ -19,7 +19,9 @@
[meta-merge "1.0.0"] [meta-merge "1.0.0"]
[ring/ring-core "1.6.3"] [ring/ring-core "1.6.3"]
[metosin/spec-tools "0.6.2-SNAPSHOT"] [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"] :plugins [[jonase/eastwood "0.2.5"]
[lein-doo "0.1.10"] [lein-doo "0.1.10"]
@ -50,7 +52,8 @@
[ring "1.6.3"] [ring "1.6.3"]
[metosin/muuntaja "0.5.0"] [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"] [criterium "0.4.4"]
[org.clojure/test.check "0.9.0"] [org.clojure/test.check "0.9.0"]