Skip to content

Commit 52619f4

Browse files
committed
Merge branch 'master' of github.com:metosin/compojure-api
2 parents 5778f9b + c1bd124 commit 52619f4

File tree

12 files changed

+140
-31
lines changed

12 files changed

+140
-31
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
test:
1515
strategy:
1616
matrix:
17-
jdk: [8, 11, 17, 21]
17+
jdk: [8, 11, 17, 21, 22]
1818

1919
name: Java ${{ matrix.jdk }}
2020

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
See also: [compojure-api 1.1.x changelog](./CHANGELOG-1.1.x.md)
22

3+
## Next
4+
* Lazily load spec and schema coercion
5+
* bump spec-tools to 0.10.6
6+
* notable changes: swagger `:name` defaults to `"body"` instead of `""` ([diff](https://github.com/metosin/spec-tools/compare/0.10.2...0.10.3))
7+
38
## 2.0.0-alpha34-SNAPSHOT
49
* **BREAKING CHANGE**: `:formatter :muuntaja` sometimes required for `api{-middleware}` options
510
* to prepare for 1.x compatibility, :muuntaja must be explicitly configured

project.clj

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
[com.fasterxml.jackson.datatype/jackson-datatype-joda "2.10.1"]
1313
[ring/ring-core "1.8.0"]
1414
[compojure "1.6.1" ]
15-
[metosin/spec-tools "0.10.0"]
15+
[metosin/spec-tools "0.10.6"]
1616
[metosin/ring-http-response "0.9.1"]
1717
[metosin/ring-swagger-ui "3.24.3"]
1818
[metosin/ring-swagger "1.0.0"]
@@ -37,7 +37,6 @@
3737
[org.clojure/core.async "0.6.532"]
3838
[javax.servlet/javax.servlet-api "4.0.1"]
3939
[peridot "0.5.2"]
40-
[com.rpl/specter "1.1.3"]
4140
[com.stuartsierra/component "0.4.0"]
4241
[expound "0.8.2"]
4342
[metosin/jsonista "0.2.5"]
@@ -61,6 +60,9 @@
6160
[org.slf4j/jul-to-slf4j "1.7.30"]
6261
[org.slf4j/log4j-over-slf4j "1.7.30"]
6362
[ch.qos.logback/logback-classic "1.2.3" ]]}
63+
:1.10 {:dependencies [[org.clojure/clojure "1.10.1"]]}
64+
:1.11 {:dependencies [[org.clojure/clojure "1.11.3"]]}
65+
:1.12 {:dependencies [[org.clojure/clojure "1.12.0-alpha11"]]}
6466
:async {:jvm-opts ["-Dcompojure-api.test.async=true"]
6567
:dependencies [[manifold "0.1.8" :exclusions [org.clojure/tools.logging]]]}}
6668
:eastwood {:namespaces [:source-paths]
@@ -86,7 +88,7 @@
8688
["change" "version" "leiningen.release/bump-version"]
8789
["vcs" "commit"]
8890
["vcs" "push"]]
89-
:aliases {"all" ["with-profile" "dev:dev,async"]
91+
:aliases {"all" ["with-profile" "dev:dev,async:dev,1.10:dev,1.11:dev,1.12"]
9092
"start-thingie" ["run"]
9193
"aot-uberjar" ["with-profile" "uberjar" "do" "clean," "ring" "uberjar"]
9294
"test-ancient" ["test"]

src/compojure/api/coerce.clj

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
;; 1.1.x
2+
(ns compojure.api.coerce
3+
(:require [schema.coerce :as sc]
4+
[compojure.api.middleware :as mw]
5+
[compojure.api.exception :as ex]
6+
[clojure.walk :as walk]
7+
[schema.utils :as su]
8+
[linked.core :as linked]))
9+
10+
(defn memoized-coercer
11+
"Returns a memoized version of a referentially transparent coercer fn. The
12+
memoized version of the function keeps a cache of the mapping from arguments
13+
to results and, when calls with the same arguments are repeated often, has
14+
higher performance at the expense of higher memory use. FIFO with 10000 entries.
15+
Cache will be filled if anonymous coercers are used (does not match the cache)"
16+
[]
17+
(let [cache (atom (linked/map))
18+
cache-size 10000]
19+
(fn [& args]
20+
(or (@cache args)
21+
(let [coercer (apply sc/coercer args)]
22+
(swap! cache (fn [mem]
23+
(let [mem (assoc mem args coercer)]
24+
(if (>= (count mem) cache-size)
25+
(dissoc mem (-> mem first first))
26+
mem))))
27+
coercer)))))
28+
29+
(defn cached-coercer [request]
30+
(or (-> request mw/get-options :coercer) sc/coercer))
31+
32+
(defn coerce-response! [request {:keys [status] :as response} responses]
33+
(-> (when-let [schema (or (:schema (get responses status))
34+
(:schema (get responses :default)))]
35+
(when-let [matchers (mw/coercion-matchers request)]
36+
(when-let [matcher (matchers :response)]
37+
(let [coercer (cached-coercer request)
38+
coerce (coercer schema matcher)
39+
body (coerce (:body response))]
40+
(if (su/error? body)
41+
(throw (ex-info
42+
(str "Response validation failed: " (su/error-val body))
43+
(assoc body :type ::ex/response-validation
44+
:response response)))
45+
(assoc response
46+
:compojure.api.meta/serializable? true
47+
:body body))))))
48+
(or response)))
49+
50+
(defn body-coercer-middleware [handler responses]
51+
(fn [request]
52+
(coerce-response! request (handler request) responses)))
53+
54+
(defn coerce! [schema key type request]
55+
(let [value (walk/keywordize-keys (key request))]
56+
(if-let [matchers (mw/coercion-matchers request)]
57+
(if-let [matcher (matchers type)]
58+
(let [coercer (cached-coercer request)
59+
coerce (coercer schema matcher)
60+
result (coerce value)]
61+
(if (su/error? result)
62+
(throw (ex-info
63+
(str "Request validation failed: " (su/error-val result))
64+
(assoc result :type ::ex/request-validation)))
65+
result))
66+
value)
67+
value)))

src/compojure/api/coercion.clj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
[compojure.api.exception :as ex]
44
[compojure.api.request :as request]
55
[compojure.api.coercion.core :as cc]
6-
[compojure.api.coercion.schema]
7-
[compojure.api.coercion.spec])
6+
;; side effects
7+
compojure.api.coercion.register-schema
8+
compojure.api.coercion.register-spec)
89
(:import (compojure.api.coercion.core CoercionError)))
910

