(Translated by https://www.hiragana.jp/)
Malli parameters merging broken · Issue #422 · metosin/reitit · GitHub
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Malli parameters merging broken #422

Closed
xificurC opened this issue Jul 16, 2020 · 11 comments · Fixed by #626
Closed

Malli parameters merging broken #422

xificurC opened this issue Jul 16, 2020 · 11 comments · Fixed by #626
Assignees
Labels

Comments

@xificurC
Copy link
Contributor

  (do
    (require '[reitit.ring :as ring] '[reitit.ring.coercion :as coercion] 'reitit.coercion.malli)
    (ring/router ["/{a}" {:parameters {:path [:map [:a string?]]}}
                  ["/{b}" {:parameters {:path [:map [:b string?]]}}]]
                 {:data {:coercion reitit.coercion.malli/coercion
                         :middleware [coercion/coerce-request-middleware]}}))

  Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (11 frames hidden)

1. Unhandled clojure.lang.ExceptionInfo
   nth not supported on this type: Keyword
   #:reitit.exception{:cause #error {
    :cause "nth not supported on this type: Keyword"
    :via
    [{:type java.lang.UnsupportedOperationException
      :message "nth not supported on this type: Keyword"
      :at [clojure.lang.RT nthFrom "RT.java" 991]}]
    :trace
    [[clojure.lang.RT nthFrom "RT.java" 991]
     [clojure.lang.RT nth "RT.java" 940]
     [malli.core$_parse_entry_syntax$_parse__13937 invoke "core.cljc" 270]
     [malli.core$_parse_entry_syntax$fn__13946 invoke "core.cljc" 273]
     [clojure.core$mapv$fn__8445 invoke "core.clj" 6912]
     [clojure.lang.ArrayChunk reduce "ArrayChunk.java" 63]
     [clojure.core.protocols$fn__8154 invokeStatic "protocols.clj" 136]
     [clojure.core.protocols$fn__8154 invoke "protocols.clj" 124]
     [clojure.core.protocols$fn__8114$G__8109__8123 invoke "protocols.clj" 19]
     [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 31]
     [clojure.core.protocols$fn__8146 invokeStatic "protocols.clj" 75]
     [clojure.core.protocols$fn__8146 invoke "protocols.clj" 75]
     [clojure.core.protocols$fn__8088$G__8083__8101 invoke "protocols.clj" 13]
     [clojure.core$reduce invokeStatic "core.clj" 6828]
     [clojure.core$mapv invokeStatic "core.clj" 6903]
     [clojure.core$mapv invoke "core.clj" 6903]
     [malli.core$_parse_entry_syntax invokeStatic "core.cljc" 273]
     [malli.core$_parse_entry_syntax invoke "core.cljc" 269]
     [malli.core$_map_schema$reify__13969 _into_schema "core.cljc" 290]
     [malli.core$into_schema invokeStatic "core.cljc" 908]
     [malli.core$into_schema invoke "core.cljc" 903]
     [malli.core$schema invokeStatic "core.cljc" 918]
     [malli.core$schema invoke "core.cljc" 910]
     [malli.core$walk invokeStatic "core.cljc" 959]
     [malli.core$walk invoke "core.cljc" 954]
     [malli.core$walk invokeStatic "core.cljc" 956]
     [malli.core$walk invoke "core.cljc" 954]
     [malli.util$closed_schema invokeStatic "util.cljc" 122]
     [malli.util$closed_schema invoke "util.cljc" 116]
     [reitit.coercion.malli$create$reify__19044 _request_coercer "malli.cljc" 176]
     [reitit.coercion$request_coercer invokeStatic "coercion.cljc" 80]
     [reitit.coercion$request_coercer invoke "coercion.cljc" 73]
     [reitit.coercion$request_coercers$iter__17680__17684$fn__17685 invoke "coercion.cljc" 125]
     [clojure.lang.LazySeq sval "LazySeq.java" 42]
     [clojure.lang.LazySeq seq "LazySeq.java" 51]
     [clojure.lang.RT seq "RT.java" 535]
     [clojure.core$seq__5402 invokeStatic "core.clj" 137]
     [clojure.core$filter$fn__5893 invoke "core.clj" 2809]
     [clojure.lang.LazySeq sval "LazySeq.java" 42]
     [clojure.lang.LazySeq seq "LazySeq.java" 51]
     [clojure.lang.RT seq "RT.java" 535]
     [clojure.core$seq__5402 invokeStatic "core.clj" 137]
     [clojure.core$seq__5402 invoke "core.clj" 137]
     [reitit.coercion$request_coercers invokeStatic "coercion.cljc" 127]
     [reitit.coercion$request_coercers invoke "coercion.cljc" 122]
     [reitit.ring.coercion$fn__17964 invokeStatic "coercion.cljc" 35]
     [reitit.ring.coercion$fn__17964 invoke "coercion.cljc" 27]
     [reitit.middleware$eval17010$fn__17012 invoke "middleware.cljc" 68]
     [reitit.middleware$eval16880$fn__16881$G__16871__16890 invoke "middleware.cljc" 8]
     [reitit.middleware$eval17002$fn__17003 invoke "middleware.cljc" 51]
     [reitit.middleware$eval16880$fn__16881$G__16871__16890 invoke "middleware.cljc" 8]
     [reitit.middleware$expand_and_transform$fn__17028 invoke "middleware.cljc" 89]
     [clojure.core$keep$fn__8559 invoke "core.clj" 7337]
     [clojure.lang.LazySeq sval "LazySeq.java" 42]
     [clojure.lang.LazySeq seq "LazySeq.java" 51]
     [clojure.lang.RT seq "RT.java" 535]
     [clojure.core$seq__5402 invokeStatic "core.clj" 137]
     [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 24]
     [clojure.core.protocols$fn__8146 invokeStatic "protocols.clj" 75]
     [clojure.core.protocols$fn__8146 invoke "protocols.clj" 75]
     [clojure.core.protocols$fn__8088$G__8083__8101 invoke "protocols.clj" 13]
     [clojure.core$reduce invokeStatic "core.clj" 6828]
     [clojure.core$into invokeStatic "core.clj" 6895]
     [clojure.core$into invoke "core.clj" 6887]
     [reitit.middleware$expand_and_transform invokeStatic "middleware.cljc" 90]
     [reitit.middleware$expand_and_transform invoke "middleware.cljc" 85]
     [reitit.middleware$compile_result invokeStatic "middleware.cljc" 117]
     [reitit.middleware$compile_result invoke "middleware.cljc" 112]
     [reitit.ring$compile_result$__GT_endpoint__17332 invoke "ring.cljc" 38]
     [reitit.ring$compile_result$fn__17339 invoke "ring.cljc" 54]
     [clojure.lang.PersistentArrayMap kvreduce "PersistentArrayMap.java" 377]
     [clojure.core$fn__8437 invokeStatic "core.clj" 6845]
     [clojure.core$fn__8437 invoke "core.clj" 6830]
     [clojure.core.protocols$fn__8167$G__8162__8176 invoke "protocols.clj" 175]
     [clojure.core$reduce_kv invokeStatic "core.clj" 6856]
     [clojure.core$reduce_kv invoke "core.clj" 6847]
     [reitit.ring$compile_result invokeStatic "ring.cljc" 51]
     [reitit.ring$compile_result invoke "ring.cljc" 32]
     [reitit.impl$compile_route invokeStatic "impl.cljc" 115]
     [reitit.impl$compile_route invoke "impl.cljc" 114]
     [reitit.impl$compile_routes$fn__16383 invoke "impl.cljc" 118]
     [clojure.core$keep$fn__8559 invoke "core.clj" 7337]
     [clojure.lang.LazySeq sval "LazySeq.java" 42]
     [clojure.lang.LazySeq seq "LazySeq.java" 51]
     [clojure.lang.RT seq "RT.java" 535]
     [clojure.core$seq__5402 invokeStatic "core.clj" 137]
     [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 24]
     [clojure.core.protocols$fn__8146 invokeStatic "protocols.clj" 75]
     [clojure.core.protocols$fn__8146 invoke "protocols.clj" 75]
     [clojure.core.protocols$fn__8088$G__8083__8101 invoke "protocols.clj" 13]
     [clojure.core$reduce invokeStatic "core.clj" 6828]
     [clojure.core$into invokeStatic "core.clj" 6895]
     [clojure.core$into invoke "core.clj" 6887]
     [reitit.impl$compile_routes invokeStatic "impl.cljc" 118]
     [reitit.impl$compile_routes invoke "impl.cljc" 117]
     [reitit.core$router invokeStatic "core.cljc" 372]
     [reitit.core$router invoke "core.cljc" 345]
     [reitit.ring$router invokeStatic "ring.cljc" 106]
     [reitit.ring$router invoke "ring.cljc" 77]
     [phoenix.http$eval23803 invokeStatic "NO_SOURCE_FILE" 70]
     [phoenix.http$eval23803 invoke "NO_SOURCE_FILE" 70]
     [clojure.lang.Compiler eval "Compiler.java" 7177]
     [clojure.lang.Compiler eval "Compiler.java" 7167]
     [clojure.lang.Compiler eval "Compiler.java" 7132]
     [clojure.core$eval invokeStatic "core.clj" 3214]
     [clojure.core$eval invoke "core.clj" 3210]
     [clojure.main$repl$read_eval_print__9086$fn__9089 invoke "main.clj" 437]
     [clojure.main$repl$read_eval_print__9086 invoke "main.clj" 437]
     [clojure.main$repl$fn__9095 invoke "main.clj" 458]
     [clojure.main$repl invokeStatic "main.clj" 458]
     [clojure.main$repl doInvoke "main.clj" 368]
     [clojure.lang.RestFn invoke "RestFn.java" 1523]
     [nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 79]
     [nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 55]
     [nrepl.middleware.interruptible_eval$interruptible_eval$fn__935$fn__939 invoke "interruptible_eval.clj" 142]
     [clojure.lang.AFn run "AFn.java" 22]
     [nrepl.middleware.session$session_exec$main_loop__1036$fn__1040 invoke "session.clj" 171]
     [nrepl.middleware.session$session_exec$main_loop__1036 invoke "session.clj" 170]
     [clojure.lang.AFn run "AFn.java" 22]
     [java.lang.Thread run "Thread.java" 834]]}}
            exception.cljc:   19  reitit.exception$exception/invokeStatic
            exception.cljc:   15  reitit.exception$exception/invoke
                 core.cljc:  395  reitit.core$router/invokeStatic
                 core.cljc:  345  reitit.core$router/invoke
                 ring.cljc:  106  reitit.ring$router/invokeStatic
                 ring.cljc:   77  reitit.ring$router/invoke
                      REPL:   70  phoenix.http/eval23803
                      REPL:   70  phoenix.http/eval23803
             Compiler.java: 7177  clojure.lang.Compiler/eval
             Compiler.java: 7167  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   79  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   55  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  142  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  171  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  170  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  834  java.lang.Thread/run

