diff --git a/README.md b/README.md index d2724b4..98b496a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ ensuring that your response handling code is also correct. Examples are below. [![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-vcr.svg)](https://clojars.org/com.github.oliyh/martian-vcr) [![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-vcr)](https://cljdoc.org/d/com.github.oliyh/martian-vcr/CURRENT) ## Features -- Bootstrap an instance from just a OpenAPI/Swagger url or provide your own API mapping +- Bootstrap an instance from just a OpenAPI/Swagger url, a local definition file or provide your own API mapping - Modular with support for [clj-http](https://github.com/dakrone/clj-http), [clj-http-lite](https://github.com/hiredman/clj-http-lite), [httpkit](https://github.com/http-kit/http-kit), [hato](https://github.com/gnarroway/hato) (Clojure), [cljs-http](https://github.com/r0man/cljs-http) and [cljs-http-promise](https://github.com/oliyh/cljs-http-promise) (ClojureScript) - Build urls and request maps from code or generate and perform the request, returning the response - Validate requests and responses to ensure they are correct before the data leaves/enters your system @@ -127,6 +127,12 @@ like that provided by [pedestal-api](https://github.com/oliyh/pedestal-api): (get-in [:body :id]))])) ``` +Note that when calling `bootstrap-openapi` you can also provide a url to a local resource, e.g. `(martian-http/bootstrap-openapi "public/openapi.json")`. +For ClojureScript the file can only be read at compile time, so a slightly different form is required using the `martian.file/load-local-resource` macro: +```clj +(martian/bootstrap-openapi "https://sandbox.example.com" (load-local-resource "openapi-test.json") martian-http/default-opts) +``` + ## No Swagger, no problem Although bootstrapping against a remote OpenAPI or Swagger API using `bootstrap-openapi` is simplest diff --git a/babashka-http-client/src/martian/babashka/http_client.clj b/babashka-http-client/src/martian/babashka/http_client.clj index 409be86..41876ff 100644 --- a/babashka-http-client/src/martian/babashka/http_client.clj +++ b/babashka-http-client/src/martian/babashka/http_client.clj @@ -2,6 +2,7 @@ (:require [babashka.http-client :as http] [babashka.json :as json] [martian.core :as martian] + [martian.file :as file] [martian.interceptors :as interceptors] [martian.openapi :as openapi] [martian.yaml :as yaml] @@ -80,13 +81,16 @@ (defn bootstrap [api-root concise-handlers & [opts]] (martian/bootstrap api-root concise-handlers (merge default-opts opts))) -(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} get-swagger-opts]] - (let [body (:body (http/get url (normalize-request get-swagger-opts))) - definition (if (yaml/yaml-url? url) - (yaml/yaml->edn body) - (json/read-str body)) +(defn- load-definition [url load-opts] + (or (file/local-resource url) + (let [body (:body (http/get url (normalize-request load-opts)))] + (if (yaml/yaml-url? url) + (yaml/yaml->edn body) + (json/read-str body))))) + +(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} load-opts]] + (let [definition (load-definition url load-opts) base-url (openapi/base-url url server-url definition)] (martian/bootstrap-openapi base-url definition (merge default-opts opts)))) (def bootstrap-swagger bootstrap-openapi) - diff --git a/babashka-http-client/test/martian/babashka/http_client_test.clj b/babashka-http-client/test/martian/babashka/http_client_test.clj index fe5eec0..8658952 100644 --- a/babashka-http-client/test/martian/babashka/http_client_test.clj +++ b/babashka-http-client/test/martian/babashka/http_client_test.clj @@ -160,6 +160,12 @@ (is (contains? (set (map first (martian/explore m))) :get-order-by-id)))) +(deftest local-file-test + (let [m (martian-http/bootstrap-openapi "public/openapi-test.json")] + (is (= "https://sandbox.example.com" (:api-root m))) + (is (= [[:list-items "Gets a list of items."]] + (martian/explore m))))) + (if-bb nil (deftest babashka-test diff --git a/clj-http-lite/src/martian/clj_http_lite.clj b/clj-http-lite/src/martian/clj_http_lite.clj index 171de7c..24f6be5 100644 --- a/clj-http-lite/src/martian/clj_http_lite.clj +++ b/clj-http-lite/src/martian/clj_http_lite.clj @@ -1,6 +1,7 @@ (ns martian.clj-http-lite (:require [clj-http.lite.client :as http] [cheshire.core :as json] + [martian.file :as file] [martian.yaml :as yaml] [martian.core :as martian] [martian.interceptors :as interceptors] @@ -24,12 +25,16 @@ (defn bootstrap [api-root concise-handlers & [opts]] (martian/bootstrap api-root concise-handlers (merge default-opts opts))) -(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} get-swagger-opts]] - (let [body (:body (http/get url (or get-swagger-opts {}))) - ;; clj-http-lite does not support {:as :json} body conversion (yet) so we do it right here - definition (if (yaml/yaml-url? url) - (yaml/yaml->edn body) - (json/parse-string body keyword)) +(defn- load-definition [url load-opts] + (or (file/local-resource url) + (let [body (:body (http/get url (or load-opts {})))] + (if (yaml/yaml-url? url) + (yaml/yaml->edn body) + ;; clj-http-lite does not support {:as :json} body conversion (yet) so we do it right here + (json/parse-string body keyword))))) + +(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} load-opts]] + (let [definition (load-definition url load-opts) base-url (openapi/base-url url server-url definition)] (martian/bootstrap-openapi base-url definition (merge default-opts opts)))) diff --git a/clj-http-lite/test/martian/clj_http_lite_test.clj b/clj-http-lite/test/martian/clj_http_lite_test.clj index e648f74..7dbcabf 100644 --- a/clj-http-lite/test/martian/clj_http_lite_test.clj +++ b/clj-http-lite/test/martian/clj_http_lite_test.clj @@ -66,3 +66,9 @@ (is (contains? (set (map first (martian/explore m))) :get-order-by-id)))) + +(deftest local-file-test + (let [m (martian-http/bootstrap-openapi "public/openapi-test.json")] + (is (= "https://sandbox.example.com" (:api-root m))) + (is (= [[:list-items "Gets a list of items."]] + (martian/explore m))))) diff --git a/clj-http/src/martian/clj_http.clj b/clj-http/src/martian/clj_http.clj index dd1422f..4912277 100644 --- a/clj-http/src/martian/clj_http.clj +++ b/clj-http/src/martian/clj_http.clj @@ -1,6 +1,7 @@ (ns martian.clj-http (:require [clj-http.client :as http] [martian.core :as martian] + [martian.file :as file] [martian.interceptors :as interceptors] [martian.openapi :as openapi] [martian.yaml :as yaml])) @@ -18,10 +19,14 @@ (defn bootstrap [api-root concise-handlers & [opts]] (martian/bootstrap api-root concise-handlers (merge default-opts opts))) -(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} get-swagger-opts]] - (let [definition (if (yaml/yaml-url? url) - (yaml/yaml->edn (:body (http/get url (dissoc get-swagger-opts :as)))) - (:body (http/get url (merge {:as :json} get-swagger-opts)))) +(defn- load-definition [url load-opts] + (or (file/local-resource url) + (if (yaml/yaml-url? url) + (yaml/yaml->edn (:body (http/get url (dissoc load-opts :as)))) + (:body (http/get url (merge {:as :json} load-opts)))))) + +(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} load-opts]] + (let [definition (load-definition url load-opts) base-url (openapi/base-url url server-url definition)] (martian/bootstrap-openapi base-url definition (merge default-opts opts)))) diff --git a/clj-http/test/martian/clj_http_test.clj b/clj-http/test/martian/clj_http_test.clj index 18632bf..deae4d8 100644 --- a/clj-http/test/martian/clj_http_test.clj +++ b/clj-http/test/martian/clj_http_test.clj @@ -67,3 +67,9 @@ (is (contains? (set (map first (martian/explore m))) :get-order-by-id)))) + +(deftest local-file-test + (let [m (martian-http/bootstrap-openapi "public/openapi-test.json")] + (is (= "https://sandbox.example.com" (:api-root m))) + (is (= [[:list-items "Gets a list of items."]] + (martian/explore m))))) diff --git a/cljs-http-promise/src/martian/cljs_http_promise.cljs b/cljs-http-promise/src/martian/cljs_http_promise.cljs index 3d1af1f..9f3f350 100644 --- a/cljs-http-promise/src/martian/cljs_http_promise.cljs +++ b/cljs-http-promise/src/martian/cljs_http_promise.cljs @@ -28,8 +28,8 @@ (defn bootstrap [api-root concise-handlers & [opts]] (martian/bootstrap api-root concise-handlers (merge default-opts opts))) -(defn bootstrap-openapi [url & [{:keys [server-url trim-base-url?] :as opts}]] - (prom/then (http/get url {:as :json}) +(defn bootstrap-openapi [url & [{:keys [server-url trim-base-url?] :as opts} load-opts]] + (prom/then (http/get url (merge {:as :json} load-opts)) (fn [response] (let [definition (:body response) raw-base-url (openapi/base-url url server-url definition) diff --git a/cljs-http-promise/test/martian/cljs_http_promise_test.cljs b/cljs-http-promise/test/martian/cljs_http_promise_test.cljs index 31206e0..947f615 100644 --- a/cljs-http-promise/test/martian/cljs_http_promise_test.cljs +++ b/cljs-http-promise/test/martian/cljs_http_promise_test.cljs @@ -2,7 +2,8 @@ (:require [martian.cljs-http-promise :as martian-http] [martian.core :as martian] [cljs.test :refer-macros [deftest is async]] - [promesa.core :as prom])) + [promesa.core :as prom]) + (:require-macros [martian.file :refer [load-local-resource]])) (def swagger-url "http://localhost:8888/swagger.json") (def openapi-url "http://localhost:8888/openapi.json") @@ -52,3 +53,9 @@ :get-order-by-id))) (prom/finally (fn [] (done)))))) + +(deftest local-file-test + (let [m (martian/bootstrap-openapi "https://sandbox.example.com" (load-local-resource "public/openapi-test.json") martian-http/default-opts)] + (is (= "https://sandbox.example.com" (:api-root m))) + (is (= [[:list-items "Gets a list of items."]] + (martian/explore m))))) diff --git a/cljs-http/src/martian/cljs_http.cljs b/cljs-http/src/martian/cljs_http.cljs index cf91b7a..700390b 100644 --- a/cljs-http/src/martian/cljs_http.cljs +++ b/cljs-http/src/martian/cljs_http.cljs @@ -3,7 +3,7 @@ [cljs.core.async :refer [ (or (io/resource url) + (let [f (io/file url)] + (when (.exists f) + f))) + slurp)] + (cond + (yaml/yaml-url? url) (yaml/yaml->edn r) + (str/ends-with? url ".edn") (read-string r) + (str/ends-with? url ".json") (json/decode r keyword)))) + +(defmacro load-local-resource [url] + (local-resource url)) diff --git a/hato/src/martian/hato.clj b/hato/src/martian/hato.clj index 1a582be..9a18eed 100644 --- a/hato/src/martian/hato.clj +++ b/hato/src/martian/hato.clj @@ -1,6 +1,7 @@ (ns martian.hato (:require [hato.client :as http] [martian.core :as martian] + [martian.file :as file] [martian.interceptors :as interceptors] [martian.openapi :as openapi] [martian.yaml :as yaml] @@ -55,10 +56,14 @@ (defn bootstrap [api-root concise-handlers & [opts]] (martian/bootstrap api-root concise-handlers (merge default-opts opts))) -(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} get-swagger-opts]] - (let [definition (if (yaml/yaml-url? url) - (yaml/yaml->edn (:body (http/get url (dissoc get-swagger-opts :as)))) - (:body (http/get url (merge {:as :json} get-swagger-opts)))) +(defn- load-definition [url load-opts] + (or (file/local-resource url) + (if (yaml/yaml-url? url) + (yaml/yaml->edn (:body (http/get url (dissoc load-opts :as)))) + (:body (http/get url (merge {:as :json} load-opts)))))) + +(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} load-opts]] + (let [definition (load-definition url load-opts) base-url (openapi/base-url url server-url definition)] (martian/bootstrap-openapi base-url definition (merge default-opts opts)))) diff --git a/hato/test/martian/hato_test.clj b/hato/test/martian/hato_test.clj index 34bba10..c7068db 100644 --- a/hato/test/martian/hato_test.clj +++ b/hato/test/martian/hato_test.clj @@ -122,3 +122,9 @@ (is (contains? (set (map first (martian/explore m))) :get-order-by-id)))) + +(deftest local-file-test + (let [m (martian-http/bootstrap-openapi "public/openapi-test.json")] + (is (= "https://sandbox.example.com" (:api-root m))) + (is (= [[:list-items "Gets a list of items."]] + (martian/explore m))))) diff --git a/httpkit/src/martian/httpkit.clj b/httpkit/src/martian/httpkit.clj index 33e6d29..af9766b 100644 --- a/httpkit/src/martian/httpkit.clj +++ b/httpkit/src/martian/httpkit.clj @@ -1,6 +1,7 @@ (ns martian.httpkit (:require [org.httpkit.client :as http] [martian.core :as martian] + [martian.file :as file] [martian.interceptors :as interceptors] [martian.openapi :as openapi] [martian.yaml :as yaml] @@ -29,13 +30,17 @@ (defn bootstrap [api-root concise-handlers & [opts]] (martian/bootstrap api-root concise-handlers (merge default-opts opts))) -(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} get-swagger-opts]] - (let [definition @(http/get url - (merge {:as :text} get-swagger-opts) - (fn [{:keys [body]}] - (if (yaml/yaml-url? url) - (yaml/yaml->edn body) - (json/decode body keyword)))) +(defn- load-definition [url load-opts] + (or (file/local-resource url) + @(http/get url + (merge {:as :text} load-opts) + (fn [{:keys [body]}] + (if (yaml/yaml-url? url) + (yaml/yaml->edn body) + (json/decode body keyword)))))) + +(defn bootstrap-openapi [url & [{:keys [server-url] :as opts} load-opts]] + (let [definition (load-definition url load-opts) base-url (openapi/base-url url server-url definition)] (martian/bootstrap-openapi base-url definition (merge default-opts opts)))) diff --git a/httpkit/test/martian/httpkit_test.clj b/httpkit/test/martian/httpkit_test.clj index 58c5952..dce2f88 100644 --- a/httpkit/test/martian/httpkit_test.clj +++ b/httpkit/test/martian/httpkit_test.clj @@ -67,3 +67,9 @@ (is (contains? (set (map first (martian/explore m))) :get-order-by-id)))) + +(deftest local-file-test + (let [m (martian-http/bootstrap-openapi "public/openapi-test.json")] + (is (= "https://sandbox.example.com" (:api-root m))) + (is (= [[:list-items "Gets a list of items."]] + (martian/explore m)))))