Merge pull request #113 from metosin/infer-swagger-id

Infer swagger
This commit is contained in:
Tommi Reiman 2018-07-21 09:47:17 +03:00 committed by GitHub
commit 73f0f355eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 22 deletions

View file

@ -6,6 +6,25 @@
* should only concern you if you are not using [Muuntaja](https://github.com/metosin/muuntaja).
* the `r/routes` returns just the path + data tuples as documented, not the compiled route results. To get the compiled results, use `r/compiled-routes` instead.
## `reitit-swagger`
* In case of just one swagger api per router, the swagger api doesn't have to identified, so this works now:
```clj
(require '[reitit.ring :as ring])
(require '[reitit.swagger :as swagger])
(require '[reitit.swagger-ui :as swagger-ui])
(ring/ring-handler
(ring/router
[["/ping"
{:get (fn [_] {:status 200, :body "pong"})}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]])
(swagger-ui/create-swagger-ui-handler {:path "/"}))
```
## `reitit-swagger-ui`
* **BREAKING**: pass swagger-ui `:config` as-is (instead of mixed-casing keys) to swagger-ui, fixes [#109](https://github.com/metosin/reitit/issues/109):

View file

@ -18,7 +18,7 @@ The following route data keys contribute to the generated swagger specification:
| key | description |
| --------------|-------------|
| :swagger | map of any swagger-data. Must have `:id` (keyword or sequence of keywords) to identify the api
| :swagger | map of any swagger-data. Can have `:id` (keyword or sequence of keywords) to identify the api
| :no-doc | optional boolean to exclude endpoint from api docs
| :tags | optional set of strings of keywords tags for an endpoint api docs
| :summary | optional short string summary of an endpoint
@ -67,7 +67,7 @@ Webjars also hosts a [version](https://github.com/webjars/swagger-ui) of the swa
### Simple example
* two routes in a single swagger-api `::api`
* two routes
* swagger-spec served from `"/swagger.json"`
* swagger-ui mounted to `"/"`
@ -84,8 +84,7 @@ Webjars also hosts a [version](https://github.com/webjars/swagger-ui) of the swa
["/pong" {:post (constantly "pong")}]]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:swagger {:id ::api}}}) ;; for all routes
:handler (swagger/create-swagger-handler)}}]])
(swagger-ui/create-swagger-ui-handler {:path "/"})))
```
@ -95,7 +94,7 @@ The generated swagger spec:
(app {:request-method :get :uri "/swagger.json"})
;{:status 200
; :body {:swagger "2.0"
; :x-id #{:user/api}
; :x-id #{:reitit.swagger/default}
; :paths {"/api/ping" {:get {}}
; "/api/pong" {:post {}}}}}
```
@ -138,8 +137,7 @@ Whole example project is in [`/examples/ring-swagger`](https://github.com/metosi
(ring/ring-handler
(ring/router
["/api"
{:swagger {:id ::math}}
["/swagger.json"
{:get {:no-doc true
:swagger {:info {:title "my-api"}}
@ -195,9 +193,9 @@ http://localhost:3000 should render now the swagger-ui:
![Swagger-ui](../images/swagger.png)
## Advanced
## Multiple swagger apis
Route data in path `[:swagger :id]` can be either a keyword or a sequence of keywords. This enables one route to be part of multiple swagger apis. Normal route data [scoping rules](../basics/route_data.md#nested-route-data) rules apply.
There can be multiple swagger apis within a router. Each route can be part of 0..n swagger apis. Swagger apis are identified by value in route data under key path `[:swagger :id]`. It can be either a keyword or a sequence of keywords. Normal route data [scoping rules](../basics/route_data.md#nested-route-data) rules apply.
Example with:

View file

@ -15,7 +15,6 @@
(ring/ring-handler
(ring/router
["/api"
{:swagger {:id ::math}}
["/swagger.json"
{:get {:no-doc true

View file

@ -74,13 +74,13 @@
"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."
(fn [{:keys [::r/router ::r/match :request-method]}]
(let [{:keys [id] :as swagger} (-> match :result request-method :data :swagger)
(let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger)
->set (fn [x] (if (or (set? x) (sequential? x)) (set x) (conj #{} x)))
ids (->set id)
swagger (->> (dissoc swagger :id)
(merge {:swagger "2.0"
:x-id ids}))
accept-route #(-> % second :swagger :id ->set (set/intersection ids) seq)
accept-route #(-> % second :swagger :id (or ::default) ->set (set/intersection ids) seq)
transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data}]]
(if (and data (not no-doc))
[method
@ -92,7 +92,6 @@
transform-path (fn [[p _ c]]
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
[(path->template p) endpoint]))]
(if id
(let [paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) (into {}))]
{:status 200
:body (meta-merge swagger {:paths paths})})))))
(let [paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) (into {}))]
{:status 200
:body (meta-merge swagger {:paths paths})}))))

View file

@ -129,6 +129,9 @@
:summary "plus"}}}}
spec)))))
(defn spec-paths [app uri]
(-> {:request-method :get, :uri uri} app :body :paths keys))
(deftest multiple-swagger-apis-test
(let [ping-route ["/ping" {:get (constantly "ping")}]
spec-route ["/swagger.json"
@ -149,15 +152,13 @@
["/deep" {:swagger {:id ::one}}
ping-route]]
["/one-two" {:swagger {:id #{::one ::two}}}
spec-route]]))
spec-paths (fn [uri]
(-> {:request-method :get, :uri uri} app :body :paths keys))]
spec-route]]))]
(is (= ["/common/ping" "/one/ping" "/two/deep/ping"]
(spec-paths "/one/swagger.json")))
(spec-paths app "/one/swagger.json")))
(is (= ["/common/ping" "/two/ping"]
(spec-paths "/two/swagger.json")))
(spec-paths app "/two/swagger.json")))
(is (= ["/common/ping" "/one/ping" "/two/ping" "/two/deep/ping"]
(spec-paths "/one-two/swagger.json")))))
(spec-paths app "/one-two/swagger.json")))))
(deftest swagger-ui-congif-test
(let [app (swagger-ui/create-swagger-ui-handler
@ -168,3 +169,16 @@
(is (= {:jsonEditor true, :url "/swagger.json"}
(->> {:request-method :get, :uri "/config.json"}
(app) :body (m/decode m/instance "application/json"))))))
(deftest without-swagger-id-test
(let [app (ring/ring-handler
(ring/router
[["/ping"
{:get (constantly "ping")}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))]
(is (= ["/ping"] (spec-paths app "/swagger.json")))
(is (= #{::swagger/default}
(-> {:request-method :get :uri "/swagger.json"}
(app) :body :x-id)))))