Skip to content

Commit afc1a93

Browse files
committed
squashed
1 parent a8cf4d6 commit afc1a93

File tree

12 files changed

+1511
-455
lines changed

12 files changed

+1511
-455
lines changed

src/compojure/api/api.clj

+42-17
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
(ns compojure.api.api
22
(:require [compojure.api.core :as c]
33
[compojure.api.swagger :as swagger]
4-
[compojure.api.middleware :as middleware]
4+
[compojure.api.middleware :as mw]
5+
[compojure.api.request :as request]
56
[compojure.api.routes :as routes]
67
[compojure.api.common :as common]
7-
[compojure.api.coerce :as coerce]
8+
[compojure.api.request :as request]
89
[ring.swagger.common :as rsc]
910
[ring.swagger.middleware :as rsm]))
1011

1112
(def api-defaults
1213
(merge
13-
middleware/api-middleware-defaults
14+
mw/api-middleware-defaults
1415
{:api {:invalid-routes-fn routes/log-invalid-child-routes
1516
:disable-api-middleware? false}
1617
:swagger {:ui nil, :spec nil}}))
@@ -23,8 +24,7 @@
2324
options map as the first parameter:
2425
2526
(api
26-
{:formats [:json-kw :edn :transit-msgpack :transit-json]
27-
:exceptions {:handlers {:compojure.api.exception/default my-logging-handler}}
27+
{:exceptions {:handlers {:compojure.api.exception/default my-logging-handler}}
2828
:api {:invalid-routes-fn (constantly nil)}
2929
:swagger {:spec \"/swagger.json\"
3030
:ui \"/api-docs\"
@@ -47,30 +47,55 @@
4747
4848
### api-middleware options
4949
50+
See `compojure.api.middleware/api-middleware` for more available options.
51+
5052
" (:doc (meta #'compojure.api.middleware/api-middleware)))}
5153
api
5254
[& body]
5355
(let [[options handlers] (common/extract-parameters body false)
56+
_ (assert (not (contains? options :format))
57+
(str "ERROR: Option [:format] is not used with 2.* version.\n"
58+
"Compojure-api uses now Muuntaja insted of ring-middleware-format,\n"
59+
"the new formatting options for it should be under [:formats]. See\n"
60+
"[[api-middleware]] documentation for more details.\n"))
61+
_ (when (and (not (:formatter options))
62+
(not (contains? options :formats))
63+
(not (System/getProperty "compojure.api.middleware.global-default-formatter")))
64+
(throw (ex-info (str "ERROR: Please set `:formatter :muuntaja` in the options map of `api`.\n"
65+
"e.g., (api {:formatter :muuntaja} routes...)\n"
66+
"To prepare for backwards compatibility with compojure-api 1.x, the formatting library must be\n"
67+
"explicitly chosen if not configured by `:format` (ring-middleware-format) or \n"
68+
"`:formats` (muuntaja). Once 2.x is stable, the default will be `:formatter :ring-middleware-format`.\n"
69+
"To globally override the formatter, use -Dcompojure.api.middleware.global-default-formatter=:muuntaja")
70+
{})))
5471
options (rsc/deep-merge api-defaults options)
5572
handler (apply c/routes (concat [(swagger/swagger-routes (:swagger options))] handlers))
56-
routes (routes/get-routes handler (:api options))
73+
partial-api-route (routes/map->Route
74+
{:childs [handler]
75+
:info {:coercion (:coercion options)}})
76+
routes (routes/get-routes partial-api-route (:api options))
5777
paths (-> routes routes/ring-swagger-paths swagger/transform-operations)
5878
lookup (routes/route-lookup-table routes)
5979
swagger-data (get-in options [:swagger :data])
6080
enable-api-middleware? (not (get-in options [:api :disable-api-middleware?]))
61-
api-handler (cond-> handler
62-
swagger-data (rsm/wrap-swagger-data swagger-data)
63-
enable-api-middleware? (middleware/api-middleware
64-
(dissoc options :api :swagger))
65-
true (middleware/wrap-options
66-
{:paths paths
67-
:coercer (coerce/memoized-coercer)
68-
:lookup lookup}))]
69-
(routes/create nil nil {} [handler] api-handler)))
81+
api-middleware-options (dissoc (mw/api-middleware-options (assoc (dissoc options :api :swagger) ::via-api true))
82+
::mw/api-middleware-defaults)
83+
api-handler (-> handler
84+
(cond-> swagger-data (rsm/wrap-swagger-data swagger-data))
85+
(cond-> enable-api-middleware? (mw/api-middleware
86+
api-middleware-options))
87+
(mw/wrap-inject-data
88+
{::request/paths paths
89+
::request/lookup lookup}))]
90+
(assoc partial-api-route :handler api-handler)))
7091

