Skip to content

Commit

Permalink
Merge branch 'master' of github.com:metosin/compojure-api
Browse files Browse the repository at this point in the history
  • Loading branch information
frenchy64 committed May 27, 2024
2 parents 5778f9b + c1bd124 commit 52619f4
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
test:
strategy:
matrix:
jdk: [8, 11, 17, 21]
jdk: [8, 11, 17, 21, 22]

name: Java ${{ matrix.jdk }}

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
See also: [compojure-api 1.1.x changelog](./CHANGELOG-1.1.x.md)

## Next
* Lazily load spec and schema coercion
* bump spec-tools to 0.10.6
* notable changes: swagger `:name` defaults to `"body"` instead of `""` ([diff](https://github.com/metosin/spec-tools/compare/0.10.2...0.10.3))

## 2.0.0-alpha34-SNAPSHOT
* **BREAKING CHANGE**: `:formatter :muuntaja` sometimes required for `api{-middleware}` options
* to prepare for 1.x compatibility, :muuntaja must be explicitly configured
Expand Down
8 changes: 5 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[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.0"]
[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"]
Expand All @@ -37,7 +37,6 @@
[org.clojure/core.async "0.6.532"]
[javax.servlet/javax.servlet-api "4.0.1"]
[peridot "0.5.2"]
[com.rpl/specter "1.1.3"]
[com.stuartsierra/component "0.4.0"]
[expound "0.8.2"]
[metosin/jsonista "0.2.5"]
Expand All @@ -61,6 +60,9 @@
[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"]]}
:async {:jvm-opts ["-Dcompojure-api.test.async=true"]
:dependencies [[manifold "0.1.8" :exclusions [org.clojure/tools.logging]]]}}
:eastwood {:namespaces [:source-paths]
Expand All @@ -86,7 +88,7 @@
["change" "version" "leiningen.release/bump-version"]
["vcs" "commit"]
["vcs" "push"]]
:aliases {"all" ["with-profile" "dev:dev,async"]
:aliases {"all" ["with-profile" "dev:dev,async:dev,1.10:dev,1.11:dev,1.12"]
"start-thingie" ["run"]
"aot-uberjar" ["with-profile" "uberjar" "do" "clean," "ring" "uberjar"]
"test-ancient" ["test"]
Expand Down
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)))
5 changes: 3 additions & 2 deletions src/compojure/api/coercion.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
[compojure.api.exception :as ex]
[compojure.api.request :as request]
[compojure.api.coercion.core :as cc]
[compojure.api.coercion.schema]
[compojure.api.coercion.spec])
;; side effects
compojure.api.coercion.register-schema
compojure.api.coercion.register-spec)
(:import (compojure.api.coercion.core CoercionError)))

(def default-coercion :schema)
Expand Down
8 changes: 8 additions & 0 deletions src/compojure/api/coercion/register_schema.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(ns compojure.api.coercion.register-schema
(:require [compojure.api.coercion.core :as cc]))