1011
(def default-coercion :schema)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(ns compojure.api.coercion.register-schema
2+
(:require [compojure.api.coercion.core :as cc]))
3+
4+
(defmethod cc/named-coercion :schema [_]
5+
(deref
6+
(or (resolve 'compojure.api.coercion.schema/default-coercion)
7+
(do (require 'compojure.api.coercion.schema)
8+
(resolve 'compojure.api.coercion.schema/default-coercion)))))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(ns compojure.api.coercion.register-spec
2+
(:require [compojure.api.coercion.core :as cc]))
3+
4+
(defmethod cc/named-coercion :spec [_]
5+
(deref
6+
(or (resolve 'compojure.api.coercion.spec/default-coercion)
7+
(do (require 'compojure.api.coercion.spec)
8+
(resolve 'compojure.api.coercion.spec/default-coercion)))))

src/compojure/api/coercion/schema.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
[compojure.api.coercion.core :as cc]
66
[clojure.walk :as walk]
77
[schema.core :as s]
8-
[compojure.api.common :as common])
8+
[compojure.api.common :as common]
9+
;; side effects
10+
compojure.api.coercion.register-schema)
911
(:import (java.io File)
1012
(schema.core OptionalKey RequiredKey)
1113
(schema.utils ValidationError NamedError)))
@@ -88,5 +90,3 @@
8890
(->SchemaCoercion :schema options))
8991

9092
(def default-coercion (create-coercion default-options))
91-
92-
(defmethod cc/named-coercion :schema [_] default-coercion)

src/compojure/api/coercion/spec.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
[clojure.walk :as walk]
77
[compojure.api.coercion.core :as cc]
88
[spec-tools.swagger.core :as swagger]
9-
[compojure.api.common :as common])
9+
[compojure.api.common :as common]
10+
;; side effects
11+
compojure.api.coercion.register-spec)
1012
(:import (clojure.lang IPersistentMap)
1113
(schema.core RequiredKey OptionalKey)
1214
(spec_tools.core Spec)
@@ -149,5 +151,3 @@
149151
(->SpecCoercion :spec options))
150152

151153
(def default-coercion (create-coercion default-options))
152-
153-
(defmethod cc/named-coercion :spec [_] default-coercion)

src/compojure/api/meta.clj

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,12 @@
693693
(defn- route-args? [arg]
694694
(not= arg []))
695695

696+
(defn- resolve-var [&env sym]
697+
(when (symbol? sym)
698+
(let [v (resolve &env sym)]
699+
(when (var? v)
700+
v))))
701+
696702
(def endpoint-vars (into #{}
697703
(mapcat (fn [n]
698704
(map #(symbol (name %) (name n))
@@ -759,14 +765,11 @@
759765
(defn- static-middleware? [&env body]
760766
(and (seq? body)
761767
(boolean
762-
(let [sym (first body)]
763-
(when (symbol? sym)
764-
(when-some [v (resolve &env sym)]
765-
(when (var? v)
766-
(when (middleware-vars (var->sym v))
767-
(let [[_ path route-arg & args] body
768-
[options body] (extract-parameters args true)]
769-
(static-body? &env body))))))))))
768+
(when-some [v (resolve-var &env (first body))]
769+
(when (middleware-vars (var->sym v))
770+
(let [[_ mid & body] body]
771+
(and (static-form? &env mid)
772+
(static-body? &env body))))))))
770773

771774
(def route-middleware-vars (into #{}
772775
(mapcat (fn [n]
@@ -803,12 +806,6 @@
803806
(= sym 'if))
804807
(static-body? &env (next form)))))))))
805808

806-
(defn- resolve-var [&env sym]
807-
(when (symbol? sym)
808-
(let [v (resolve &env sym)]
809-
(when (var? v)
810-
v))))
811-
812809
(defn- static-resolved-form? [&env form]
813810
(boolean
814811
(or (and (seq? form)
@@ -1007,7 +1004,7 @@
10071004
(let [coach (some-> (System/getProperty "compojure.api.meta.static-context-coach")
10081005
edn/read-string)]
10091006
(if-not coach
1010-
(when (ffirst (reset-vals! warned-non-static? true))
1007+
(when (first (reset-vals! warned-non-static? true))
10111008
(println
10121009
(str (format "WARNING: Performance issue detected with compojure-api usage in %s.\n" (ns-name *ns*))
10131010
"To fix this warning, set: -Dcompojure.api.meta.static-context-coach={:default :print}.\n"

0 commit comments

Comments
 (0)