7192
(defmacro
72-
^{:doc (str
73-
"Defines an api.
93+
^{:superseded-by "api"
94+
:deprecated "2.0.0"
95+
:doc (str
96+
"Deprecated: please use (def name (api ...body..))
97+
98+
Defines an api.
7499
75100
API middleware options:
76101

src/compojure/api/coerce.clj

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
;; 1.1.x
12
(ns compojure.api.coerce
23
(:require [schema.coerce :as sc]
34
[compojure.api.middleware :as mw]

src/compojure/api/core.clj

+12-12
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@
5454
:deprecated "1.1.14"
5555
:superseded-by "route-middleware"}
5656
[middleware & body]
57-
(when (not= "true" (System/getProperty "compojure.api.core.suppress-middleware-warning"))
58-
(println (str "compojure.api.core.middleware is deprecated because of security issues. "
59-
"Please use route-middleware instead. middleware will be disabled in a future release."
60-
"Set -dcompojure.api.core.suppress-middleware-warning=true to suppress this warning.")))
57+
(assert (= "true" (System/getProperty "compojure.api.core.allow-dangerous-middleware"))
58+
(str "compojure.api.core.middleware is deprecated because of security issues. "
59+
"Please use route-middleware instead. "
60+
"Set compojure.api.core.allow-dangerous-middleware=true to keep using middleware."))
6161
`(let [body# (routes ~@body)
6262
wrap-mw# (mw/compose-middleware ~middleware)]
6363
(routes/create nil nil {} [body#] (wrap-mw# body#))))
6464

6565
(defn route-middleware
66-
"Wraps routes with given middleware using thread-first macro."
66+
"Wraps routes with given middlewares using thread-first macro."
6767
{:style/indent 1
6868
:supercedes "middleware"}
6969
[middleware & body]
@@ -76,11 +76,11 @@
7676

7777
(defmacro context {:style/indent 2} [& args] (meta/restructure nil args {:context? true :&form &form :&env &env}))
7878

79-
(defmacro GET {:style/indent 2} [& args] (meta/restructure :get args nil))
80-
(defmacro ANY {:style/indent 2} [& args] (meta/restructure nil args nil))
81-
(defmacro HEAD {:style/indent 2} [& args] (meta/restructure :head args nil))
82-
(defmacro PATCH {:style/indent 2} [& args] (meta/restructure :patch args nil))
83-
(defmacro DELETE {:style/indent 2} [& args] (meta/restructure :delete args nil))
79+
(defmacro GET {:style/indent 2} [& args] (meta/restructure :get args nil))
80+
(defmacro ANY {:style/indent 2} [& args] (meta/restructure nil args nil))
81+
(defmacro HEAD {:style/indent 2} [& args] (meta/restructure :head args nil))
82+
(defmacro PATCH {:style/indent 2} [& args] (meta/restructure :patch args nil))
83+
(defmacro DELETE {:style/indent 2} [& args] (meta/restructure :delete args nil))
8484
(defmacro OPTIONS {:style/indent 2} [& args] (meta/restructure :options args nil))
85-
(defmacro POST {:style/indent 2} [& args] (meta/restructure :post args nil))
86-
(defmacro PUT {:style/indent 2} [& args] (meta/restructure :put args nil))
85+
(defmacro POST {:style/indent 2} [& args] (meta/restructure :post args nil))
86+
(defmacro PUT {:style/indent 2} [& args] (meta/restructure :put args nil))

src/compojure/api/exception.clj

+49-30
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
(:require [ring.util.http-response :as response]
33
[clojure.walk :as walk]
44
[compojure.api.impl.logging :as logging]
5-
[schema.utils :as su])
6-
(:import [schema.utils ValidationError NamedError]
7-
[com.fasterxml.jackson.core JsonParseException]
8-
[org.yaml.snakeyaml.parser ParserException]))
5+
[compojure.api.coercion.core :as cc]
6+
[compojure.api.coercion.schema]))
97

108
;;
119
;; Default exception handlers
@@ -21,42 +19,62 @@
2119
(response/internal-server-error {:type "unknown-exception"
2220
:class (.getName (.getClass e))}))
2321

24-
(defn stringify-error
25-
"Stringifies symbols and validation errors in Schema error, keeping the structure intact."
26-
[error]
27-
(walk/postwalk
28-
(fn [x]
29-
(cond
30-
(instance? ValidationError x) (str (su/validation-error-explain x))
31-
(instance? NamedError x) (str (su/named-error-explain x))
32-
:else x))
33-
error))
34-
22+
;; TODO: coercion should handle how to publish data
3523
(defn response-validation-handler
36-
"Creates error response based on Schema error."
24+
"Creates error response based on a response error. The following keys are available:
25+
26+
:type type of the exception (::response-validation)
27+
:coercion coercion instance used
28+
:in location of the value ([:response :body])
29+
:schema schema to be validated against
30+
:error schema error
31+
:request raw request
32+
:response raw response"
3733
[e data req]
38-
(response/internal-server-error {:errors (stringify-error (su/error-val data))}))
34+
(response/internal-server-error
35+
(-> data
36+
(dissoc :request :response)
37+
(update :coercion cc/get-name)
38+
(assoc :value (-> data :response :body))
39+
(->> (cc/encode-error (:coercion data))))))
3940

41+
;; TODO: coercion should handle how to publish data
4042
(defn request-validation-handler
41-
"Creates error response based on Schema error."
43+
"Creates error response based on Schema error. The following keys are available:
44+
45+
:type type of the exception (::request-validation)
46+
:coercion coercion instance used
47+
:value value that was validated
48+
:in location of the value (e.g. [:request :query-params])
49+
:schema schema to be validated against
50+
:error schema error
51+
:request raw request"
4252
[e data req]
43-
(response/bad-request {:errors (stringify-error (su/error-val data))}))
53+
(response/bad-request
54+
(-> data
55+
(dissoc :request)
56+
(update :coercion cc/get-name)
57+
(->> (cc/encode-error (:coercion data))))))
58+
59+
(defn http-response-handler
60+
"reads response from ex-data :response"
61+
[_ {:keys [response]} _]
62+
response)
4463

4564
(defn schema-error-handler
4665
"Creates error response based on Schema error."
4766
[e data req]
48-
; FIXME: Why error is not wrapped to ErrorContainer here?
49-
(response/bad-request {:errors (stringify-error (:error data))}))
67+
(response/bad-request
68+
{:errors (compojure.api.coercion.schema/stringify (:error data))}))
5069

5170
(defn request-parsing-handler
5271
[^Exception ex data req]
53-
(let [cause (.getCause ex)]
54-
(response/bad-request {:type (cond
55-
(instance? JsonParseException cause) "json-parse-exception"
56-
(instance? ParserException cause) "yaml-parse-exception"
57-
:else "parse-exception")
58-
:message (.getMessage cause)})))
59-
72+
(let [cause (.getCause ex)
73+
original (.getCause cause)]
74+
(response/bad-request
75+
(merge (select-keys data [:type :format :charset])
76+
(if original {:original (.getMessage original)})
77+
{:message (.getMessage cause)}))))
6078
;;
6179
;; Logging
6280
;;
@@ -75,5 +93,6 @@
7593
;; Mappings from other Exception types to our base types
7694
;;
7795

78-
(def legacy-exception-types
79-
{:ring.swagger.schema/validation ::request-validation})
96+
(def mapped-exception-types
97+
{:ring.swagger.schema/validation ::request-validation
98+
:muuntaja/decode ::request-parsing})

src/compojure/api/impl/logging.clj

+11-10
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@
66
(declare log!)
77

88
;; use c.t.l logging if available, default to console logging
9-
(if (find-ns 'clojure.tools.logging)
9+
(try
1010
(eval
1111
`(do
1212
(require 'clojure.tools.logging)
1313
(defmacro ~'log! [& ~'args]
14-
`(do
15-
(clojure.tools.logging/log ~@~'args)))))
16-
(let [log (fn [level more] (println (.toUpperCase (name level)) (str/join " " more)))]
17-
(defn log! [level x & more]
18-
(if (instance? Throwable x)
19-
(do
20-
(log level more)
21-
(.printStackTrace ^Throwable x))
22-
(log level (into [x] more))))))
14+
`(clojure.tools.logging/log ~@~'args))))
15+
(catch Exception _
16+
(let [log (fn [level more] (println (.toUpperCase (name level)) (str/join " " more)))]
17+
(defn log! [level x & more]
18+
(if (instance? Throwable x)
19+
(do
20+
(log level more)
21+
(.printStackTrace ^Throwable x))
22+
(log level (into [x] more))))
23+
(log! :warn "clojure.tools.logging not found on classpath, compojure.api logging to console."))))

0 commit comments

Comments
 (0)