@miikka miikka added the bug label Jul 24, 2020
@miikka
Copy link
Contributor

miikka commented Jul 24, 2020

Thanks for the reproduction example. Seems like a bug; further investigation and PR welcome!

@ikitommi
Copy link
Member

ikitommi commented Aug 6, 2020

Oh, didn't think how the hiccup-syntax meta-merges. Not good:

(meta-merge.core/meta-merge
  {:parameters {:path [:map [:a string?]]}}
  {:parameters {:path [:map [:b string?]]}})
; => {:parameters {:path [:map [:a string?] :map [:b string?]]}}

this is what should happen:

(malli.util/merge
  [:map [:a 'string?]]
  [:map [:b 'string?]])
; => [:map [:a string?] [:b string?]]

... might need to add a callback to core-router to handle certain paths with custom logic. This is not just malli, also spec and plumatic are effected:

(require '[spec-tools.data-spec :as ds])

(meta-merge.core/meta-merge
  {:parameters {:path {(ds/opt :a) string?}}}
  {:parameters {:path {:a string?}}})
; => {:parameters {:path {#spec_tools.data_spec.OptionalKey{:k :a} string?, :a string?}}}
(require '[schema.core :as s])

(meta-merge.core/meta-merge
  {:parameters {:path {(s/optional-key :a) s/Str}}}
  {:parameters {:path {:a s/Str}}})
; => {:parameters {:path {#schema.core.OptionalKey{:k :a} java.lang.String, :a java.lang.String}}}

@Deraen Deraen changed the title malli parameters coercion merging broken Malli parameters merging broken Feb 26, 2021
@Deraen
Copy link
Member

Deraen commented Feb 26, 2021

Changing meta-merge logic would be inconvenient, but maybe it is possible to handle certain paths, like :parameters before or after meta-merge.

@ikitommi
Copy link
Member

I would add a new protocol Mergable and make meta-merge, or it's successor use that protocol for doing merge logic, e.g. (-merge [this a b]) method. Then, I would add a hook to core router to enable pre-merge manipulation of route data. Coercion would plug into that and inject reified Mergable to parameters. We could also make nil satisfy the protocol and make it so that the last nil wins (weavejester/meta-merge#11).

bwalex pushed a commit to bwalex/reitit that referenced this issue Dec 12, 2021
Deraen added a commit that referenced this issue Dec 25, 2021
@Rovanion
Copy link

It seems :parameters merging is still broken, but in slightly different way:

(ns tove.routes
  (:require
   [reitit.ring           :as ring]
   [reitit.ring.coercion  :as rrc]
   [reitit.coercion.malli :as mc]))

(ring/router
 [["/temperaturm%C3%A4tningar"
   [""                           {:get  identity}]
   ["/:odlingsplats"             {:parameters {:path [:map [:odlingsplats :string]]}}
    [""                          {:get  identity}]
    ["/-/new"                    {:get  identity :post identity}]
    ["/:tidpunkt"                {:parameters {:path [:map [:tidpunkt :string]]}}
     ["/edit"                    {:get identity}]]]]]
 {:data {:middleware [rrc/coerce-request-middleware]
         :coercion   mc/coercion}})

;;;

1. Unhandled clojure.lang.ExceptionInfo :malli.core/invalid-ref {:ref :map} {:ref :map} {:type :malli.core/invalid-ref, :message :malli.core/invalid-ref, :data {:ref :map}, :reitit.exception/cause #error { :cause ":malli.core/invalid-ref {:ref :map}" :data {:type :malli.core/invalid-ref, :message :malli.core/invalid-ref, :data {:ref :map}} :via [{:type clojure.lang.ExceptionInfo :message ":malli.core/invalid-ref {:ref :map}" :data {:type :malli.core/invalid-ref, :message :malli.core/invalid-ref, :data {:ref :map}} :at [malli.core$_fail_BANG_ invokeStatic "core.cljc" 136]}] :trace [[malli.core$_fail_BANG_ invokeStatic "core.cljc" 136] [malli.core$_fail_BANG_ invoke "core.cljc" 134] [malli.core$_parse_entry invokeStatic "core.cljc" 448] [malli.core$_parse_entry invoke "core.cljc" 401] [malli.core$_eager_entry_parser invokeStatic "core.cljc" 469] [malli.core$_eager_entry_parser invoke "core.cljc" 450] [malli.core$_create_entry_parser invokeStatic "core.cljc" 483] [malli.core$_create_entry_parser invoke "core.cljc" 480] [malli.core$_map_schema$reify__16327 _into_schema "core.cljc" 946] [malli.core$into_schema invokeStatic "core.cljc" 1921] [malli.core$into_schema invoke "core.cljc" 1912] [malli.core$schema invokeStatic "core.cljc" 1982] [malli.core$schema invoke "core.cljc" 1963] [malli.experimental.lite$schema invokeStatic "lite.cljc" 14] [malli.experimental.lite$schema invoke "lite.cljc" 14] [reitit.coercion.malli$create$fn__25099$fn__25100 invoke "malli.cljc" 142] [reitit.coercion.malli$create$fn__25099 invoke "malli.cljc" 142] [reitit.coercion.malli$create$reify__25104 _request_coercer "malli.cljc" 185] [reitit.coercion$request_coercer invokeStatic "coercion.cljc" 80] [reitit.coercion$request_coercer invoke "coercion.cljc" 73] [reitit.coercion$request_coercers$iter__24857__24861$fn__24862 invoke "coercion.cljc" 125] [clojure.lang.LazySeq sval "LazySeq.java" 42] [clojure.lang.LazySeq seq "LazySeq.java" 51] [clojure.lang.RT seq "RT.java" 535] [clojure.core$seq__5467 invokeStatic "core.clj" 139] [clojure.core$filter$fn__5962 invoke "core.clj" 2826] [clojure.lang.LazySeq sval "LazySeq.java" 42] [clojure.lang.LazySeq seq "LazySeq.java" 51] [clojure.lang.RT seq "RT.java" 535] [clojure.core$seq__5467 invokeStatic "core.clj" 139] [clojure.core$seq__5467 invoke "core.clj" 139] [reitit.coercion$request_coercers invokeStatic "coercion.cljc" 127] [reitit.coercion$request_coercers invoke "coercion.cljc" 122] [reitit.ring.coercion$fn__25289 invokeStatic "coercion.cljc" 35] [reitit.ring.coercion$fn__25289 invoke "coercion.cljc" 27] [reitit.middleware$eval20005$fn__20007 invoke "middleware.cljc" 68] [reitit.middleware$eval19875$fn__19876$G__19866__19885 invoke "middleware.cljc" 8] [reitit.middleware$eval19997$fn__19998 invoke "middleware.cljc" 51] [reitit.middleware$eval19875$fn__19876$G__19866__19885 invoke "middleware.cljc" 8] [reitit.middleware$expand_and_transform$fn__20023 invoke "middleware.cljc" 89] [clojure.core$keep$fn__8649 invoke "core.clj" 7405] [clojure.lang.LazySeq sval "LazySeq.java" 42] [clojure.lang.LazySeq seq "LazySeq.java" 51] [clojure.lang.RT seq "RT.java" 535] [clojure.core$seq__5467 invokeStatic "core.clj" 139] [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 24] [clojure.core.protocols$fn__8236 invokeStatic "protocols.clj" 75] [clojure.core.protocols$fn__8236 invoke "protocols.clj" 75] [clojure.core.protocols$fn__8178$G__8173__8191 invoke "protocols.clj" 13] [clojure.core$reduce invokeStatic "core.clj" 6886] [clojure.core$into invokeStatic "core.clj" 6958] [clojure.core$into invoke "core.clj" 6950] [reitit.middleware$expand_and_transform invokeStatic "middleware.cljc" 90] [reitit.middleware$expand_and_transform invoke "middleware.cljc" 85] [reitit.middleware$compile_result invokeStatic "middleware.cljc" 117] [reitit.middleware$compile_result invoke "middleware.cljc" 112] [reitit.ring$compile_result$__GT_endpoint__20147 invoke "ring.cljc" 38] [reitit.ring$compile_result$fn__20154 invoke "ring.cljc" 54] [clojure.lang.PersistentArrayMap kvreduce "PersistentArrayMap.java" 429] [clojure.core$fn__8525 invokeStatic "core.clj" 6908] [clojure.core$fn__8525 invoke "core.clj" 6888] [clojure.core.protocols$fn__8257$G__8252__8266 invoke "protocols.clj" 175] [clojure.core$reduce_kv invokeStatic "core.clj" 6919] [clojure.core$reduce_kv invoke "core.clj" 6910] [reitit.ring$compile_result invokeStatic "ring.cljc" 51] [reitit.ring$compile_result invoke "ring.cljc" 32] [reitit.impl$compile_route invokeStatic "impl.cljc" 115] [reitit.impl$compile_route invoke "impl.cljc" 114] [reitit.impl$compile_routes$fn__19390 invoke "impl.cljc" 118] [clojure.core$keep$fn__8649 invoke "core.clj" 7405] [clojure.lang.LazySeq sval "LazySeq.java" 42] [clojure.lang.LazySeq seq "LazySeq.java" 51] [clojure.lang.RT seq "RT.java" 535] [clojure.core$seq__5467 invokeStatic "core.clj" 139] [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 24] [clojure.core.protocols$fn__8236 invokeStatic "protocols.clj" 75] [clojure.core.protocols$fn__8236 invoke "protocols.clj" 75] [clojure.core.protocols$fn__8178$G__8173__8191 invoke "protocols.clj" 13] [clojure.core$reduce invokeStatic "core.clj" 6886] [clojure.core$into invokeStatic "core.clj" 6958] [clojure.core$into invoke "core.clj" 6950] [reitit.impl$compile_routes invokeStatic "impl.cljc" 118] [reitit.impl$compile_routes invoke "impl.cljc" 117] [reitit.core$router invokeStatic "core.cljc" 339] [reitit.core$router invoke "core.cljc" 312] [reitit.ring$router invokeStatic "ring.cljc" 106] [reitit.ring$router invoke "ring.cljc" 77] [tove.routes$eval47912 invokeStatic "form-init16969643940640255909.clj" 130] [tove.routes$eval47912 invoke "form-init16969643940640255909.clj" 130] [clojure.lang.Compiler eval "Compiler.java" 7194] [clojure.lang.Compiler eval "Compiler.java" 7149] [clojure.core$eval invokeStatic "core.clj" 3215] [clojure.core$eval invoke "core.clj" 3211] [nrepl.middleware.interruptible_eval$evaluate$fn__29412$fn__29413 invoke "interruptible_eval.clj" 87] [clojure.lang.AFn applyToHelper "AFn.java" 152] [clojure.lang.AFn applyTo "AFn.java" 144] [clojure.core$apply invokeStatic "core.clj" 667] [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1990] [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1990] [clojure.lang.RestFn invoke "RestFn.java" 425] [nrepl.middleware.interruptible_eval$evaluate$fn__29412 invoke "interruptible_eval.clj" 87] [clojure.main$repl$read_eval_print__9206$fn__9209 invoke "main.clj" 437] [clojure.main$repl$read_eval_print__9206 invoke "main.clj" 437] [clojure.main$repl$fn__9215 invoke "main.clj" 458] [clojure.main$repl invokeStatic "main.clj" 458] [clojure.main$repl doInvoke "main.clj" 368] [clojure.lang.RestFn invoke "RestFn.java" 1523] [nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84] [nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56] [nrepl.middleware.interruptible_eval$interruptible_eval$fn__29445$fn__29449 invoke "interruptible_eval.clj" 152] [clojure.lang.AFn run "AFn.java" 22] [nrepl.middleware.session$session_exec$main_loop__29515$fn__29519 invoke "session.clj" 218] [nrepl.middleware.session$session_exec$main_loop__29515 invoke "session.clj" 217] [clojure.lang.AFn run "AFn.java" 22] [java.lang.Thread run "Thread.java" 833]]}} exception.cljc: 19 reitit.exception$exception/invokeStatic exception.cljc: 15 reitit.exception$exception/invoke core.cljc: 362 reitit.core$router/invokeStatic core.cljc: 312 reitit.core$router/invoke ring.cljc: 106 reitit.ring$router/invokeStatic ring.cljc: 77 reitit.ring$router/invoke REPL: 130 tove.routes/eval47912 REPL: 130 tove.routes/eval47912 Compiler.java: 7194 clojure.lang.Compiler/eval Compiler.java: 7149 clojure.lang.Compiler/eval core.clj: 3215 clojure.core/eval core.clj: 3211 clojure.core/eval interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn/fn AFn.java: 152 clojure.lang.AFn/applyToHelper AFn.java: 144 clojure.lang.AFn/applyTo core.clj: 667 clojure.core/apply core.clj: 1990 clojure.core/with-bindings* core.clj: 1990 clojure.core/with-bindings* RestFn.java: 425 clojure.lang.RestFn/invoke interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn main.clj: 437 clojure.main/repl/read-eval-print/fn main.clj: 437 clojure.main/repl/read-eval-print main.clj: 458 clojure.main/repl/fn main.clj: 458 clojure.main/repl main.clj: 368 clojure.main/repl RestFn.java: 1523 clojure.lang.RestFn/invoke interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 152 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn AFn.java: 22 clojure.lang.AFn/run session.clj: 218 nrepl.middleware.session/session-exec/main-loop/fn session.clj: 217 nrepl.middleware.session/session-exec/main-loop AFn.java: 22 clojure.lang.AFn/run Thread.java: 833 java.lang.Thread/run

@brjann
Copy link

brjann commented Dec 28, 2022

I just got bit by the inability to merge nested path parameters. It would be awesome if reitit could automatically handle merging of both these cases

;; With malli syntax
["/foo/:foo"
 {:parameters {:path [:map [:foo :int]]}}
 ["/bar/:bar"
  {:parameters {:path [:map [:bar :int]]}}]]
;; With malli schemas
["/foo/:foo"
 {:parameters {:path (m/schema [:map [:foo :int]])}}
 ["/bar/:bar"
  {:parameters {:path (m/schema [:map [:bar :int]])}}]]

@ikitommi ikitommi self-assigned this Dec 29, 2022
@ikitommi
Copy link
Member

Assigning myself to this

@Deraen
Copy link
Member

Deraen commented Dec 29, 2022

@ikitommi Check the latest mentioned PR also (#561). At least that would solve providing API that isn't specific to parameters value, but it leaves it to the user to solve how to apply certain merge logic to one parameter.

I think one option we talked about was that the merging rules could be given as a map (similar to exception handlers), the key would be a top-level data key, and the default would be the current implementation. That would only allow customizing rules for top-level data, but that is probably enough for most cases, and users could still implement more complex logic by configuring the default merge handler.

@ikitommi
Copy link
Member

Thanks, will check that.

@ikitommi
Copy link
Member

I think I have a found a fix for this:

  1. new ctrl.merge implementation, replacing meta-merge (allows also replacing a brach of a tree with nil!)
  2. hooking it to do route data merging via :meta-merge option

Looks like:

(require '[reitit.ring :as ring]
         '[malli.util :as mu]
         '[reitit.core :as r]
         '[ctrl.merge :as cm])

(defn ring-path-map [f]
  [[[:parameters cm/any] f]
   [[ring/http-methods :parameters cm/any] f]
   [[:responses cm/any :body] f]
   [[ring/http-methods :responses cm/any :body] f]])

(defn malli-merge [x y _] (mu/merge x y))

(-> (ring/router
     ["/api" {:parameters {:header [:map ["Api" :string]]}}
      ["/math/:x" {:parameters {:path [:map [:x :int]]
                                :query [:map [:b :string]]
                                :header [:map ["Math" :string]]}
                   :responses {200 {:body [:map [:total :int]]}
                               500 {:description "fail"}}}
       ["/plus/:y" {:get {:parameters {:query ^:replace [:map [:a :int]]
                                       :body [:map [:b :int]]
                                       :header [:map ["Plus" :string]]
                                       :path [:map [:y :int]]}
                          :responses {200 {:body [:map [:total2 :int]]}
                                      500 {:description "fail"}}
                          :handler (constantly {:status 200, :body "ok"})}}]]]
     {:meta-merge #(cm/merge %1 %2 {::cm/path-map (ring-path-map malli-merge)})})
    (ring/ring-handler)
    (ring/get-router)
    (r/compiled-routes)
    (last)
    (last)
    :get
    :data)
;{:parameters {:header [:map
;                       ["Api" :string]
;                       ["Math" :string]
;                       ["Plus" :string]],
;              :path [:map
;                     [:x :int]
;                     [:y :int]],
;              :query [:map [:a :int]],
;              :body [:map [:b :int]]},
; :responses {200 {:body [:map
;                         [:total :int]
;                         [:total2 :int]]}
;             500 {:description "fail"}},
; :handler #object[clojure.core$constantly$fn__5740]}

TODO:

  • this should be part of the Coercion protocol?
  • we should only allow 1 coercion per route tree, e.g. one can't merge Spec, Schema and Malli together. This is a BREAKING CHANGE, but for the better?

@ikitommi
Copy link
Member

proposing #626 as a fix for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants