Skip to content

Commit

Permalink
:headers double
Browse files Browse the repository at this point in the history
  • Loading branch information
frenchy64 committed Apr 24, 2024
1 parent e1d795d commit efd5a71
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 7 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
See also: [compojure-api 1.1.x changelog](./CHANGELOG-1.1.x.md)

## NEXT
* Throw an error on malformed `:{body,query}`, in particular is anything other than 2 elements was provided
* Disable check with `-Dcompojure.api.meta.allow-bad-{body,query}=true`
* 50% reduction in the number of times `:{return,body,query,responses}` schemas are evaluated
* Throw an error on malformed `:{body,query,headers}`, in particular is anything other than 2 elements was provided
* Disable check with `-Dcompojure.api.meta.allow-bad-{body,query,headers}=true`
* 50% reduction in the number of times `:{return,body,query,responses,headers}` schemas are evaluated
* saves 1 evaluation for static contexts
* saves 1 evaluation per request for dynamic contexts

Expand Down
14 changes: 10 additions & 4 deletions src/compojure/api/meta.clj
Original file line number Diff line number Diff line change
Expand Up @@ -390,10 +390,16 @@
" :headers [headers HeaderSchema]"
" (ok headers))")))

(defmethod restructure-param :headers [_ [value schema] acc]
(-> acc
(update-in [:lets] into [value (src-coerce! schema :headers :string)])
(assoc-in [:info :public :parameters :header] schema)))
(defmethod restructure-param :headers [_ [value schema :as bv] acc]
(when-not (= "true" (System/getProperty "compojure.api.meta.allow-bad-headers"))
(assert (= 2 (count bv))
(str ":headers should be [sym schema], provided: " bv
"\nDisable this check with -Dcompojure.api.meta.allow-bad-headers=true")))
(let [g (gensym 'headers-schema)]
(-> acc
(update :outer-lets into [g schema])
(update-in [:lets] into [value (src-coerce! g :headers :string)])
(assoc-in [:info :public :parameters :header] g))))

;;
;; body-params
Expand Down
138 changes: 138 additions & 0 deletions test/compojure/api/meta_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -861,3 +861,141 @@
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 1 @times)))))

(deftest headers-double-eval-test
(testing "no :headers double expansion"
(is-expands (GET "/ping" []
:headers [headers EXPENSIVE]
(ok "kikka"))
`(let [?headers-schema ~'EXPENSIVE]
(map->Route
{:path "/ping",
:method :get,
:info (merge-parameters
{:public {:parameters {:header ?headers-schema}}})
:handler
(make-route
:get
{:__record__ "clout.core.CompiledRoute",
:source "/ping",
:re #"/ping",
:keys [],
:absolute? false}
(fn [?request]
(let-request [[:as +compojure-api-request+] ?request]
(let [~'headers (compojure.api.coercion/coerce-request!
?headers-schema
:headers
:string
true
false
+compojure-api-request+)]
(do ~'(ok "kikka"))))))}))))
(testing "no context"
(let [times (atom 0)
route (GET "/ping" []
:headers [body (do (swap! times inc) s/Any)]
(ok "kikka"))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 1 @times))))
(testing "inferred static context"
(let [times (atom 0)
route (context
"" []
(GET "/ping" []
:headers [body (do (swap! times inc) s/Any)]
(ok "kikka")))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 1 @times))))
(testing "dynamic context that doesn't bind variables"
(let [times (atom 0)
route (context
"" []
:dynamic true
(GET "/ping" []
:headers [body (do (swap! times inc) s/Any)]
(ok "kikka")))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 11 @times))))
(testing "dynamic context where schema is bound outside context"
(let [times (atom 0)
route (let [s s/Any]
(context
"" []
:dynamic true
(GET "/ping" []
;;TODO could lift this since the locals occur outside the context
:headers [body (do (swap! times inc) s)]
(ok "kikka"))))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 11 @times))))
(testing "dynamic context that binds req and uses it in schema"
(let [times (atom 0)
route (context
"" req
(GET "/ping" req
:headers [body (do (swap! times inc)
;; should never lift this since it refers to request
(second [req s/Any]))]
(ok "kikka")))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 11 @times))))
(testing "bind :headers in static context"
(is-thrown-with-msg?
AssertionError
#"cannot be :static"
(eval `(context
"" []
:static true
:headers [body (do (swap! times update :outer inc)
s/Any)]
(GET "/ping" req
:headers [body (do (swap! times update :inner inc)
s/Any)]
(ok "kikka"))))))
(testing "bind :headers in dynamic context"
(let [times (atom {:outer 0 :inner 0})
route (context
"" []
:dynamic true
:headers [body (do (swap! times update :outer inc)
s/Any)]
(GET "/ping" req
:headers [body (do (swap! times update :inner inc)
s/Any)]
(ok "kikka")))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= {:outer 1 :inner 1} @times))
(dorun (repeatedly 10 exercise))
(is (= {:outer 1 :inner 11} @times))))
(testing "idea for lifting impl"
(let [times (atom 0)
route (let [rs (GET "/ping" req
:headers [body (do (swap! times inc)
s/Any)]
(ok "kikka"))]
(context
"" []
:dynamic true
rs))
exercise #(is (= "kikka" (:body (route {:request-method :get :uri "/ping"}))))]
(exercise)
(is (= 1 @times))
(dorun (repeatedly 10 exercise))
(is (= 1 @times)))))

0 comments on commit efd5a71

Please sign in to comment.