(defmethod cc/named-coercion :schema [_]
(deref
(or (resolve 'compojure.api.coercion.schema/default-coercion)
(do (require 'compojure.api.coercion.schema)
(resolve 'compojure.api.coercion.schema/default-coercion)))))
8 changes: 8 additions & 0 deletions src/compojure/api/coercion/register_spec.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(ns compojure.api.coercion.register-spec
(:require [compojure.api.coercion.core :as cc]))

(defmethod cc/named-coercion :spec [_]
(deref
(or (resolve 'compojure.api.coercion.spec/default-coercion)
(do (require 'compojure.api.coercion.spec)
(resolve 'compojure.api.coercion.spec/default-coercion)))))
6 changes: 3 additions & 3 deletions src/compojure/api/coercion/schema.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
[compojure.api.coercion.core :as cc]
[clojure.walk :as walk]
[schema.core :as s]
[compojure.api.common :as common])
[compojure.api.common :as common]
;; side effects
compojure.api.coercion.register-schema)
(:import (java.io File)
(schema.core OptionalKey RequiredKey)
(schema.utils ValidationError NamedError)))
Expand Down Expand Up @@ -88,5 +90,3 @@
(->SchemaCoercion :schema options))

(def default-coercion (create-coercion default-options))

(defmethod cc/named-coercion :schema [_] default-coercion)
6 changes: 3 additions & 3 deletions src/compojure/api/coercion/spec.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
[clojure.walk :as walk]
[compojure.api.coercion.core :as cc]
[spec-tools.swagger.core :as swagger]
[compojure.api.common :as common])
[compojure.api.common :as common]
;; side effects
compojure.api.coercion.register-spec)
(:import (clojure.lang IPersistentMap)
(schema.core RequiredKey OptionalKey)
(spec_tools.core Spec)
Expand Down Expand Up @@ -149,5 +151,3 @@
(->SpecCoercion :spec options))

(def default-coercion (create-coercion default-options))

(defmethod cc/named-coercion :spec [_] default-coercion)
27 changes: 12 additions & 15 deletions src/compojure/api/meta.clj
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,12 @@
(defn- route-args? [arg]
(not= arg []))

(defn- resolve-var [&env sym]
(when (symbol? sym)
(let [v (resolve &env sym)]
(when (var? v)
v))))

(def endpoint-vars (into #{}
(mapcat (fn [n]
(map #(symbol (name %) (name n))
Expand Down Expand Up @@ -759,14 +765,11 @@
(defn- static-middleware? [&env body]
(and (seq? body)
(boolean
(let [sym (first body)]
(when (symbol? sym)
(when-some [v (resolve &env sym)]
(when (var? v)
(when (middleware-vars (var->sym v))
(let [[_ path route-arg & args] body
[options body] (extract-parameters args true)]
(static-body? &env body))))))))))
(when-some [v (resolve-var &env (first body))]
(when (middleware-vars (var->sym v))
(let [[_ mid & body] body]
(and (static-form? &env mid)
(static-body? &env body))))))))

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

(defn- resolve-var [&env sym]
(when (symbol? sym)
(let [v (resolve &env sym)]
(when (var? v)
v))))

(defn- static-resolved-form? [&env form]
(boolean
(or (and (seq? form)
Expand Down Expand Up @@ -1007,7 +1004,7 @@
(let [coach (some-> (System/getProperty "compojure.api.meta.static-context-coach")
edn/read-string)]
(if-not coach
(when (ffirst (reset-vals! warned-non-static? true))
(when (first (reset-vals! warned-non-static? true))
(println
(str (format "WARNING: Performance issue detected with compojure-api usage in %s.\n" (ns-name *ns*))
"To fix this warning, set: -Dcompojure.api.meta.static-context-coach={:default :print}.\n"
Expand Down
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
8 changes: 4 additions & 4 deletions test19/compojure/api/coercion/spec_coercion_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@
:responses {:default {:description ""}}}}
"/body-map" {:post {:parameters [{:description ""
:in "body"
:name ""
:name "body"
:required true
:schema {:properties {:x {:format "int64"
:type "integer"}
Expand All @@ -404,7 +404,7 @@
:responses {:default {:description ""}}}}
"/body-params" {:post {:parameters [{:description ""
:in "body"
:name ""
:name "body"
:required true
:schema {:properties {:x {:format "int64"
:type "integer"}
Expand All @@ -415,7 +415,7 @@
:responses {:default {:description ""}}}}
"/body-string" {:post {:parameters [{:description ""
:in "body"
:name ""
:name "body"
:required true
:schema {:type "string"}}]
:responses {:default {:description ""}}}}
Expand Down Expand Up @@ -476,7 +476,7 @@
:default {:description ""}}}
:post {:parameters [{:description ""
:in "body"
:name ""
:name "body"
:required true
:schema {:properties {:x {:format "int64"
:type "integer"}
Expand Down

0 comments on commit 52619f4

Please sign in to comment.