Skip to content

Commit

Permalink
add back c.api.coerce
Browse files Browse the repository at this point in the history
  • Loading branch information
frenchy64 committed May 14, 2024
1 parent 8ca7e86 commit 965aff2
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
67 changes: 67 additions & 0 deletions src/compojure/api/coerce.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
;; 1.1.x
(ns compojure.api.coerce
(:require [schema.coerce :as sc]
[compojure.api.middleware :as mw]
[compojure.api.exception :as ex]
[clojure.walk :as walk]
[schema.utils :as su]
[linked.core :as linked]))

(defn memoized-coercer
"Returns a memoized version of a referentially transparent coercer fn. The
memoized version of the function keeps a cache of the mapping from arguments
to results and, when calls with the same arguments are repeated often, has
higher performance at the expense of higher memory use. FIFO with 10000 entries.
Cache will be filled if anonymous coercers are used (does not match the cache)"
[]
(let [cache (atom (linked/map))
cache-size 10000]
(fn [& args]
(or (@cache args)
(let [coercer (apply sc/coercer args)]
(swap! cache (fn [mem]
(let [mem (assoc mem args coercer)]
(if (>= (count mem) cache-size)
(dissoc mem (-> mem first first))
mem))))
coercer)))))

(defn cached-coercer [request]
(or (-> request mw/get-options :coercer) sc/coercer))

(defn coerce-response! [request {:keys [status] :as response} responses]
(-> (when-let [schema (or (:schema (get responses status))
(:schema (get responses :default)))]
(when-let [matchers (mw/coercion-matchers request)]
(when-let [matcher (matchers :response)]
(let [coercer (cached-coercer request)
coerce (coercer schema matcher)
body (coerce (:body response))]
(if (su/error? body)
(throw (ex-info
(str "Response validation failed: " (su/error-val body))
(assoc body :type ::ex/response-validation
:response response)))
(assoc response
:compojure.api.meta/serializable? true
:body body))))))
(or response)))

(defn body-coercer-middleware [handler responses]
(fn [request]
(coerce-response! request (handler request) responses)))

(defn coerce! [schema key type request]
(let [value (walk/keywordize-keys (key request))]
(if-let [matchers (mw/coercion-matchers request)]
(if-let [matcher (matchers type)]
(let [coercer (cached-coercer request)
coerce (coercer schema matcher)
result (coerce value)]
(if (su/error? result)
(throw (ex-info
(str "Request validation failed: " (su/error-val result))
(assoc result :type ::ex/request-validation)))
result))
value)
value)))
21 changes: 21 additions & 0 deletions src/compojure/api/middleware.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
[ring.middleware.nested-params :refer [wrap-nested-params]]
[ring.middleware.params :refer [wrap-params]]
[ring.swagger.coerce :as coerce]

[muuntaja.middleware]
[muuntaja.core :as m]
Expand Down Expand Up @@ -88,6 +89,12 @@
;; Options
;;

;; 1.1.x
(defn get-options
"Extracts compojure-api options from the request."
[request]
(::options request))

(defn wrap-inject-data
"Injects data into the request."
[handler data]
Expand All @@ -108,6 +115,20 @@
([request respond raise]
(handler (coercion/set-request-coercion request coercion) respond raise))))

;; 1.1.x
(def default-coercion-matchers
{:body coerce/json-schema-coercion-matcher
:string coerce/query-schema-coercion-matcher
:response coerce/json-schema-coercion-matcher})

;; 1.1.x
(defn coercion-matchers [request]
(let [options (get-options request)]
(if (contains? options :coercion)
(if-let [provider (:coercion options)]
(provider request))
default-coercion-matchers)))

;;
;; Muuntaja
;;
Expand Down

0 comments on commit 965aff2

Please sign in to comment.