Skip to content

Commit

Permalink
experimenting with local file definition for #174 (#175)
Browse files Browse the repository at this point in the history
* api for local file definition for #174
  • Loading branch information
oliyh authored Apr 17, 2023
1 parent 51798d3 commit 10bef14
Show file tree
Hide file tree
Showing 16 changed files with 130 additions and 37 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
16 changes: 10 additions & 6 deletions babashka-http-client/src/martian/babashka/http_client.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 11 additions & 6 deletions clj-http-lite/src/martian/clj_http_lite.clj
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -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))))

Expand Down
6 changes: 6 additions & 0 deletions clj-http-lite/test/martian/clj_http_lite_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))
13 changes: 9 additions & 4 deletions clj-http/src/martian/clj_http.clj
Original file line number Diff line number Diff line change
@@ -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]))
Expand All @@ -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))))

Expand Down
6 changes: 6 additions & 0 deletions clj-http/test/martian/clj_http_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))
4 changes: 2 additions & 2 deletions cljs-http-promise/src/martian/cljs_http_promise.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion cljs-http-promise/test/martian/cljs_http_promise_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)))))
6 changes: 3 additions & 3 deletions cljs-http/src/martian/cljs_http.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[cljs.core.async :refer [<!]]
[martian.core :as martian]
[martian.interceptors :as i]
[martian.openapi :refer [openapi-schema?] :as openapi]
[martian.openapi :as openapi]
[tripod.context :as tc]
[clojure.string :as string])
(:require-macros [cljs.core.async.macros :refer [go]]))
Expand All @@ -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} get-swagger-opts]]
(go (let [definition (:body (<! (http/get url (merge {:as :json} get-swagger-opts))))
(defn bootstrap-openapi [url & [{:keys [server-url trim-base-url?] :as opts} load-opts]]
(go (let [definition (:body (<! (http/get url (merge {:as :json} load-opts))))
raw-base-url (openapi/base-url url server-url definition)
base-url (if trim-base-url?
(string/replace raw-base-url #"/$" "")
Expand Down
13 changes: 10 additions & 3 deletions cljs-http/test/martian/cljs_http_test.cljs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
(ns martian.cljs-http-test
(:require [martian.cljs-http :as martian-http]
[martian.core :as martian]
[cljs.test :refer-macros [deftest testing is run-tests async]]
[cljs.core.async :refer [<! timeout]])
(:require-macros [cljs.core.async.macros :refer [go]]))
[cljs.test :refer-macros [deftest is async]]
[cljs.core.async :refer [<!]])
(:require-macros [cljs.core.async.macros :refer [go]]
[martian.file :refer [load-local-resource]]))

(def swagger-url "http://localhost:8888/swagger.json")
(def openapi-url "http://localhost:8888/openapi.json")
Expand Down Expand Up @@ -50,3 +51,9 @@
(is (contains? (set (map first (martian/explore m)))
:get-order-by-id)))
(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)))))
19 changes: 19 additions & 0 deletions core/src/martian/file.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(ns martian.file
(:require [clojure.java.io :as io]
[martian.yaml :as yaml]
[clojure.string :as str]
[cheshire.core :as json]))

(defn local-resource [url]
(when-let [r (some-> (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))
13 changes: 9 additions & 4 deletions hato/src/martian/hato.clj
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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))))

Expand Down
6 changes: 6 additions & 0 deletions hato/test/martian/hato_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))
19 changes: 12 additions & 7 deletions httpkit/src/martian/httpkit.clj
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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))))

Expand Down
6 changes: 6 additions & 0 deletions httpkit/test/martian/httpkit_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))

0 comments on commit 10bef14

Please sign in to comment.