From b87b6ae0072a689f1862fe7b72fe5b2750a621fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 14:59:39 +0300 Subject: [PATCH 01/34] Adding required external dependencies. --- deps.edn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index 0bfe8185..37cf50d3 100644 --- a/deps.edn +++ b/deps.edn @@ -22,7 +22,8 @@ org.postgresql/postgresql {:mvn/version "42.3.3"}, piotr-yuxuan/closeable-map {:mvn/version "0.35.0"}, seancorfield/next.jdbc {:mvn/version "1.2.659"}, - yogthos/config {:mvn/version "1.2.0"}} + yogthos/config {:mvn/version "1.2.0"} + ring {:mvn/version "1.9.6"}} :aliases {:dev From 5ef25d7c9ac4f54d9bbbf8021f204db8d61f1049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 15:00:49 +0300 Subject: [PATCH 02/34] Implementing the helpers of Swagger Data generation. --- src/xiana/route/helpers.clj | 83 ++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/src/xiana/route/helpers.clj b/src/xiana/route/helpers.clj index d106e511..8ea2dab0 100644 --- a/src/xiana/route/helpers.clj +++ b/src/xiana/route/helpers.clj @@ -1,5 +1,13 @@ (ns xiana.route.helpers - "The default not found and action functions") + "The default not found and action functions" + (:require + [reitit.trie :as trie] + [clojure.set :as set] + [clojure.string :as str] + [reitit.coercion :as rcoercion] + [reitit.core :as r] + [meta-merge.core :refer [meta-merge]] + [reitit.ring :as ring])) (defn not-found "Default not-found response handler helper." @@ -16,3 +24,76 @@ (assoc :error e) (assoc :response {:status 500 :body "Internal Server error"}))))) + +(defonce all-methods + [:get :patch :trace :connect :delete :head :post :options :put]) + +(def routes->routes' + #(-> (fn *** [[url opt-map & nested-routes :as route]] + (let [new-opt-map (if (:action opt-map) + (let [action' (:action opt-map) + swagger-base-of-endpoint (:swagger-* opt-map)] + (reduce (fn [acc method] + (-> acc + (assoc-in [method :handler] identity) + (assoc-in [method :action] action') + (merge swagger-base-of-endpoint))) + opt-map + all-methods)) + (let [swagger-base-of-endpoint (get opt-map :swagger-* {})] + (reduce (fn [acc method] + (if (get acc method) + (if (get-in acc [method :handler]) + acc + (-> acc + (assoc-in [method :handler] identity) + (merge swagger-base-of-endpoint))) + acc)) + opt-map + all-methods)))] + (if (-> route meta :no-doc) + nil + (apply conj [url new-opt-map] + (map *** nested-routes))))) + (keep %) + vec)) + +(defn routes->swagger-data [routes'] + (let [request-method :get + routes' (-> routes' routes->routes' (ring/router {})) + {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) + ids (trie/into-set id) + strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) + strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) + swagger (->> (strip-endpoint-keys swagger) + (merge {:swagger "2.0" + :x-id ids})) + accept-route (fn [route] + (-> route second :swagger :id (or ::default) (trie/into-set) (set/intersection ids) seq)) + swagger-path (fn [path opts] + (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) + base-swagger-spec {:responses ^:displace {:default {:description ""}}} + transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data + middleware :middleware + interceptors :interceptors + :as args}]] + (if (and data (not no-doc)) + [method + (meta-merge + base-swagger-spec + ;;(apply meta-merge (keep (comp :swagger :data) middleware)) + ;;(apply meta-merge (keep (comp :swagger :data) interceptors)) + (if coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) + transform-path (fn [[p _ c]] + (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] + [(swagger-path p (r/options routes')) endpoint])) + map-in-order #(->> % (apply concat) (apply array-map)) + paths (->> routes' (r/compiled-routes) + ;; (filter accept-route) + ;;(map transform-endpoint) + (map transform-path) + map-in-order)] + (meta-merge swagger {:paths paths}))) From 63430a5e6461350f627ca56f3ea2c4354004f6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 15:01:11 +0300 Subject: [PATCH 03/34] Implementing the core functionality of swagger data generetion. --- src/xiana/swagger.clj | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/xiana/swagger.clj diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj new file mode 100644 index 00000000..8e34a69f --- /dev/null +++ b/src/xiana/swagger.clj @@ -0,0 +1,22 @@ +(ns xiana.swagger + (:require + [jsonista.core :as json] + [xiana.route.helpers :as helpers])) + +(defn routes->swagger-json [routes & {type :type}] + (-> routes + helpers/routes->routes' + helpers/routes->swagger-data + ((cond + (= type :json) json/write-value-as-string + (= type :edn) identity + :else identity)))) + +(defn ->swagger-data + "Update routes." + [config] + (let [routes-swagger-data (-> config + :routes + .routes + (routes->swagger-json :type json))] + (assoc config :swagger-data routes-swagger-data))) From b1f90be120ff79c2c2fe539ba22b73614db96a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 15:01:30 +0300 Subject: [PATCH 04/34] Writing base tests for swagger data generation. --- test/xiana/route_test.clj | 97 ++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/test/xiana/route_test.clj b/test/xiana/route_test.clj index b637f843..bda18548 100644 --- a/test/xiana/route_test.clj +++ b/test/xiana/route_test.clj @@ -1,9 +1,10 @@ (ns xiana.route-test (:require - [clojure.test :refer :all] - [xiana.route :as route] - [xiana.route.helpers :as helpers] - [xiana.state :as state])) + [clojure.test :refer :all] + [xiana.route :as route] + [xiana.route.helpers :as helpers] + [xiana.swagger :as xsw] + [xiana.state :as state])) (def sample-request {:uri "/" :request-method :get}) @@ -15,10 +16,18 @@ "Sample routes structure." {:routes [["/" {:action :action}]]}) +(def sample-routes-with-no-doc + "Sample routes structure with no-documentation meta flag." + {:routes [^{:no-doc true} ["/" {:action :action}]]}) + (def sample-routes-with-handler "Sample routes structure." {:routes [["/" {:handler :handler}]]}) +(def sample-routes-with-handler-and-no-doc + "Sample routes structure with no-documentation meta flag." + {:routes [^{:no-doc true} ["/" {:handler :handler}]]}) + (def sample-routes-without-action "Sample routes structure (without action or handler)." {:routes [["/" {}]]}) @@ -32,8 +41,8 @@ (deftest contains-updated-request-data ;; get state from sample request micro/match flow (let [state (route/match (state/make - (route/reset sample-routes) - sample-request)) + (route/reset sample-routes) + sample-request)) ;; expected request data expected {:method :get :match #{[:data {:action :action}] @@ -52,8 +61,8 @@ (deftest contains-not-found-action ;; get action from sample request micro/match flow (let [action (-> (state/make - (route/reset sample-routes) - sample-not-found-request) + (route/reset sample-routes) + sample-not-found-request) route/match :request-data :action) @@ -66,8 +75,8 @@ (deftest route-contains-default-action ;; get action from the updated state/match (micro) flow computation (let [action (-> (state/make - (route/reset sample-routes-with-handler) - sample-request) + (route/reset sample-routes-with-handler) + sample-request) route/match :request-data :action) @@ -80,8 +89,8 @@ (deftest handles-route-without-action-or-handler ;; get action from the updated state/match (micro) flow computation (let [action (-> (state/make - (route/reset sample-routes-without-action) - sample-request) + (route/reset sample-routes-without-action) + sample-request) route/match :request-data :action) @@ -89,3 +98,67 @@ expected helpers/not-found] ;; verify if action has the expected value (is (= action expected)))) + +(deftest swagger-route-data-generation + (testing "Swagger Data generation from Routes" + (testing "Swagger Data from Empty Route" + (let [generated (-> sample-routes-without-action + :routes + (xsw/routes->swagger-json :type :edn)) + count-of-generated-routes-data (-> generated + :paths + keys + count)] + (is generated) + (is (zero? count-of-generated-routes-data)))) + (testing "Swagger Data from Sampel Route /w handler" + (let [generated-swagger-data (-> sample-routes-with-handler + :routes + (xsw/routes->swagger-json :type :edn))] + (testing "One swagger route for one route entry?" + (let [generated-route-count (->> + generated-swagger-data + :paths + keys + count)] + (is (= generated-route-count + 1)))) + (testing "Actions should generate every methods" + (let [index-generated-methods-by-sample (->> + generated-swagger-data + :paths + (#(get % "/")) + keys + vec)] + (is (= + index-generated-methods-by-sample + helpers/all-methods)))))) + (testing "Swagger Data from Sample Route /w action" + (let [generated-swagger-data (-> sample-routes + :routes + (xsw/routes->swagger-json :type :edn))] + (testing "One swagger route for one route entry?" + (let [generated-route-count (->> + generated-swagger-data + :paths + keys + count)] + (is (= generated-route-count + 1)))) + (testing "Actions should generate every methods" + (let [index-generated-methods-by-sample (->> + generated-swagger-data + :paths + (#(get % "/")) + keys + vec)] + (is (= + index-generated-methods-by-sample + helpers/all-methods)))))))) + +(-> sample-routes-with-handler + :routes + (xsw/routes->swagger-json :type :edn)) + +(comment + (clojure.test/test-vars [#'swagger-route-data-generation])) From 575dc307e215358317cdbb6b57d8d6574b2c3658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 15:02:40 +0300 Subject: [PATCH 05/34] Clearing the test file from dev expressions. --- test/xiana/route_test.clj | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/xiana/route_test.clj b/test/xiana/route_test.clj index bda18548..dc5c978d 100644 --- a/test/xiana/route_test.clj +++ b/test/xiana/route_test.clj @@ -155,10 +155,3 @@ (is (= index-generated-methods-by-sample helpers/all-methods)))))))) - -(-> sample-routes-with-handler - :routes - (xsw/routes->swagger-json :type :edn)) - -(comment - (clojure.test/test-vars [#'swagger-route-data-generation])) From 763cea966be792c3d6055036529fee5e03897093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 16:07:59 +0300 Subject: [PATCH 06/34] Fix on dependency of ring. --- deps.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index 37cf50d3..82033578 100644 --- a/deps.edn +++ b/deps.edn @@ -23,7 +23,7 @@ piotr-yuxuan/closeable-map {:mvn/version "0.35.0"}, seancorfield/next.jdbc {:mvn/version "1.2.659"}, yogthos/config {:mvn/version "1.2.0"} - ring {:mvn/version "1.9.6"}} + ring/ring {:mvn/version "1.9.6"}} :aliases {:dev From 2eb6b860f84f700842b02296a4b299ea24c20974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Wed, 19 Oct 2022 16:08:25 +0300 Subject: [PATCH 07/34] Fix on json sym -> keyw --- src/xiana/swagger.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 8e34a69f..57f51f5c 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -18,5 +18,5 @@ (let [routes-swagger-data (-> config :routes .routes - (routes->swagger-json :type json))] + (routes->swagger-json :type :json))] (assoc config :swagger-data routes-swagger-data))) From 0a29b4a110eaa0e06f338fde65642df4a4bd52b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 20 Oct 2022 18:40:27 +0300 Subject: [PATCH 08/34] Add Swagger config keys. --- config/dev/config.edn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/dev/config.edn b/config/dev/config.edn index 738cd9c3..34c55b6e 100644 --- a/config/dev/config.edn +++ b/config/dev/config.edn @@ -6,6 +6,8 @@ :password "postgres"} :xiana/web-server {:port 3000 :join? false} + :xiana/swagger {:uri-path "swagger/swagger.json" :path :swagger.json} + :xiana/swagger-ui {:uri-path "swagger/swagger-ui"} :xiana/migration {:store :database :migration-dir "resources/migrations" :init-in-transaction? false From 3203ace15c08425872bbabb5e889284e25f860be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 20 Oct 2022 18:42:27 +0300 Subject: [PATCH 09/34] Update and re-factor Swagger implementation. --- src/xiana/swagger.clj | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 57f51f5c..55c5895f 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -12,11 +12,25 @@ (= type :edn) identity :else identity)))) +(defn swagger-configs-there? [config] + "Checks if the config has the required keys for swagger functionality. + Required keys: + * :xiana/swagger + * :xiana/swagger-ui" + (->> config + ((juxt :xiana/swagger-ui :xiana/swagger)) + (reduce #(and % %2) true) + boolean)) + (defn ->swagger-data - "Update routes." - [config] - (let [routes-swagger-data (-> config - :routes - .routes - (routes->swagger-json :type :json))] - (assoc config :swagger-data routes-swagger-data))) + "Update routes for swagger data generation." + [config & {routes :routes}] + (if (swagger-configs-there? config) + (let [routes (or routes (-> config :routes)) + routes-swagger-data (routes->swagger-json routes + :type :json) + config-key (-> config + (get :xiana/swagger {}) + (get :path :swagger.json))] + (assoc config config-key routes-swagger-data)) + config)) From 8ab933c1b3e259822fc01788fbd93d1dfd03f43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 20 Oct 2022 18:51:09 +0300 Subject: [PATCH 10/34] Fix styling with a new-line. --- config/dev/config.edn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/dev/config.edn b/config/dev/config.edn index 34c55b6e..1be409e9 100644 --- a/config/dev/config.edn +++ b/config/dev/config.edn @@ -6,7 +6,8 @@ :password "postgres"} :xiana/web-server {:port 3000 :join? false} - :xiana/swagger {:uri-path "swagger/swagger.json" :path :swagger.json} + :xiana/swagger {:uri-path "swagger/swagger.json" + :path :swagger.json} :xiana/swagger-ui {:uri-path "swagger/swagger-ui"} :xiana/migration {:store :database :migration-dir "resources/migrations" From bb5e6661e1dc1501b748c87548b1c0099649e9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Mon, 24 Oct 2022 15:32:11 +0300 Subject: [PATCH 11/34] Improve functionality with arbitrary compilation map of routes. --- src/xiana/route/helpers.clj | 8 ++++---- src/xiana/swagger.clj | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/xiana/route/helpers.clj b/src/xiana/route/helpers.clj index 8ea2dab0..b869753e 100644 --- a/src/xiana/route/helpers.clj +++ b/src/xiana/route/helpers.clj @@ -58,9 +58,9 @@ (keep %) vec)) -(defn routes->swagger-data [routes'] +(defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] (let [request-method :get - routes' (-> routes' routes->routes' (ring/router {})) + routes' (-> routes' routes->routes' (ring/router (or route-opt-map {}))) {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) @@ -81,8 +81,8 @@ [method (meta-merge base-swagger-spec - ;;(apply meta-merge (keep (comp :swagger :data) middleware)) - ;;(apply meta-merge (keep (comp :swagger :data) interceptors)) + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) (if coercion (rcoercion/get-apidocs coercion :swagger data)) (select-keys data [:tags :summary :description]) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 55c5895f..e98501b5 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -3,10 +3,15 @@ [jsonista.core :as json] [xiana.route.helpers :as helpers])) -(defn routes->swagger-json [routes & {type :type}] +(defn routes->swagger-json [routes + & {type :type + render? :render? + route-opt-map :route-opt-map}] (-> routes helpers/routes->routes' - helpers/routes->swagger-data + ((if render? + #(helpers/routes->swagger-data % :route-opt-map route-opt-map) + identity)) ((cond (= type :json) json/write-value-as-string (= type :edn) identity @@ -24,11 +29,16 @@ (defn ->swagger-data "Update routes for swagger data generation." - [config & {routes :routes}] + [config & {routes :routes + render? :render? + type :type + route-opt-map :route-opt-map}] (if (swagger-configs-there? config) (let [routes (or routes (-> config :routes)) routes-swagger-data (routes->swagger-json routes - :type :json) + :type type + :render? render? + :route-opt-map route-opt-map) config-key (-> config (get :xiana/swagger {}) (get :path :swagger.json))] From 45d17fb944c250f7667f5b80974e7be996bb166c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Tue, 25 Oct 2022 14:15:05 +0300 Subject: [PATCH 12/34] Kondo fixes. --- src/xiana/route/helpers.clj | 12 ++++-------- src/xiana/swagger.clj | 3 ++- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/xiana/route/helpers.clj b/src/xiana/route/helpers.clj index b869753e..b301b735 100644 --- a/src/xiana/route/helpers.clj +++ b/src/xiana/route/helpers.clj @@ -2,7 +2,6 @@ "The default not found and action functions" (:require [reitit.trie :as trie] - [clojure.set :as set] [clojure.string :as str] [reitit.coercion :as rcoercion] [reitit.core :as r] @@ -68,27 +67,24 @@ swagger (->> (strip-endpoint-keys swagger) (merge {:swagger "2.0" :x-id ids})) - accept-route (fn [route] - (-> route second :swagger :id (or ::default) (trie/into-set) (set/intersection ids) seq)) swagger-path (fn [path opts] (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) base-swagger-spec {:responses ^:displace {:default {:description ""}}} transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data middleware :middleware - interceptors :interceptors - :as args}]] - (if (and data (not no-doc)) + interceptors :interceptors}]] + (when (and data (not no-doc)) [method (meta-merge base-swagger-spec (apply meta-merge (keep (comp :swagger :data) middleware)) (apply meta-merge (keep (comp :swagger :data) interceptors)) - (if coercion + (when coercion (rcoercion/get-apidocs coercion :swagger data)) (select-keys data [:tags :summary :description]) (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] - (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] + (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] [(swagger-path p (r/options routes')) endpoint])) map-in-order #(->> % (apply concat) (apply array-map)) paths (->> routes' (r/compiled-routes) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index e98501b5..5ee6f852 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -17,11 +17,12 @@ (= type :edn) identity :else identity)))) -(defn swagger-configs-there? [config] +(defn swagger-configs-there? "Checks if the config has the required keys for swagger functionality. Required keys: * :xiana/swagger * :xiana/swagger-ui" + [config] (->> config ((juxt :xiana/swagger-ui :xiana/swagger)) (reduce #(and % %2) true) From 602e04b49b8d6e4bad20e1e57bd4add319283a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Tue, 25 Oct 2022 14:18:31 +0300 Subject: [PATCH 13/34] Fix on Styling. --- src/xiana/route/helpers.clj | 28 +++++++-------- src/xiana/swagger.clj | 20 +++++------ test/xiana/route_test.clj | 70 ++++++++++++++++++------------------- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/xiana/route/helpers.clj b/src/xiana/route/helpers.clj index b301b735..7b122395 100644 --- a/src/xiana/route/helpers.clj +++ b/src/xiana/route/helpers.clj @@ -1,12 +1,12 @@ (ns xiana.route.helpers "The default not found and action functions" (:require - [reitit.trie :as trie] - [clojure.string :as str] - [reitit.coercion :as rcoercion] - [reitit.core :as r] - [meta-merge.core :refer [meta-merge]] - [reitit.ring :as ring])) + [clojure.string :as str] + [meta-merge.core :refer [meta-merge]] + [reitit.coercion :as rcoercion] + [reitit.core :as r] + [reitit.ring :as ring] + [reitit.trie :as trie])) (defn not-found "Default not-found response handler helper." @@ -76,20 +76,20 @@ (when (and data (not no-doc)) [method (meta-merge - base-swagger-spec - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (when coercion - (rcoercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) + base-swagger-spec + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (when coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] [(swagger-path p (r/options routes')) endpoint])) map-in-order #(->> % (apply concat) (apply array-map)) paths (->> routes' (r/compiled-routes) ;; (filter accept-route) - ;;(map transform-endpoint) + ;; (map transform-endpoint) (map transform-path) map-in-order)] (meta-merge swagger {:paths paths}))) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 5ee6f852..e4ae9e98 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -1,7 +1,7 @@ (ns xiana.swagger (:require - [jsonista.core :as json] - [xiana.route.helpers :as helpers])) + [jsonista.core :as json] + [xiana.route.helpers :as helpers])) (defn routes->swagger-json [routes & {type :type @@ -36,12 +36,12 @@ route-opt-map :route-opt-map}] (if (swagger-configs-there? config) (let [routes (or routes (-> config :routes)) - routes-swagger-data (routes->swagger-json routes - :type type - :render? render? - :route-opt-map route-opt-map) - config-key (-> config - (get :xiana/swagger {}) - (get :path :swagger.json))] - (assoc config config-key routes-swagger-data)) + routes-swagger-data (routes->swagger-json routes + :type type + :render? render? + :route-opt-map route-opt-map) + config-key (-> config + (get :xiana/swagger {}) + (get :path :swagger.json))] + (assoc config config-key routes-swagger-data)) config)) diff --git a/test/xiana/route_test.clj b/test/xiana/route_test.clj index dc5c978d..14eb2836 100644 --- a/test/xiana/route_test.clj +++ b/test/xiana/route_test.clj @@ -1,10 +1,10 @@ (ns xiana.route-test (:require - [clojure.test :refer :all] - [xiana.route :as route] - [xiana.route.helpers :as helpers] - [xiana.swagger :as xsw] - [xiana.state :as state])) + [clojure.test :refer :all] + [xiana.route :as route] + [xiana.route.helpers :as helpers] + [xiana.state :as state] + [xiana.swagger :as xsw])) (def sample-request {:uri "/" :request-method :get}) @@ -41,8 +41,8 @@ (deftest contains-updated-request-data ;; get state from sample request micro/match flow (let [state (route/match (state/make - (route/reset sample-routes) - sample-request)) + (route/reset sample-routes) + sample-request)) ;; expected request data expected {:method :get :match #{[:data {:action :action}] @@ -61,8 +61,8 @@ (deftest contains-not-found-action ;; get action from sample request micro/match flow (let [action (-> (state/make - (route/reset sample-routes) - sample-not-found-request) + (route/reset sample-routes) + sample-not-found-request) route/match :request-data :action) @@ -75,8 +75,8 @@ (deftest route-contains-default-action ;; get action from the updated state/match (micro) flow computation (let [action (-> (state/make - (route/reset sample-routes-with-handler) - sample-request) + (route/reset sample-routes-with-handler) + sample-request) route/match :request-data :action) @@ -89,8 +89,8 @@ (deftest handles-route-without-action-or-handler ;; get action from the updated state/match (micro) flow computation (let [action (-> (state/make - (route/reset sample-routes-without-action) - sample-request) + (route/reset sample-routes-without-action) + sample-request) route/match :request-data :action) @@ -117,41 +117,41 @@ (xsw/routes->swagger-json :type :edn))] (testing "One swagger route for one route entry?" (let [generated-route-count (->> - generated-swagger-data - :paths - keys - count)] + generated-swagger-data + :paths + keys + count)] (is (= generated-route-count 1)))) (testing "Actions should generate every methods" (let [index-generated-methods-by-sample (->> - generated-swagger-data - :paths - (#(get % "/")) - keys - vec)] + generated-swagger-data + :paths + (#(get % "/")) + keys + vec)] (is (= - index-generated-methods-by-sample - helpers/all-methods)))))) + index-generated-methods-by-sample + helpers/all-methods)))))) (testing "Swagger Data from Sample Route /w action" (let [generated-swagger-data (-> sample-routes :routes (xsw/routes->swagger-json :type :edn))] (testing "One swagger route for one route entry?" (let [generated-route-count (->> - generated-swagger-data - :paths - keys - count)] + generated-swagger-data + :paths + keys + count)] (is (= generated-route-count 1)))) (testing "Actions should generate every methods" (let [index-generated-methods-by-sample (->> - generated-swagger-data - :paths - (#(get % "/")) - keys - vec)] + generated-swagger-data + :paths + (#(get % "/")) + keys + vec)] (is (= - index-generated-methods-by-sample - helpers/all-methods)))))))) + index-generated-methods-by-sample + helpers/all-methods)))))))) From aa58a2e189c98e380eb927d1fec8ddd3cfd56fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Tue, 25 Oct 2022 15:28:22 +0300 Subject: [PATCH 14/34] Fix over render? flag. --- src/xiana/swagger.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index e4ae9e98..6a7091b4 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -9,7 +9,7 @@ route-opt-map :route-opt-map}] (-> routes helpers/routes->routes' - ((if render? + ((if (not (false? render?)) #(helpers/routes->swagger-data % :route-opt-map route-opt-map) identity)) ((cond From d432a885e4563c0c8ff9dced18b3a1ccfba0cca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Tue, 25 Oct 2022 19:50:53 +0300 Subject: [PATCH 15/34] Removing the logic from it since it's not the right place now. --- src/xiana/route/helpers.clj | 79 +------------------------------------ 1 file changed, 1 insertion(+), 78 deletions(-) diff --git a/src/xiana/route/helpers.clj b/src/xiana/route/helpers.clj index 7b122395..d106e511 100644 --- a/src/xiana/route/helpers.clj +++ b/src/xiana/route/helpers.clj @@ -1,12 +1,5 @@ (ns xiana.route.helpers - "The default not found and action functions" - (:require - [clojure.string :as str] - [meta-merge.core :refer [meta-merge]] - [reitit.coercion :as rcoercion] - [reitit.core :as r] - [reitit.ring :as ring] - [reitit.trie :as trie])) + "The default not found and action functions") (defn not-found "Default not-found response handler helper." @@ -23,73 +16,3 @@ (assoc :error e) (assoc :response {:status 500 :body "Internal Server error"}))))) - -(defonce all-methods - [:get :patch :trace :connect :delete :head :post :options :put]) - -(def routes->routes' - #(-> (fn *** [[url opt-map & nested-routes :as route]] - (let [new-opt-map (if (:action opt-map) - (let [action' (:action opt-map) - swagger-base-of-endpoint (:swagger-* opt-map)] - (reduce (fn [acc method] - (-> acc - (assoc-in [method :handler] identity) - (assoc-in [method :action] action') - (merge swagger-base-of-endpoint))) - opt-map - all-methods)) - (let [swagger-base-of-endpoint (get opt-map :swagger-* {})] - (reduce (fn [acc method] - (if (get acc method) - (if (get-in acc [method :handler]) - acc - (-> acc - (assoc-in [method :handler] identity) - (merge swagger-base-of-endpoint))) - acc)) - opt-map - all-methods)))] - (if (-> route meta :no-doc) - nil - (apply conj [url new-opt-map] - (map *** nested-routes))))) - (keep %) - vec)) - -(defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] - (let [request-method :get - routes' (-> routes' routes->routes' (ring/router (or route-opt-map {}))) - {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) - ids (trie/into-set id) - strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) - strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) - swagger (->> (strip-endpoint-keys swagger) - (merge {:swagger "2.0" - :x-id ids})) - swagger-path (fn [path opts] - (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) - base-swagger-spec {:responses ^:displace {:default {:description ""}}} - transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data - middleware :middleware - interceptors :interceptors}]] - (when (and data (not no-doc)) - [method - (meta-merge - base-swagger-spec - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (when coercion - (rcoercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) - transform-path (fn [[p _ c]] - (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] - [(swagger-path p (r/options routes')) endpoint])) - map-in-order #(->> % (apply concat) (apply array-map)) - paths (->> routes' (r/compiled-routes) - ;; (filter accept-route) - ;; (map transform-endpoint) - (map transform-path) - map-in-order)] - (meta-merge swagger {:paths paths}))) From a63568ea32887676996f2be6e85099107deee151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Tue, 25 Oct 2022 19:51:19 +0300 Subject: [PATCH 16/34] Multiple fixes and changes. Track on PR. --- src/xiana/swagger.clj | 116 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 11 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 6a7091b4..0de228af 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -1,16 +1,110 @@ (ns xiana.swagger (:require + [clojure.string :as str] [jsonista.core :as json] - [xiana.route.helpers :as helpers])) + [meta-merge.core :refer [meta-merge]] + [reitit.coercion :as rcoercion] + [reitit.core :as r] + [reitit.ring :as ring] + [reitit.trie :as trie] + [ring.util.response])) + +(defonce all-methods + [:get :patch :trace :connect :delete :head :post :options :put]) + +(def routes->routes' + #(-> (fn *** [[url opt-map & nested-routes :as route]] + (let [new-opt-map (if (:action opt-map) + (let [action' (:action opt-map) + swagger-base-of-endpoint (:swagger-* opt-map)] + (reduce (fn [acc method] + (-> acc + (assoc-in [method :handler] identity) + (assoc-in [method :action] action') + (merge swagger-base-of-endpoint))) + opt-map + all-methods)) + (let [swagger-base-of-endpoint (get opt-map :swagger-* {})] + (reduce (fn [acc method] + (if (get acc method) + (if (get-in acc [method :handler]) + acc + (-> acc + (assoc-in [method :handler] identity) + (merge swagger-base-of-endpoint))) + acc)) + opt-map + all-methods)))] + (if (-> route meta :no-doc) + nil + (apply conj [url new-opt-map] + (map *** nested-routes))))) + (keep %) + vec)) + +(defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] + (let [request-method :get + routes' (-> routes' routes->routes' (ring/router (or route-opt-map {}))) + {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) + ids (trie/into-set id) + strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) + strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) + swagger (->> (strip-endpoint-keys swagger) + (merge {:swagger "2.0" + :x-id ids})) + swagger-path (fn [path opts] + (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) + base-swagger-spec {:responses ^:displace {:default {:description ""}}} + transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data + middleware :middleware + interceptors :interceptors}]] + (when (and data (not no-doc)) + [method + (meta-merge + base-swagger-spec + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (when coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) + transform-path (fn [[p _ c]] + (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] + [(swagger-path p (r/options routes')) endpoint])) + map-in-order #(->> % (apply concat) (apply array-map)) + paths (->> routes' (r/compiled-routes) + ;; (filter accept-route) + ;; (map transform-endpoint) + (map transform-path) + map-in-order)] + (meta-merge swagger {:paths paths}))) + +(def default-internal-swagger-endpoints + [^{:no-doc true} + ["/swagger-ui" + {:get {:action + (fn [state] + (assoc state + :response + (-> "swaggerui.html" + (ring.util.response/resource-response {:root "public"}) + (ring.util.response/header "Content-Type" "text/html; charset=utf-8"))))}}] + ^{:no-doc true} + ["/swagger.json" + {:action (fn [state] + (assoc state + :response + (ring.util.response/response + (str (-> state :deps ((-> state :deps :xiana/swagger :path)))))))}]]) (defn routes->swagger-json [routes & {type :type render? :render? route-opt-map :route-opt-map}] (-> routes - helpers/routes->routes' + routes->routes' ((if (not (false? render?)) - #(helpers/routes->swagger-data % :route-opt-map route-opt-map) + #(routes->swagger-data % :route-opt-map route-opt-map) identity)) ((cond (= type :json) json/write-value-as-string @@ -23,25 +117,25 @@ * :xiana/swagger * :xiana/swagger-ui" [config] - (->> config - ((juxt :xiana/swagger-ui :xiana/swagger)) - (reduce #(and % %2) true) - boolean)) + (every? some? ((juxt :xiana/swagger-ui :xiana/swagger) config))) (defn ->swagger-data "Update routes for swagger data generation." [config & {routes :routes + internal? :internal? render? :render? type :type route-opt-map :route-opt-map}] (if (swagger-configs-there? config) - (let [routes (or routes (-> config :routes)) + (let [routes (or routes + (get config :routes [])) + routes (if internal? + (apply conj routes default-internal-swagger-endpoints) + routes) routes-swagger-data (routes->swagger-json routes :type type :render? render? :route-opt-map route-opt-map) - config-key (-> config - (get :xiana/swagger {}) - (get :path :swagger.json))] + config-key (get-in config [:xiana/swagger :path] :swagger.json)] (assoc config config-key routes-swagger-data)) config)) From 2f4359feb1a3d8bbf3b19e8fdf8baa9e6544bd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Tue, 25 Oct 2022 20:16:19 +0300 Subject: [PATCH 17/34] Fix on tests. The var itself moved up. --- test/xiana/route_test.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/xiana/route_test.clj b/test/xiana/route_test.clj index 14eb2836..9db8f6a4 100644 --- a/test/xiana/route_test.clj +++ b/test/xiana/route_test.clj @@ -132,7 +132,7 @@ vec)] (is (= index-generated-methods-by-sample - helpers/all-methods)))))) + xsw/all-methods)))))) (testing "Swagger Data from Sample Route /w action" (let [generated-swagger-data (-> sample-routes :routes @@ -154,4 +154,4 @@ vec)] (is (= index-generated-methods-by-sample - helpers/all-methods)))))))) + xsw/all-methods)))))))) From 35702a8218f1ada4212d3864ae1f64877fd07edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 27 Oct 2022 19:39:03 +0300 Subject: [PATCH 18/34] Providing new swagger/ui values on config. --- config/dev/config.edn | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/config/dev/config.edn b/config/dev/config.edn index 1be409e9..d21d56de 100644 --- a/config/dev/config.edn +++ b/config/dev/config.edn @@ -6,9 +6,16 @@ :password "postgres"} :xiana/web-server {:port 3000 :join? false} - :xiana/swagger {:uri-path "swagger/swagger.json" - :path :swagger.json} - :xiana/swagger-ui {:uri-path "swagger/swagger-ui"} + :xiana/swagger {:uri-path "/swagger/swagger.json" + :path :swagger.json + :data {:coercion (reitit.coercion.malli/create + {:error-keys #{:coercion :in :schema :value :errors :humanized} + :compile malli.util/closed-schema + :strip-extra-keys true + :default-values true + :options nil}) + :middleware [reitit.swagger/swagger-feature]}} + :xiana/swagger-ui {:uri-path "/swagger/swagger-ui"} :xiana/migration {:store :database :migration-dir "resources/migrations" :init-in-transaction? false From e7184e98ef89d32a787c0f5a4af6acdda53a5d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 27 Oct 2022 19:39:22 +0300 Subject: [PATCH 19/34] A new dependency, hiccup added. --- deps.edn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index 82033578..1a38dfa9 100644 --- a/deps.edn +++ b/deps.edn @@ -23,7 +23,8 @@ piotr-yuxuan/closeable-map {:mvn/version "0.35.0"}, seancorfield/next.jdbc {:mvn/version "1.2.659"}, yogthos/config {:mvn/version "1.2.0"} - ring/ring {:mvn/version "1.9.6"}} + ring/ring {:mvn/version "1.9.6"} + hiccup/hiccup {:mvn/version "1.0.5"}} :aliases {:dev From 989b1dcdd5b0045e3bf7d4ec162fe94d8771bf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 27 Oct 2022 19:39:47 +0300 Subject: [PATCH 20/34] Re-factore the Swagger/UI implementation on Xiana; * Providing swagger-ui as a config generated content, implicitly. * Providing swagger/ui end-points implicitly. * New API over Swagger functionality. --- src/xiana/swagger.clj | 170 +++++++++++++++++++++++++++++++----------- 1 file changed, 127 insertions(+), 43 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 0de228af..a1943cd2 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -1,13 +1,18 @@ (ns xiana.swagger (:require - [clojure.string :as str] - [jsonista.core :as json] - [meta-merge.core :refer [meta-merge]] - [reitit.coercion :as rcoercion] - [reitit.core :as r] - [reitit.ring :as ring] - [reitit.trie :as trie] - [ring.util.response])) + [clojure.string :as str] + [jsonista.core :as json] + [meta-merge.core :refer [meta-merge]] + [reitit.coercion :as rcoercion] + [reitit.core :as r] + [reitit.ring :as ring] + [reitit.trie :as trie] + [ring.util.response] + [reitit.coercion.malli] + [malli.util] + [reitit.swagger] + [hiccup.core :as h] + [xiana.config :as config])) (defonce all-methods [:get :patch :trace :connect :delete :head :post :options :put]) @@ -61,13 +66,13 @@ (when (and data (not no-doc)) [method (meta-merge - base-swagger-spec - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (when coercion - (rcoercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) + base-swagger-spec + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (when coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] [(swagger-path p (r/options routes')) endpoint])) @@ -79,23 +84,92 @@ map-in-order)] (meta-merge swagger {:paths paths}))) -(def default-internal-swagger-endpoints - [^{:no-doc true} - ["/swagger-ui" - {:get {:action - (fn [state] - (assoc state - :response - (-> "swaggerui.html" - (ring.util.response/resource-response {:root "public"}) - (ring.util.response/header "Content-Type" "text/html; charset=utf-8"))))}}] - ^{:no-doc true} - ["/swagger.json" - {:action (fn [state] +(defn ->default-internal-swagger-ui-html [config] + (let [schema-protocol (get-in config [:deps :xiana/web-server :protocol] :http) + swagger-json-uri-path (get-in config [:deps :xiana/swagger :uri-path])] + (h/html [:html {:lang "en"} + [:head + [:meta {:charset "UTF-8"}] + [:title "Swagger UI"] + [:link + {:referrerpolicy "no-referrer", + :crossorigin "anonymous", + :integrity + "sha512-lfbw/3iTOqI2s3gVb0fIwex5Y9WpcFM8Oq6XMpD8R5jMjOgzIgXjDNg7mNqbWS1I6qqC7sFaaMHXNsnVstkQYQ==", + :href + "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.52.4/swagger-ui.min.css", + :rel "stylesheet"}] + [:style + "html {box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll;} + *, *:before, *:after { box-sizing: inherit;} + body {margin: 0; background: #fafafa;}"] + [:link + {:sizes "32x32", + :href "./favicon-32x32.png", + :type "image/png", + :rel "icon"}] + [:link + {:sizes "16x16", + :href "./favicon-16x16.png", + :type "image/png", + :rel "icon"}]] + [:body + [:div#swagger-ui] + [:script + {:referrerpolicy "no-referrer", + :crossorigin "anonymous", + :integrity + "sha512-w+D7rGMfhW/r7/lGU7mu92gjvuo4ZQddFOm5iJ0EAQNS7mmhCb10I8GcgrGTr1zJvCYcxj4roHMo66sLNQOgqA==", + :src + "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.52.4/swagger-ui-bundle.min.js"}] + [:script + {:referrerpolicy "no-referrer", + :crossorigin "anonymous", + :integrity + "sha512-OdiS0y42zD5WmBnJ6H8K3SCYjAjIJQrUOAraBx5PH1QSLtq+KNLy80uQKruXCJTGZKdQ7hhu/AD+WC+wuYUS+w==", + :src + "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.52.4/swagger-ui-standalone-preset.min.js"}] + [:script + (str "window.onload = function () +{ +// TODO: [Seçkin] can be replace-able with in-app configuration and pass it with json-encoding + window.ui = SwaggerUIBundle( + { + url: '" swagger-json-uri-path "', + schemes: ['" (name schema-protocol) "'], + dom_id: '#swagger-ui', + deepLinking: true, + presets: [SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset], + plugins: [SwaggerUIBundle.plugins.DownloadUrl], + layout: 'StandaloneLayout'} + ); +};")]]]))) + +#_(-> (config/config {:framework-edn-config "config/dev/config.edn"}) + ->default-internal-swagger-ui-html) + +(defn ->default-internal-swagger-endpoints [config] + [(let [{:keys [uri-path]} (get-in config [:xiana/swagger-ui])] + ^{:no-doc true} + [uri-path + {:get {:action + (fn [state] (assoc state :response - (ring.util.response/response - (str (-> state :deps ((-> state :deps :xiana/swagger :path)))))))}]]) + (-> state + ->default-internal-swagger-ui-html + ring.util.response/response + (ring.util.response/header "Content-Type" "text/html; charset=utf-8"))))}}]) + (let [{:keys [uri-path]} (get-in config [:xiana/swagger])] + ^{:no-doc true} + [uri-path + {:action (fn [state] + (assoc state + :response + (-> (str (-> state :deps ((get-in state [:deps :xiana/swagger :path])))) + ring.util.response/response + (ring.util.response/header "Content-Type" "application/json; charset=utf-8"))))}])]) (defn routes->swagger-json [routes & {type :type @@ -126,16 +200,26 @@ render? :render? type :type route-opt-map :route-opt-map}] - (if (swagger-configs-there? config) - (let [routes (or routes - (get config :routes [])) - routes (if internal? - (apply conj routes default-internal-swagger-endpoints) - routes) - routes-swagger-data (routes->swagger-json routes - :type type - :render? render? - :route-opt-map route-opt-map) - config-key (get-in config [:xiana/swagger :path] :swagger.json)] - (assoc config config-key routes-swagger-data)) - config)) + (let [internal? (or internal? true) + render? (or render? true) + type (or type :json) + config (update-in config [:xiana/swagger :data] eval) + route-opt-map {:data (or (get-in config [:xiana/swagger :data]) + route-opt-map)} + config (update-in config [:xiana/swagger :data] (constantly route-opt-map))] + (if (swagger-configs-there? config) + (let [routes (or routes + (get config :routes [])) + routes (if internal? + (apply conj routes (->default-internal-swagger-endpoints config)) + routes) + routes-swagger-data (routes->swagger-json routes + :type type + :render? render? + :route-opt-map route-opt-map) + #_"TODO: [Seçkin] You can't just place not-found value." + config-key (get-in config [:xiana/swagger :path] :swagger.json)] + (-> config + (assoc config-key routes-swagger-data) + (assoc :routes routes))) + config))) From 3401651063e015d379199a32685df781b81a97be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 27 Oct 2022 19:45:30 +0300 Subject: [PATCH 21/34] Removing left-over require. --- src/xiana/swagger.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index a1943cd2..ec27a813 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -11,8 +11,7 @@ [reitit.coercion.malli] [malli.util] [reitit.swagger] - [hiccup.core :as h] - [xiana.config :as config])) + [hiccup.core :as h])) (defonce all-methods [:get :patch :trace :connect :delete :head :post :options :put]) From 85965658e3c7bddab9bf88c48bfe1145bab508df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=9CKRER?= Date: Thu, 27 Oct 2022 19:46:37 +0300 Subject: [PATCH 22/34] Formatted internally. --- config/dev/config.edn | 10 +++++----- src/xiana/swagger.clj | 38 +++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/config/dev/config.edn b/config/dev/config.edn index d21d56de..1ddae625 100644 --- a/config/dev/config.edn +++ b/config/dev/config.edn @@ -9,11 +9,11 @@ :xiana/swagger {:uri-path "/swagger/swagger.json" :path :swagger.json :data {:coercion (reitit.coercion.malli/create - {:error-keys #{:coercion :in :schema :value :errors :humanized} - :compile malli.util/closed-schema - :strip-extra-keys true - :default-values true - :options nil}) + {:error-keys #{:coercion :in :schema :value :errors :humanized} + :compile malli.util/closed-schema + :strip-extra-keys true + :default-values true + :options nil}) :middleware [reitit.swagger/swagger-feature]}} :xiana/swagger-ui {:uri-path "/swagger/swagger-ui"} :xiana/migration {:store :database diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index ec27a813..8aa2dc55 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -1,17 +1,17 @@ (ns xiana.swagger (:require - [clojure.string :as str] - [jsonista.core :as json] - [meta-merge.core :refer [meta-merge]] - [reitit.coercion :as rcoercion] - [reitit.core :as r] - [reitit.ring :as ring] - [reitit.trie :as trie] - [ring.util.response] - [reitit.coercion.malli] - [malli.util] - [reitit.swagger] - [hiccup.core :as h])) + [clojure.string :as str] + [hiccup.core :as h] + [jsonista.core :as json] + [malli.util] + [meta-merge.core :refer [meta-merge]] + [reitit.coercion :as rcoercion] + [reitit.coercion.malli] + [reitit.core :as r] + [reitit.ring :as ring] + [reitit.swagger] + [reitit.trie :as trie] + [ring.util.response])) (defonce all-methods [:get :patch :trace :connect :delete :head :post :options :put]) @@ -65,13 +65,13 @@ (when (and data (not no-doc)) [method (meta-merge - base-swagger-spec - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (when coercion - (rcoercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) + base-swagger-spec + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (when coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] [(swagger-path p (r/options routes')) endpoint])) From 8614a5019b1f7c94093387fcfcfe803d14bc5e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=BCkrer?= Date: Tue, 15 Nov 2022 17:36:09 +0300 Subject: [PATCH 23/34] Update swagger.clj Removing unwanted, development s-expressions from the PR. --- src/xiana/swagger.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 8aa2dc55..b8b320d7 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -145,9 +145,6 @@ ); };")]]]))) -#_(-> (config/config {:framework-edn-config "config/dev/config.edn"}) - ->default-internal-swagger-ui-html) - (defn ->default-internal-swagger-endpoints [config] [(let [{:keys [uri-path]} (get-in config [:xiana/swagger-ui])] ^{:no-doc true} From eb17d7db930fffd1256c426e738f46da3f25e78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=BCkrer?= Date: Fri, 20 Jan 2023 16:17:50 +0300 Subject: [PATCH 24/34] Changes from re-factoring session with Core team. --- src/xiana/swagger.clj | 133 ++++++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 50 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index b8b320d7..353341f2 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -1,50 +1,80 @@ (ns xiana.swagger (:require - [clojure.string :as str] - [hiccup.core :as h] - [jsonista.core :as json] - [malli.util] - [meta-merge.core :refer [meta-merge]] - [reitit.coercion :as rcoercion] - [reitit.coercion.malli] - [reitit.core :as r] - [reitit.ring :as ring] - [reitit.swagger] - [reitit.trie :as trie] - [ring.util.response])) + [clojure.string :as str] + [hiccup.core :as h] + [jsonista.core :as json] + [malli.util] + [meta-merge.core :refer [meta-merge]] + [reitit.coercion :as rcoercion] + [reitit.coercion.malli] + [reitit.core :as r] + [reitit.ring :as ring] + [reitit.swagger] + [reitit.trie :as trie] + [ring.util.response])) (defonce all-methods [:get :patch :trace :connect :delete :head :post :options :put]) -(def routes->routes' - #(-> (fn *** [[url opt-map & nested-routes :as route]] - (let [new-opt-map (if (:action opt-map) - (let [action' (:action opt-map) - swagger-base-of-endpoint (:swagger-* opt-map)] - (reduce (fn [acc method] - (-> acc - (assoc-in [method :handler] identity) - (assoc-in [method :action] action') - (merge swagger-base-of-endpoint))) - opt-map - all-methods)) - (let [swagger-base-of-endpoint (get opt-map :swagger-* {})] - (reduce (fn [acc method] - (if (get acc method) - (if (get-in acc [method :handler]) - acc - (-> acc - (assoc-in [method :handler] identity) - (merge swagger-base-of-endpoint))) - acc)) - opt-map - all-methods)))] - (if (-> route meta :no-doc) - nil - (apply conj [url new-opt-map] - (map *** nested-routes))))) - (keep %) - vec)) +(defn xiana-route->reitit-route + "xiana-route->reitit-route is taking route entry of our custom shape of routes + and transforms it into proper reitit route entry that is valid on the Swagger + implemention of reitit. + + (xiana-route->reitit-route [\"/swagger-ui\" {:action :swagger-ui + :some-values true}]) + ;; => [\"/swagger-ui\" + {:get + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :patch + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :trace + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :connect + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :delete + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :head + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :post + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :action :swagger-ui, + :options + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :put + {:handler #function[clojure.core/identity], :action :swagger-ui}, + :some-values true}] + " + [[url opt-map & nested-routes :as route]] + (let [new-opt-map (if (:action opt-map) + (let [action' (:action opt-map) + swagger-base-of-endpoint (:swagger-* opt-map)] + (reduce (fn [acc method] + (-> acc + (assoc-in [method :handler] identity) + (assoc-in [method :action] action') + (merge swagger-base-of-endpoint))) + opt-map + all-methods)) + (let [swagger-base-of-endpoint (get opt-map :swagger-* {})] + (reduce (fn [acc method] + (if (get acc method) + (if (get-in acc [method :handler]) + acc + (-> acc + (assoc-in [method :handler] identity) + (merge swagger-base-of-endpoint))) + acc)) + opt-map + all-methods)))] + (if (-> route meta :no-doc) + nil + (apply conj [url new-opt-map] + (map *** nested-routes))))) + +(defn xiana-routes->reitit-routes [all-methods routes] + (vec + (keep (partial xiana-route->reitit-route (all-methods)) routes))) (defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] (let [request-method :get @@ -65,13 +95,13 @@ (when (and data (not no-doc)) [method (meta-merge - base-swagger-spec - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (when coercion - (rcoercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) + base-swagger-spec + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (when coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] [(swagger-path p (r/options routes')) endpoint])) @@ -131,7 +161,7 @@ [:script (str "window.onload = function () { -// TODO: [Seçkin] can be replace-able with in-app configuration and pass it with json-encoding +// TODO: [@LeaveNhA] can be replace-able with in-app configuration and pass it with json-encoding window.ui = SwaggerUIBundle( { url: '" swagger-json-uri-path "', @@ -145,6 +175,9 @@ ); };")]]]))) +#_(-> (config/config {:framework-edn-config "config/dev/config.edn"}) + ->default-internal-swagger-ui-html) + (defn ->default-internal-swagger-endpoints [config] [(let [{:keys [uri-path]} (get-in config [:xiana/swagger-ui])] ^{:no-doc true} @@ -213,7 +246,7 @@ :type type :render? render? :route-opt-map route-opt-map) - #_"TODO: [Seçkin] You can't just place not-found value." + #_"TODO: [@LeaveNhA] You can't just place not-found value." config-key (get-in config [:xiana/swagger :path] :swagger.json)] (-> config (assoc config-key routes-swagger-data) From 722ed8ac133eb11aae0ac3cdee969442d5aaa19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A7kin=20K=C3=BCkrer?= Date: Fri, 20 Jan 2023 16:19:03 +0300 Subject: [PATCH 25/34] Fixing the re-factored function usage. --- src/xiana/swagger.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 353341f2..65c3a880 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -78,7 +78,7 @@ (defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] (let [request-method :get - routes' (-> routes' routes->routes' (ring/router (or route-opt-map {}))) + routes' (-> routes' xiana-routes->reitit-routes (ring/router (or route-opt-map {}))) {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) @@ -205,7 +205,7 @@ render? :render? route-opt-map :route-opt-map}] (-> routes - routes->routes' + xiana-routes->reitit-routes ((if (not (false? render?)) #(routes->swagger-data % :route-opt-map route-opt-map) identity)) From d4b8bcf07892f88e2c3a2613a78b7584f202874d Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Wed, 6 Sep 2023 07:46:50 -0300 Subject: [PATCH 26/34] Fix bugs WIP --- src/xiana/swagger.clj | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 65c3a880..36b0fa38 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -13,6 +13,8 @@ [reitit.trie :as trie] [ring.util.response])) +(def start-one 1) + (defonce all-methods [:get :patch :trace :connect :delete :head :post :options :put]) @@ -45,7 +47,8 @@ {:handler #function[clojure.core/identity], :action :swagger-ui}, :some-values true}] " - [[url opt-map & nested-routes :as route]] +;;; changed the order of args to not break functions + [[url opt-map & nested-routes :as route] all-methods] (let [new-opt-map (if (:action opt-map) (let [action' (:action opt-map) swagger-base-of-endpoint (:swagger-* opt-map)] @@ -70,15 +73,23 @@ (if (-> route meta :no-doc) nil (apply conj [url new-opt-map] - (map *** nested-routes))))) + (map #(xiana-route->reitit-route % all-methods) nested-routes))))) +;;; ^^ changed the function to get all-methods + +(defn partial-second + [f second-arg] + (fn [x] + (f x second-arg))) -(defn xiana-routes->reitit-routes [all-methods routes] +;;; ^^ helper function for below vv we need this to keep the order of Seç +(defn xiana-routes->reitit-routes [routes all-methods] (vec - (keep (partial xiana-route->reitit-route (all-methods)) routes))) + (keep (partial-second xiana-route->reitit-route all-methods) routes))) +;;; TODO ^^ use #(xiana-route->reitit-route % all-methods) (defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] (let [request-method :get - routes' (-> routes' xiana-routes->reitit-routes (ring/router (or route-opt-map {}))) + routes' (-> (xiana-routes->reitit-routes routes' all-methods) (ring/router (or route-opt-map {}))) {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) @@ -205,7 +216,7 @@ render? :render? route-opt-map :route-opt-map}] (-> routes - xiana-routes->reitit-routes + (xiana-routes->reitit-routes all-methods) ((if (not (false? render?)) #(routes->swagger-data % :route-opt-map route-opt-map) identity)) @@ -224,11 +235,13 @@ (defn ->swagger-data "Update routes for swagger data generation." +;;; vv maybe remove optional parameter here, because it is unused somehow!!! [config & {routes :routes internal? :internal? render? :render? type :type - route-opt-map :route-opt-map}] + route-opt-map :route-opt-map + :as m}] (let [internal? (or internal? true) render? (or render? true) type (or type :json) @@ -236,12 +249,17 @@ route-opt-map {:data (or (get-in config [:xiana/swagger :data]) route-opt-map)} config (update-in config [:xiana/swagger :data] (constantly route-opt-map))] +;;; ^^ this could be (assoc-in config [:xiana/swagger :data] route-opt-map) ?? +;;; it is adding nothing at all to config just eval the [:xiana/swagger :data] +;;; only if :data must be a fn now it is doing something + (if (swagger-configs-there? config) (let [routes (or routes (get config :routes [])) routes (if internal? (apply conj routes (->default-internal-swagger-endpoints config)) routes) + _ (def routes* routes) routes-swagger-data (routes->swagger-json routes :type type :render? render? From 52be36259a47fdfc951fbabae731cea243b351ed Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Thu, 7 Sep 2023 08:11:06 -0300 Subject: [PATCH 27/34] Refactor nonsense code --- src/xiana/swagger.clj | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 36b0fa38..5e1e0bd5 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -89,7 +89,7 @@ (defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] (let [request-method :get - routes' (-> (xiana-routes->reitit-routes routes' all-methods) (ring/router (or route-opt-map {}))) + routes' (-> routes' (ring/router (or route-opt-map {}))) {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) @@ -189,7 +189,10 @@ #_(-> (config/config {:framework-edn-config "config/dev/config.edn"}) ->default-internal-swagger-ui-html) -(defn ->default-internal-swagger-endpoints [config] + +(defn ->default-internal-swagger-endpoints + "This will return a vector of two items, swagger.json and swagger-ui, each a route vector" + [config] [(let [{:keys [uri-path]} (get-in config [:xiana/swagger-ui])] ^{:no-doc true} [uri-path @@ -217,7 +220,7 @@ route-opt-map :route-opt-map}] (-> routes (xiana-routes->reitit-routes all-methods) - ((if (not (false? render?)) + ((if render? #(routes->swagger-data % :route-opt-map route-opt-map) identity)) ((cond @@ -242,8 +245,8 @@ type :type route-opt-map :route-opt-map :as m}] - (let [internal? (or internal? true) - render? (or render? true) + (let [internal? true + render? true type (or type :json) config (update-in config [:xiana/swagger :data] eval) route-opt-map {:data (or (get-in config [:xiana/swagger :data]) @@ -252,14 +255,12 @@ ;;; ^^ this could be (assoc-in config [:xiana/swagger :data] route-opt-map) ?? ;;; it is adding nothing at all to config just eval the [:xiana/swagger :data] ;;; only if :data must be a fn now it is doing something - (if (swagger-configs-there? config) (let [routes (or routes (get config :routes [])) routes (if internal? (apply conj routes (->default-internal-swagger-endpoints config)) routes) - _ (def routes* routes) routes-swagger-data (routes->swagger-json routes :type type :render? render? From a50b13da656986af312a9f3143016994fa766601 Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Wed, 13 Sep 2023 07:53:53 -0300 Subject: [PATCH 28/34] Clean ->swagger-data function --- src/xiana/swagger.clj | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 5e1e0bd5..726b3d56 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -16,7 +16,8 @@ (def start-one 1) (defonce all-methods - [:get :patch :trace :connect :delete :head :post :options :put]) + #_[:get :patch :trace :connect :delete :head :post :options :put] + [:get :post]) (defn xiana-route->reitit-route "xiana-route->reitit-route is taking route entry of our custom shape of routes @@ -223,7 +224,7 @@ ((if render? #(routes->swagger-data % :route-opt-map route-opt-map) identity)) - ((cond + ((cond (= type :json) json/write-value-as-string (= type :edn) identity :else identity)))) @@ -238,34 +239,20 @@ (defn ->swagger-data "Update routes for swagger data generation." -;;; vv maybe remove optional parameter here, because it is unused somehow!!! - [config & {routes :routes - internal? :internal? - render? :render? - type :type - route-opt-map :route-opt-map - :as m}] + [config] (let [internal? true render? true - type (or type :json) + type :json config (update-in config [:xiana/swagger :data] eval) - route-opt-map {:data (or (get-in config [:xiana/swagger :data]) - route-opt-map)} - config (update-in config [:xiana/swagger :data] (constantly route-opt-map))] -;;; ^^ this could be (assoc-in config [:xiana/swagger :data] route-opt-map) ?? -;;; it is adding nothing at all to config just eval the [:xiana/swagger :data] -;;; only if :data must be a fn now it is doing something + route-opt-map {:data (get-in config [:xiana/swagger :data])} + config (assoc-in config [:xiana/swagger :data] route-opt-map)] (if (swagger-configs-there? config) - (let [routes (or routes - (get config :routes [])) - routes (if internal? - (apply conj routes (->default-internal-swagger-endpoints config)) - routes) + (let [routes (get config :routes) + routes (apply conj routes (->default-internal-swagger-endpoints config)) routes-swagger-data (routes->swagger-json routes :type type :render? render? :route-opt-map route-opt-map) - #_"TODO: [@LeaveNhA] You can't just place not-found value." config-key (get-in config [:xiana/swagger :path] :swagger.json)] (-> config (assoc config-key routes-swagger-data) From d6a70e56e63e246ccb25ed977bb58c594d54c417 Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Thu, 14 Sep 2023 07:35:28 -0300 Subject: [PATCH 29/34] Clean routes->swagger-data function --- src/xiana/swagger.clj | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 726b3d56..f12b2332 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -75,18 +75,10 @@ nil (apply conj [url new-opt-map] (map #(xiana-route->reitit-route % all-methods) nested-routes))))) -;;; ^^ changed the function to get all-methods -(defn partial-second - [f second-arg] - (fn [x] - (f x second-arg))) - -;;; ^^ helper function for below vv we need this to keep the order of Seç (defn xiana-routes->reitit-routes [routes all-methods] (vec - (keep (partial-second xiana-route->reitit-route all-methods) routes))) -;;; TODO ^^ use #(xiana-route->reitit-route % all-methods) + (keep #(xiana-route->reitit-route % all-methods) routes))) (defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] (let [request-method :get @@ -219,15 +211,10 @@ & {type :type render? :render? route-opt-map :route-opt-map}] - (-> routes - (xiana-routes->reitit-routes all-methods) - ((if render? - #(routes->swagger-data % :route-opt-map route-opt-map) - identity)) - ((cond - (= type :json) json/write-value-as-string - (= type :edn) identity - :else identity)))) + (let [reitit-routes (xiana-routes->reitit-routes routes all-methods)] + (if (and render? (= type :json)) + (json/write-value-as-string (routes->swagger-data reitit-routes :route-opt-map route-opt-map)) + reitit-routes))) (defn swagger-configs-there? "Checks if the config has the required keys for swagger functionality. From 7b42371f4df657f26a5d0b7e7aefe0c1159ac5ed Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Thu, 14 Sep 2023 08:47:57 -0300 Subject: [PATCH 30/34] Refactor routes->swagger-data --- src/xiana/swagger.clj | 69 ++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index f12b2332..aeca4df4 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -80,40 +80,43 @@ (vec (keep #(xiana-route->reitit-route % all-methods) routes))) -(defn routes->swagger-data [routes' & {route-opt-map :route-opt-map}] - (let [request-method :get - routes' (-> routes' (ring/router (or route-opt-map {}))) - {:keys [id] :or {id ::default} :as swagger} (-> routes' :result request-method :data :swagger) - ids (trie/into-set id) - strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) - strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) - swagger (->> (strip-endpoint-keys swagger) - (merge {:swagger "2.0" - :x-id ids})) - swagger-path (fn [path opts] - (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) - base-swagger-spec {:responses ^:displace {:default {:description ""}}} - transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data - middleware :middleware - interceptors :interceptors}]] - (when (and data (not no-doc)) - [method - (meta-merge - base-swagger-spec - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (when coercion - (rcoercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) - transform-path (fn [[p _ c]] - (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] - [(swagger-path p (r/options routes')) endpoint])) +(defn strip-top-level-keys + [m] + (dissoc m :id :info :host :basePath :definitions :securityDefinitions)) + +(def base-swagger-spec {:responses ^:displace {:default {:description ""}}}) + +(defn transform-endpoint + [[method {{:keys [coercion no-doc swagger] :as data} :data + middleware :middleware + interceptors :interceptors}]] + (when (and data (not no-doc)) + [method + (meta-merge + base-swagger-spec + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (when coercion + (rcoercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) + +(defn swagger-path + [path opts] + (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) + +(defn transform-path + [[p _ c] router] + (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] + [(swagger-path p (r/options router)) endpoint])) + +(defn routes->swagger-data [routes & {route-opt-map :route-opt-map}] + (let [router (ring/router routes (or route-opt-map {})) + swagger {:swagger "2.0" + :x-id ::default} map-in-order #(->> % (apply concat) (apply array-map)) - paths (->> routes' (r/compiled-routes) - ;; (filter accept-route) - ;; (map transform-endpoint) - (map transform-path) + paths (->> router (r/compiled-routes) + (map #(transform-path % router)) map-in-order)] (meta-merge swagger {:paths paths}))) From 9e385f327e82a5beec34550672c6bb990c4e197d Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Wed, 27 Sep 2023 13:31:22 -0300 Subject: [PATCH 31/34] Add new docstrings --- src/xiana/swagger.clj | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index aeca4df4..d475f0d7 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -13,8 +13,6 @@ [reitit.trie :as trie] [ring.util.response])) -(def start-one 1) - (defonce all-methods #_[:get :patch :trace :connect :delete :head :post :options :put] [:get :post]) @@ -76,7 +74,9 @@ (apply conj [url new-opt-map] (map #(xiana-route->reitit-route % all-methods) nested-routes))))) -(defn xiana-routes->reitit-routes [routes all-methods] +(defn xiana-routes->reitit-routes + "Transforms routes to the proper reitit form." + [routes all-methods] (vec (keep #(xiana-route->reitit-route % all-methods) routes))) @@ -106,21 +106,27 @@ (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) (defn transform-path - [[p _ c] router] - (when-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] - [(swagger-path p (r/options router)) endpoint])) + "Transform a path of a compiled route to swagger format." + [[path _ api-verb-map] router] + (when-let [endpoint (some->> api-verb-map (keep transform-endpoint) (seq) (into {}))] + [(swagger-path path (r/options router)) endpoint])) -(defn routes->swagger-data [routes & {route-opt-map :route-opt-map}] +(defn routes->swagger-data + "Creates the json representation of the routes " + [routes & {route-opt-map :route-opt-map}] (let [router (ring/router routes (or route-opt-map {})) swagger {:swagger "2.0" :x-id ::default} map-in-order #(->> % (apply concat) (apply array-map)) - paths (->> router (r/compiled-routes) + paths (->> router + (r/compiled-routes) (map #(transform-path % router)) map-in-order)] (meta-merge swagger {:paths paths}))) -(defn ->default-internal-swagger-ui-html [config] +(defn ->default-internal-swagger-ui-html + "Generate the html for swagger UI" + [config] (let [schema-protocol (get-in config [:deps :xiana/web-server :protocol] :http) swagger-json-uri-path (get-in config [:deps :xiana/swagger :uri-path])] (h/html [:html {:lang "en"} @@ -210,10 +216,11 @@ ring.util.response/response (ring.util.response/header "Content-Type" "application/json; charset=utf-8"))))}])]) -(defn routes->swagger-json [routes - & {type :type - render? :render? - route-opt-map :route-opt-map}] +(defn routes->swagger-json + "Create swagger.json for all methods for each endpoint" + [routes & {type :type + render? :render? + route-opt-map :route-opt-map}] (let [reitit-routes (xiana-routes->reitit-routes routes all-methods)] (if (and render? (= type :json)) (json/write-value-as-string (routes->swagger-data reitit-routes :route-opt-map route-opt-map)) @@ -228,7 +235,7 @@ (every? some? ((juxt :xiana/swagger-ui :xiana/swagger) config))) (defn ->swagger-data - "Update routes for swagger data generation." + "Update config with swagger data" [config] (let [internal? true render? true From eca9ddac8b0c9274534a010b3e04b7d4e9c2dda9 Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Wed, 27 Sep 2023 13:35:48 -0300 Subject: [PATCH 32/34] Fix clj-kondo --- src/xiana/swagger.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index d475f0d7..708f0a27 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -237,8 +237,7 @@ (defn ->swagger-data "Update config with swagger data" [config] - (let [internal? true - render? true + (let [render? true type :json config (update-in config [:xiana/swagger :data] eval) route-opt-map {:data (get-in config [:xiana/swagger :data])} From 33526635efe52dd9fff168a8b2b883c1129a410f Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Fri, 29 Sep 2023 18:32:38 -0300 Subject: [PATCH 33/34] Move swagger tests to proper namespace --- test/xiana/route_test.clj | 57 +----------------------- test/xiana/swagger_test.clj | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 56 deletions(-) create mode 100644 test/xiana/swagger_test.clj diff --git a/test/xiana/route_test.clj b/test/xiana/route_test.clj index 9db8f6a4..446d9a49 100644 --- a/test/xiana/route_test.clj +++ b/test/xiana/route_test.clj @@ -99,59 +99,4 @@ ;; verify if action has the expected value (is (= action expected)))) -(deftest swagger-route-data-generation - (testing "Swagger Data generation from Routes" - (testing "Swagger Data from Empty Route" - (let [generated (-> sample-routes-without-action - :routes - (xsw/routes->swagger-json :type :edn)) - count-of-generated-routes-data (-> generated - :paths - keys - count)] - (is generated) - (is (zero? count-of-generated-routes-data)))) - (testing "Swagger Data from Sampel Route /w handler" - (let [generated-swagger-data (-> sample-routes-with-handler - :routes - (xsw/routes->swagger-json :type :edn))] - (testing "One swagger route for one route entry?" - (let [generated-route-count (->> - generated-swagger-data - :paths - keys - count)] - (is (= generated-route-count - 1)))) - (testing "Actions should generate every methods" - (let [index-generated-methods-by-sample (->> - generated-swagger-data - :paths - (#(get % "/")) - keys - vec)] - (is (= - index-generated-methods-by-sample - xsw/all-methods)))))) - (testing "Swagger Data from Sample Route /w action" - (let [generated-swagger-data (-> sample-routes - :routes - (xsw/routes->swagger-json :type :edn))] - (testing "One swagger route for one route entry?" - (let [generated-route-count (->> - generated-swagger-data - :paths - keys - count)] - (is (= generated-route-count - 1)))) - (testing "Actions should generate every methods" - (let [index-generated-methods-by-sample (->> - generated-swagger-data - :paths - (#(get % "/")) - keys - vec)] - (is (= - index-generated-methods-by-sample - xsw/all-methods)))))))) + diff --git a/test/xiana/swagger_test.clj b/test/xiana/swagger_test.clj new file mode 100644 index 00000000..5bfef24c --- /dev/null +++ b/test/xiana/swagger_test.clj @@ -0,0 +1,89 @@ +(ns xiana.swagger-test + (:require [xiana.swagger :as sut] + [clojure.test :as t :refer [deftest is testing]])) + +(def sample-routes + "Sample routes structure." + {:routes [["/" {:action :action}]]}) + +(def sample-routes-with-handler + "Sample routes structure." + {:routes [["/" {:handler :handler}]]}) + +(def sample-routes-without-action + "Sample routes structure (without action or handler)." + {:routes [["/" {}]]}) + +(deftest swagger-route-data-generation + (testing "Swagger Data generation from Routes\n" + (testing "Swagger Data from Empty Route" + (let [generated (-> [] + :routes + (sut/routes->swagger-json :type :edn)) + count-of-generated-routes-data (count generated)] + (is generated) + (is (zero? count-of-generated-routes-data)))) + + (testing "Swagger Data from Sample Route /w handle\n" + (let [generated-swagger-data (-> sample-routes-with-handler + :routes + (sut/routes->swagger-json :render? true :type :edn))] + (testing "One swagger route for one route entry?" + (let [generated-route-count (-> generated-swagger-data + :paths + count)] + (is (= generated-route-count 1))) +) + (testing "Actions should generate every methods" + (let [index-generated-methods-by-sample (-> + generated-swagger-data + :paths + (get "/") + keys + set)] + (is (= index-generated-methods-by-sample + (set sut/all-methods))))))) + + (testing "Swagger Data from Sample Route /w action" + (let [generated-swagger-data (-> sample-routes + :routes + (sut/routes->swagger-json :render? true :type :edn))] + (testing "One swagger route for one route entry?" + (let [generated-route-count (-> + generated-swagger-data + :paths + keys + count)] + (is (= generated-route-count + 1)))) + (testing "Actions should generate every methods" + (let [index-generated-methods-by-sample (-> + generated-swagger-data + :paths + (get "/") + keys + set)] + (is (= + index-generated-methods-by-sample + (set sut/all-methods))))))))) + + + + + +(comment +; (def all-methods [:get :post]) + ;; (deftest routes + ;; (testing "single-route" + ;; (= ["/" + ;; {:action :swagger-ui + ;; :some-values true + ;; :get + ;; {:handler #'identity :action :swagger-ui} + ;; :post + ;; {:handler #'identity :action :swagger-ui}}] + ;; (sut/xiana-route->reitit-route + ;; all-methods + ;; ["/" {:action :swagger-ui + ;; :some-values true}])))) + ) From bbeaaedc8d294aadd79e2a6049009df1fb0140c5 Mon Sep 17 00:00:00 2001 From: Gustavo Valente Date: Fri, 29 Sep 2023 18:33:07 -0300 Subject: [PATCH 34/34] Fix code to pass tests --- src/xiana/swagger.clj | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/xiana/swagger.clj b/src/xiana/swagger.clj index 708f0a27..87640d25 100644 --- a/src/xiana/swagger.clj +++ b/src/xiana/swagger.clj @@ -13,9 +13,8 @@ [reitit.trie :as trie] [ring.util.response])) -(defonce all-methods - #_[:get :patch :trace :connect :delete :head :post :options :put] - [:get :post]) +(def all-methods + [:get :patch :trace :connect :delete :head :post :options :put]) (defn xiana-route->reitit-route "xiana-route->reitit-route is taking route entry of our custom shape of routes @@ -222,8 +221,14 @@ render? :render? route-opt-map :route-opt-map}] (let [reitit-routes (xiana-routes->reitit-routes routes all-methods)] - (if (and render? (= type :json)) - (json/write-value-as-string (routes->swagger-data reitit-routes :route-opt-map route-opt-map)) + (if render? + (let [swagger-data (routes->swagger-data reitit-routes :route-opt-map route-opt-map)] + (cond + (= type :json) + (json/write-value-as-string swagger-data) + + (= type :edn) + swagger-data)) reitit-routes))) (defn swagger-configs-there? @@ -254,3 +259,6 @@ (assoc config-key routes-swagger-data) (assoc :routes routes))) config))) + +(comment + (->swagger-data {}))