fix: match-by-name! should throw when match-by-name is PartialMatch

If a path param was nil, match-by-name (via impl/path-for) was
treating the parameter as missing, but match-by-name!
(via impl/throw-on-missing-path-params) was treating it as present.

That is:

(reitit/match-by-name router :page {:id "1"}) ;; => Match
(reitit/match-by-name router :page) ;; => PartialMatch
(reitit/match-by-name router :page {:id nil}) ;; => PartialMatch

(reitit/match-by-name! router :page {:id "1"}) ;; => Match
(reitit/match-by-name! router :page) ;; => ExceptionInfo: missing path-params for route /pages/:id -> #{:id}
(reitit/match-by-name! router :page {:id nil}) ;; => nil  !!!

fixes #758
This commit is contained in:
Joel Kaasinen 2025-10-24 09:58:03 +03:00
parent ef9dd495be
commit d2f44b8015
2 changed files with 11 additions and 6 deletions

View file

@ -198,9 +198,8 @@
(:path route))) (:path route)))
(defn throw-on-missing-path-params [template required path-params] (defn throw-on-missing-path-params [template required path-params]
(when-not (every? #(contains? path-params %) required) (let [missing (set (remove #(get path-params %) required))]
(let [defined (-> path-params keys set) (when-not (empty? missing)
missing (set/difference required defined)]
(ex/fail! (ex/fail!
(str "missing path-params for route " template " -> " missing) (str "missing path-params for route " template " -> " missing)
{:path-params path-params, :required required})))) {:path-params path-params, :required required}))))

View file

@ -13,8 +13,7 @@
(testing "routers handling wildcard paths" (testing "routers handling wildcard paths"
(are [r name] (are [r name]
(testing "wild" (testing (str name)
(testing "simple" (testing "simple"
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})] (let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
(is (= name (r/router-name router))) (is (= name (r/router-name router)))
@ -52,10 +51,17 @@
:path-params nil}) :path-params nil})
(r/match-by-name router ::beer))) (r/match-by-name router ::beer)))
(is (r/partial-match? (r/match-by-name router ::beer))) (is (r/partial-match? (r/match-by-name router ::beer)))
(is (r/partial-match? (r/match-by-name router ::beer {:size nil}))
"nil counts as missing")
(is (thrown-with-msg? (is (thrown-with-msg?
ExceptionInfo ExceptionInfo
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$" #"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
(r/match-by-name! router ::beer)))))) (r/match-by-name! router ::beer)))
(is (thrown-with-msg?
ExceptionInfo
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
(r/match-by-name! router ::beer {:size nil}))
"nil counts as missing"))))
(testing "decode %-encoded path params" (testing "decode %-encoded path params"
(let [router (r/router [["/one-param-path/:param1" ::one] (let [router (r/router [["/one-param-path/:param1" ::one]