Skip to content

Commit

Permalink
Merge branch 'master' into compojure1-compat
Browse files Browse the repository at this point in the history
  • Loading branch information
frenchy64 committed Jun 17, 2024
2 parents d5cd3d7 + c1bd124 commit d1957a3
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 20 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
12 changes: 5 additions & 7 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
[com.fasterxml.jackson.datatype/jackson-datatype-joda "2.10.1"]
[ring/ring-core "1.8.0"]
[compojure "1.6.1" ]
[org.clojure/core.memoize "0.8.2"]
[clj-commons/clj-yaml "0.7.0"]
[org.yaml/snakeyaml "1.24"]
[ring-middleware-format "0.7.4"]
[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 @@ -41,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 @@ -65,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 @@ -90,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 @@ -84,5 +86,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)
21 changes: 21 additions & 0 deletions src/compojure/api/middleware.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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 @@ -103,6 +104,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 @@ -123,6 +130,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 d1957a3

Please sign in to comment.