Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
frenchy64 committed Jun 6, 2024
1 parent 7e4f63b commit ce27594
Show file tree
Hide file tree
Showing 11 changed files with 521 additions and 399 deletions.
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

0 comments on commit ce27594

Please sign in to comment.