Fork me on GitHub
#malli
<
2024-04-25
>
jmckitrick12:04:09

I’m currently using data-spec to add swagger info to routes for swagger-ui. We want to migrate to malli . I’m looking at malli.swagger/transform but cannot tell where to integrate it into the route data. Suggestions?

ikitommi07:04:08

you can annotate the schemas with :swagger/description etc. Have you looked the https://github.com/metosin/malli?tab=readme-ov-file#swagger2?

jmckitrick10:04:24

I’ve figured most of it out, with just a few details left. How can I override the default ‘inline_model’ labels and replace them with my schema names?

jmckitrick12:04:39

Right now the schema is in :post -> :parameters -> :body

vemv13:04:54

Is anyone regularly inspecting/navigating Malli schemas in a tool like REBL, portal, the CIDER inspector, etc? My question is two-faced • what exact tool are you using • what pattern are you using for defining schemas ◦ For me the tricky part is keeping everything defined as plain data (with all the benefits it carries), while keeping names (e.g. the Foo in (def Foo :string)) There are surely plenty of choices (vars, registries, and more), and I wouldn't say it's hard to hack something. But I wonder if anyone is doing something remarkable today

vemv13:04:11

In CIDER we have a spec browser https://docs.cider.mx/cider/usage/misc_features.html#browsing-the-clojure-spec-registry , as of today we couldn't realistically offer that for Malli I believe as it has a far wider variety of usage patterns

Ben Sless13:04:34

Since malli schemas are Just Data that's often enough for me when inspecting and navigating

vemv13:04:29

Names are lost though, and things can get expanded beyond comprehensibility

Ben Sless14:04:50

True, which is why I'm stoked for var ref schemas

vemv14:04:47

In principle I want to like them, but then doesn't stuff stop being data? Simple example:

(def Foo
  (into NewFoo
        [[:id Id]]))
This won't work if NewFoo became #'NewFoo

vemv14:04:09

...there's mu/merge and friends, but those return a reify, i.e. also not-data

Ben Sless14:04:42

oh right, they return an into-schema

Ben Sless15:04:07

I'm spoiled by the inspector that freely digs into closed over fields

vemv16:04:31

Yeah it shoulnd't be bad at all, but as have the chance to design some things from scratch I wanted to see if there's an optimal pattern

vemv16:04:02

Most likely I'd go for a keyword registry, and perhaps datafy/nav somewhere in the mix

Tommi Martin13:04:04

