Skip to content

Commit 66270ff

Browse files
committed
backport src/compojure/api/coercion/schema.clj
1 parent 0e8ae7a commit 66270ff

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
(ns compojure.api.coercion.schema
2+
(:require [schema.coerce :as sc]
3+
[schema.utils :as su]
4+
[ring.swagger.coerce :as coerce]
5+
[compojure.api.coercion.core :as cc]
6+
[clojure.walk :as walk]
7+
[schema.core :as s]
8+
[compojure.api.common :as common])
9+
(:import (java.io File)
10+
(schema.core OptionalKey RequiredKey)
11+
(schema.utils ValidationError NamedError)))
12+
13+
(def string-coercion-matcher coerce/query-schema-coercion-matcher)
14+
(def json-coercion-matcher coerce/json-schema-coercion-matcher)
15+
16+
(defn stringify
17+
"Stringifies Schema records recursively."
18+
[error]
19+
(walk/prewalk
20+
(fn [x]
21+
(cond
22+
(class? x) (.getName ^Class x)
23+
(instance? OptionalKey x) (pr-str (list 'opt (:k x)))
24+
(instance? RequiredKey x) (pr-str (list 'req (:k x)))
25+
(and (satisfies? s/Schema x) (record? x)) (try (pr-str (s/explain x)) (catch Exception _ x))
26+
(instance? ValidationError x) (str (su/validation-error-explain x))
27+
(instance? NamedError x) (str (su/named-error-explain x))
28+
:else x))
29+
error))
30+
31+
(def memoized-coercer
32+
(common/fifo-memoize sc/coercer 1000))
33+
34+
;; don't use coercion for certain types
35+
(defmulti coerce-response? identity :default ::default)
36+
(defmethod coerce-response? ::default [_] true)
37+
(defmethod coerce-response? File [_] false)
38+
39+
(defrecord SchemaCoercion [name options]
40+
cc/Coercion
41+
(get-name [_] name)
42+
43+
(get-apidocs [_ _ data] data)
44+
45+
(make-open [_ schema]
46+
(if (map? schema)
47+
(assoc schema s/Keyword s/Any)
48+
schema))
49+
50+
(encode-error [_ error]
51+
(-> error
52+
(update :schema pr-str)
53+
(update :errors stringify)))
54+
55+
(coerce-request [_ schema value type format request]
56+
(let [type-options (options type)]
57+
(if-let [matcher (or (get (get type-options :formats) format)
58+
(get type-options :default))]
59+
(let [coerce (memoized-coercer schema matcher)
60+
coerced (coerce value)]
61+
(if (su/error? coerced)
62+
(let [errors (su/error-val coerced)]
63+
(cc/map->CoercionError
64+
{:schema schema
65+
:errors errors}))
66+
coerced))
67+
value)))
68+
69+
(accept-response? [_ model]
70+
(coerce-response? model))
71+
72+
(coerce-response [this schema value type format request]
73+
(cc/coerce-request this schema value type format request)))
74+
75+
(def default-options
76+
{:body {:default (constantly nil)
77+
:formats {"application/json" json-coercion-matcher
78+
"application/msgpack" json-coercion-matcher
79+
"application/x-yaml" json-coercion-matcher}}
80+
:string {:default string-coercion-matcher}
81+
:response {:default (constantly nil)}})
82+
83+
(defn create-coercion [options]
84+
(->SchemaCoercion :schema options))
85+
86+
(def default-coercion (create-coercion default-options))
87+
88+
(defmethod cc/named-coercion :schema [_] default-coercion)

0 commit comments

Comments
 (0)