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

wip legacy coercion compat #473

Closed
wants to merge 2 commits into from
Closed
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
55 changes: 33 additions & 22 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
:url "http://www.eclipse.org/legal/epl-v10.html"
:distribution :repo
:comments "same as Clojure"}
:scm {:name "git"
:url "https://github.com/metosin/compojure-api"}
:dependencies [[prismatic/plumbing "0.6.0"]
[cheshire "5.13.0"]
[compojure "1.6.1"]
[prismatic/schema "1.1.12"]
[org.tobereplaced/lettercase "1.0.0"]
[frankiesardo/linked "1.3.0"]
[ring-middleware-format "0.7.4"]
:dependencies [[prismatic/schema "1.1.12"]
[prismatic/plumbing "0.5.5"]
[ikitommi/linked "1.3.1-alpha1"] ;; waiting for the original
[metosin/muuntaja "0.6.6"]
[com.fasterxml.jackson.datatype/jackson-datatype-joda "2.10.1"]
[ring/ring-core "1.8.0"]
[compojure "1.6.1" ]
[metosin/spec-tools "0.10.6"]
[metosin/ring-http-response "0.9.1"]
[metosin/ring-swagger-ui "3.24.3"]
[metosin/ring-swagger "1.0.0"]
[metosin/ring-swagger-ui "2.2.10"]]

;; Fix dependency conflicts
[clj-time "0.15.2"]
[joda-time "2.10.5"]
[riddley "0.2.0"]]
:pedantic? :abort
:profiles {:uberjar {:aot :all
:ring {:handler examples.thingie/app}
:source-paths ["examples/thingie/src"]
Expand All @@ -31,18 +36,18 @@
[lein-ring "0.12.0"]
[funcool/codeina "0.5.0"]]
:dependencies [[org.clojure/clojure "1.9.0"]
;; bump
[fipp "0.6.26"]
[metosin/spec-tools "0.10.6"]
[metosin/muuntaja "0.6.6"]
[metosin/jsonista "0.2.5"]
[com.fasterxml.jackson.datatype/jackson-datatype-joda "2.10.1"]
[slingshot "0.12.2"]
[peridot "0.5.1"]
[javax.servlet/servlet-api "2.5"]
[midje "1.9.9"]
[midje "1.9.9" :exclusions [org.clojure/tools.namespace]]
[org.clojure/core.unify "0.6.0"]
[org.clojure/core.async "0.6.532"]
[javax.servlet/javax.servlet-api "4.0.1"]
[peridot "0.5.2"]
[com.stuartsierra/component "0.4.0"]
[expound "0.8.2"]
[metosin/jsonista "0.2.5"]
[reloaded.repl "0.2.4"]
[metosin/muuntaja-msgpack "0.6.6"]
[metosin/muuntaja-yaml "0.6.6"]
[org.immutant/immutant "2.1.10"]
[http-kit "2.3.0"]
[criterium "0.4.5"]]
:ring {:handler examples.thingie/app
Expand All @@ -52,10 +57,16 @@
:perf {:jvm-opts ^:replace ["-server"
"-Xmx4096m"
"-Dclojure.compiler.direct-linking=true"]}
:logging {:dependencies [[org.clojure/tools.logging "0.5.0"]]}
:logging {:dependencies [[org.clojure/tools.logging "0.5.0"]
[org.slf4j/jcl-over-slf4j "1.7.30"]
[org.slf4j/jul-to-slf4j "1.7.30"]
[org.slf4j/log4j-over-slf4j "1.7.30"]
[ch.qos.logback/logback-classic "1.2.3" ]]}
:1.10 {:dependencies [[org.clojure/clojure "1.10.1"]]}
:1.11 {:dependencies [[org.clojure/clojure "1.11.3"]]}
:1.12 {:dependencies [[org.clojure/clojure "1.12.0-alpha11"]]}}
:1.12 {:dependencies [[org.clojure/clojure "1.12.0-alpha11"]]}
:async {:jvm-opts ["-Dcompojure-api.test.async=true"]
:dependencies [[manifold "0.1.8" :exclusions [org.clojure/tools.logging]]]}}
:eastwood {:namespaces [:source-paths]
:add-linters [:unused-namespaces]}
:codeina {:sources ["src"]
Expand Down
47 changes: 20 additions & 27 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,35 +47,28 @@

### 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)
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)))

(defmacro
^{:doc (str
"Defines an api.

API middleware options:

" (:doc (meta #'compojure.api.middleware/api-middleware)))}
defapi
[name & body]
{:style/indent 1}
`(def ~name (api ~@body)))
api-middleware-options (mw/api-middleware-options (dissoc options :api :swagger))
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)))
66 changes: 0 additions & 66 deletions src/compojure/api/coerce.clj

This file was deleted.

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})
16 changes: 10 additions & 6 deletions src/compojure/api/meta.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
[ring.swagger.json-schema :as js]
[schema.core :as s]
[schema-tools.core :as st]
[compojure.api.coerce :as coerce]
compojure.core))
[compojure.api.coercion :as coercion]
[compojure.api.help :as help]
compojure.core
compojure.api.compojure-compat
[compojure.api.common :as common]))

(def +compojure-api-request+
"lexically bound ring-request for handlers."
Expand All @@ -33,9 +36,10 @@
(s/defn src-coerce!
"Return source code for coerce! for a schema with coercion type,
extracted from a key in a ring request."
[schema, key, type :- mw/CoercionType]
(assert (not (#{:query :json} type)) (str type " is DEPRECATED since 0.22.0. Use :body or :string instead."))
`(coerce/coerce! ~schema ~key ~type ~+compojure-api-request+))
([schema, key, type]
(src-coerce! schema, key, type, true))
([schema, key, type, keywordize?]
`(coercion/coerce-request! ~schema ~key ~type ~keywordize? false ~+compojure-api-request+)))

(defn- convert-return [schema]
{200 {:schema schema
Expand Down Expand Up @@ -303,7 +307,7 @@
_ (assert (not parameters) ":parameters is deprecated with 1.0.0, use :swagger instead.")

;; response coercion middleware, why not just code?
middleware (if (seq responses) (conj middleware `[coerce/body-coercer-middleware (common/merge-vector ~responses)]) middleware)]
middleware (if (seq responses) (conj middleware `[coercion/wrap-coerce-response (common/merge-vector ~responses)]) middleware)]

(if context?

Expand Down
Loading
Loading