Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Squash 2.x changes for diff purposes #471

Draft
wants to merge 1 commit into
base: 1.1.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions src/compojure/api/api.clj
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
(ns compojure.api.api
(:require [compojure.api.core :as c]
[compojure.api.swagger :as swagger]
[compojure.api.middleware :as middleware]
[compojure.api.middleware :as mw]
[compojure.api.request :as request]
[compojure.api.routes :as routes]
[compojure.api.common :as common]
[compojure.api.coerce :as coerce]
[compojure.api.request :as request]
[ring.swagger.common :as rsc]
[ring.swagger.middleware :as rsm]))

(def api-defaults
(merge
middleware/api-middleware-defaults
mw/api-middleware-defaults
{:api {:invalid-routes-fn routes/log-invalid-child-routes
:disable-api-middleware? false}
:swagger {:ui nil, :spec nil}}))
Expand All @@ -23,8 +24,7 @@
options map as the first parameter:

(api
{:formats [:json-kw :edn :transit-msgpack :transit-json]
:exceptions {:handlers {:compojure.api.exception/default my-logging-handler}}
{:exceptions {:handlers {:compojure.api.exception/default my-logging-handler}}
:api {:invalid-routes-fn (constantly nil)}
:swagger {:spec \"/swagger.json\"
:ui \"/api-docs\"
Expand All @@ -47,30 +47,55 @@

### api-middleware options

See `compojure.api.middleware/api-middleware` for more available options.

" (:doc (meta #'compojure.api.middleware/api-middleware)))}
api
[& body]
(let [[options handlers] (common/extract-parameters body false)
_ (assert (not (contains? options :format))
(str "ERROR: Option [:format] is not used with 2.* version.\n"
"Compojure-api uses now Muuntaja insted of ring-middleware-format,\n"
"the new formatting options for it should be under [:formats]. See\n"
"[[api-middleware]] documentation for more details.\n"))
_ (when (and (not (:formatter options))
(not (contains? options :formats))
(not (System/getProperty "compojure.api.middleware.global-default-formatter")))
(throw (ex-info (str "ERROR: Please set `:formatter :muuntaja` in the options map of `api`.\n"
"e.g., (api {:formatter :muuntaja} routes...)\n"
"To prepare for backwards compatibility with compojure-api 1.x, the formatting library must be\n"
"explicitly chosen if not configured by `:format` (ring-middleware-format) or \n"
"`:formats` (muuntaja). Once 2.x is stable, the default will be `:formatter :ring-middleware-format`.\n"
"To globally override the formatter, use -Dcompojure.api.middleware.global-default-formatter=:muuntaja")
{})))
options (rsc/deep-merge api-defaults options)
handler (apply c/routes (concat [(swagger/swagger-routes (:swagger options))] handlers))
routes (routes/get-routes handler (:api options))
partial-api-route (routes/map->Route
{:childs [handler]
:info {:coercion (:coercion options)}})
routes (routes/get-routes partial-api-route (:api options))
paths (-> routes routes/ring-swagger-paths swagger/transform-operations)
lookup (routes/route-lookup-table routes)
swagger-data (get-in options [:swagger :data])
enable-api-middleware? (not (get-in options [:api :disable-api-middleware?]))
api-handler (cond-> handler
swagger-data (rsm/wrap-swagger-data swagger-data)
enable-api-middleware? (middleware/api-middleware
(dissoc options :api :swagger))
true (middleware/wrap-options
{:paths paths
:coercer (coerce/memoized-coercer)
:lookup lookup}))]
(routes/create nil nil {} [handler] api-handler)))
api-middleware-options (dissoc (mw/api-middleware-options (assoc (dissoc options :api :swagger) ::via-api true))
::mw/api-middleware-defaults)
api-handler (-> handler
(cond-> swagger-data (rsm/wrap-swagger-data swagger-data))
(cond-> enable-api-middleware? (mw/api-middleware
api-middleware-options))
(mw/wrap-inject-data
{::request/paths paths
::request/lookup lookup}))]
(assoc partial-api-route :handler api-handler)))

(defmacro
^{:doc (str
"Defines an api.
^{:superseded-by "api"
:deprecated "2.0.0"
:doc (str
"Deprecated: please use (def name (api ...body..))

Defines an api.

API middleware options:

Expand Down
1 change: 1 addition & 0 deletions src/compojure/api/coerce.clj
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
;; 1.1.x
(ns compojure.api.coerce
(:require [schema.coerce :as sc]
[compojure.api.middleware :as mw]
Expand Down
24 changes: 12 additions & 12 deletions src/compojure/api/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@
:deprecated "1.1.14"
:superseded-by "route-middleware"}
[middleware & body]
(when (not= "true" (System/getProperty "compojure.api.core.suppress-middleware-warning"))
(println (str "compojure.api.core.middleware is deprecated because of security issues. "
"Please use route-middleware instead. middleware will be disabled in a future release."
"Set -dcompojure.api.core.suppress-middleware-warning=true to suppress this warning.")))
(assert (= "true" (System/getProperty "compojure.api.core.allow-dangerous-middleware"))
(str "compojure.api.core.middleware is deprecated because of security issues. "
"Please use route-middleware instead. "
"Set compojure.api.core.allow-dangerous-middleware=true to keep using middleware."))
`(let [body# (routes ~@body)
wrap-mw# (mw/compose-middleware ~middleware)]
(routes/create nil nil {} [body#] (wrap-mw# body#))))

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

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

(defmacro GET {:style/indent 2} [& args] (meta/restructure :get args nil))
(defmacro ANY {:style/indent 2} [& args] (meta/restructure nil args nil))
(defmacro HEAD {:style/indent 2} [& args] (meta/restructure :head args nil))
(defmacro PATCH {:style/indent 2} [& args] (meta/restructure :patch args nil))
(defmacro DELETE {:style/indent 2} [& args] (meta/restructure :delete args nil))
(defmacro GET {:style/indent 2} [& args] (meta/restructure :get args nil))
(defmacro ANY {:style/indent 2} [& args] (meta/restructure nil args nil))
(defmacro HEAD {:style/indent 2} [& args] (meta/restructure :head args nil))
(defmacro PATCH {:style/indent 2} [& args] (meta/restructure :patch args nil))
(defmacro DELETE {:style/indent 2} [& args] (meta/restructure :delete args nil))
(defmacro OPTIONS {:style/indent 2} [& args] (meta/restructure :options args nil))
(defmacro POST {:style/indent 2} [& args] (meta/restructure :post args nil))
(defmacro PUT {:style/indent 2} [& args] (meta/restructure :put args nil))
(defmacro POST {:style/indent 2} [& args] (meta/restructure :post args nil))
(defmacro PUT {:style/indent 2} [& args] (meta/restructure :put args nil))
79 changes: 49 additions & 30 deletions src/compojure/api/exception.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
(:require [ring.util.http-response :as response]
[clojure.walk :as walk]
[compojure.api.impl.logging :as logging]
[schema.utils :as su])
(:import [schema.utils ValidationError NamedError]
[com.fasterxml.jackson.core JsonParseException]
[org.yaml.snakeyaml.parser ParserException]))
[compojure.api.coercion.core :as cc]
[compojure.api.coercion.schema]))

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

(defn stringify-error
"Stringifies symbols and validation errors in Schema error, keeping the structure intact."
[error]
(walk/postwalk
(fn [x]
(cond
(instance? ValidationError x) (str (su/validation-error-explain x))
(instance? NamedError x) (str (su/named-error-explain x))
:else x))
error))

;; TODO: coercion should handle how to publish data
(defn response-validation-handler
"Creates error response based on Schema error."
"Creates error response based on a response error. The following keys are available:

:type type of the exception (::response-validation)
:coercion coercion instance used
:in location of the value ([:response :body])
:schema schema to be validated against
:error schema error
:request raw request
:response raw response"
[e data req]
(response/internal-server-error {:errors (stringify-error (su/error-val data))}))
(response/internal-server-error
(-> data
(dissoc :request :response)
(update :coercion cc/get-name)
(assoc :value (-> data :response :body))
(->> (cc/encode-error (:coercion data))))))

;; TODO: coercion should handle how to publish data
(defn request-validation-handler
"Creates error response based on Schema error."
"Creates error response based on Schema error. The following keys are available:

:type type of the exception (::request-validation)
:coercion coercion instance used
:value value that was validated
:in location of the value (e.g. [:request :query-params])
:schema schema to be validated against
:error schema error
:request raw request"
[e data req]
(response/bad-request {:errors (stringify-error (su/error-val data))}))
(response/bad-request
(-> data
(dissoc :request)
(update :coercion cc/get-name)
(->> (cc/encode-error (:coercion data))))))

(defn http-response-handler
"reads response from ex-data :response"
[_ {:keys [response]} _]
response)

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

(defn request-parsing-handler
[^Exception ex data req]
(let [cause (.getCause ex)]
(response/bad-request {:type (cond
(instance? JsonParseException cause) "json-parse-exception"
(instance? ParserException cause) "yaml-parse-exception"
:else "parse-exception")
:message (.getMessage cause)})))

(let [cause (.getCause ex)
original (.getCause cause)]
(response/bad-request
(merge (select-keys data [:type :format :charset])
(if original {:original (.getMessage original)})
{:message (.getMessage cause)}))))
;;
;; Logging
;;
Expand All @@ -75,5 +93,6 @@
;; Mappings from other Exception types to our base types
;;

(def legacy-exception-types
{:ring.swagger.schema/validation ::request-validation})
(def mapped-exception-types
{:ring.swagger.schema/validation ::request-validation
:muuntaja/decode ::request-parsing})
21 changes: 11 additions & 10 deletions src/compojure/api/impl/logging.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
(declare log!)

;; use c.t.l logging if available, default to console logging
(if (find-ns 'clojure.tools.logging)
(try
(eval
`(do
(require 'clojure.tools.logging)
(defmacro ~'log! [& ~'args]
`(do
(clojure.tools.logging/log ~@~'args)))))
(let [log (fn [level more] (println (.toUpperCase (name level)) (str/join " " more)))]
(defn log! [level x & more]
(if (instance? Throwable x)
(do
(log level more)
(.printStackTrace ^Throwable x))
(log level (into [x] more))))))
`(clojure.tools.logging/log ~@~'args))))
(catch Exception _
(let [log (fn [level more] (println (.toUpperCase (name level)) (str/join " " more)))]
(defn log! [level x & more]
(if (instance? Throwable x)
(do
(log level more)
(.printStackTrace ^Throwable x))
(log level (into [x] more))))
(log! :warn "clojure.tools.logging not found on classpath, compojure.api logging to console."))))
Loading