Hello, would it be possible to get pointed to the syntax of [:multi {:dispatch custom-dispatch} schema? What is the expected return of the custom-dispatch ? In examples it is a keyword or a string. but what about cases where the multi is called from inside ::m/default ? in those cases the dispatch function receives a map of all fields that didn't match the schema. I'm sorry maybe i was blind and couldn't find the answer in documentation. Code example in a thread:

Tommi Martin13:04:28

(require '[malli.core :as m])

  (defn test-dp
    [m]
    (clojure.pprint/pprint m)
    #_"what am I supposed to return?")

  (m/validate
   [:map
    [:name :string]
    [:field1 :string]
    [::m/default
     [:multi {:dispatch test-dp}
      ["service" [:map-of :keyword [:map
                                    [:type :string]
                                    [:name :string]
                                    [:new-field :string]]]]
      ["source" [:map-of :keyword [:map
                                   [:type :string]
                                   [:name :string]
                                   [:some-other-field :string]]]]]]]
   {:name "test"
    :field1 "test-field"
    :random1 {:rng {:type "service"
                    :name "test1"
                    :new-field "some-value"}}
    :random2 {:rng2 {:type "source"
                     :name "test2"
                     :some-other-field "some-other-value"}}})

Tommi Martin13:04:51

value of m in test-dp with this code:

{:random1
 {:rng {:type "service", :name "test1", :new-field "some-value"}},
 :random2
 {:rng2
  {:type "source",
   :name "test2",
   :some-other-field "some-other-value"}}}

ambrosebs15:04:25

(m/validate [:multi {:dispatch D} [C1 S1] [C2 S2] [::m/default S3]] V)
=>
(({C1 (validator S1), C2 (validator S2)} (D V) (validator S3)) V)
so the keys of your clauses need to be = to the result of applying the dispatch function to the validated value.

ambrosebs16:04:04

I think in your case you need to pull the :multi up to the top-level to wrap your :map if you want the dispatch to see those fields.

ambrosebs16:04:07

Actually maybe not, you just need to write a dispatch that returns either "service" or "source" depending on which branch you want.

ambrosebs16:04:02

That information seems to be in the dissoc'ed map.

Tommi Martin06:04:59

There was an issue in default usage of the schema (i think) I've corrected it in the following:

(require '[malli.core :as m])

  (defn test-dp
    [m]
    (clojure.pprint/pprint m)
    #_"Should the dispatch return?"
    "service")

#_"value of m in test-dp when both randomX fields are present:"
; {:random1
;  {:rng {:type "service", :name "test1", :new-field "some-value"}},
;  :random2
;  {:rng2
;   {:type "source",
;    :name "test2",
;    :some-other-field "some-other-value"}}}
  
  (m/validate
   [:map
    [:name :string]
    [:field1 :string] 
    [::m/default 
     [:multi {:dispatch test-dp}
      ["service" [:map-of
                  :keyword
                  [:map-of
                   :keyword
                   [:map
                    [:type :string]
                    [:name :string]
                    [:new-field :string]]]]]
      ["source" [:map-of
                 :keyword
                 [:map-of
                  :keyword
                  [:map
                   [:type :string]
                   [:name :string]
                   [:some-other-field :string]]]]]]]]
   {:name "test"
    :field1 "test-field"
    :random1 {:rng {:type "service"
                    :name "test1" 
                    :new-field "some-value"}}
    #_:random2 #_{:rng2 {:type "source"
                     :name "test2"
                     :some-other-field "some-other-value"}}})
New addition in the schema was an addition map-of nesting at the top. If you remove the :random2 from the data to be validated and return a string service from the dispatch function, it works. But if both :random1 and :random2 are present it feels like the dispatch function breaks. as it gets a merged map in as a parameter. If the expected return is a string. The dispatch will not work with the schema described as it cannot return "random1 is a service" and "random2 is a source" at the same time.

Tommi Martin06:04:46

It looks like the solution I'm looking for here could be wrong. Would anyone have examples of malli schemas that fulfil the following conditions: 1. The schema is for a map 2. The map has :keyword :string/double/int/boolean/etc. values 3. Map has atleast 2 keys that are change based on external factors and cannot be hardcoded into the map. The content of these keys is a map and it's structure does not change regardless or if the key changes? 4. The content of the submaps is validated

Tommi Martin10:04:36

It turns out i was using the multi and the default incorrectly. A working solution for this is as follows:

(require '[malli.core :as m])

  (m/validate
   [:map
    [:name :string]
    [:field1 :string]
    [::m/default
     [:map-of
      :keyword
      [:map-of :keyword 
       [:multi {:dispatch :type}
        ["service" [:map
                    [:type :string]
                    [:name :string]
                    [:new-field :string]]]
        ["source" [:map
                   [:type :string]
                   [:name :string]
                   [:some-other-field :string]]]]]]]]
   {:name "test"
    :field1 "test-field"
    :random1 {:rng {:type "service"
                    :name "test1"
                    :new-field "some-value"}}
    :random2 {:rng2 {:type "source"
                     :name "test2"
                     :some-other-field "some-other-value"}}})
Notice how the ::m/default syntax has changed. it no longer has multiple :map-of :keyword [:map-of :keyword [:map... values I was way too tunnel visioned on keeping the declarations isolated. By having multiple map-of values in the the multi declaration and the dispatch. It caused malli to dump all of the remaining fields into the dispatch function. Placing the map-of values before the multi declaration changed the data sent to the dispatch function from what you see in previous messages to:
{:type "service",
 :name "test1",
 :new-field "some-value"}
And as expected the dispatch function is now called several times, once per each data object. With this, returning the string is possible. Thank you @U055XFK8V for your help and sorry for taking your time with poor explanations.

👍 2