spec->malli was mentioned here a while ago. is anyone aware if such a thing exists?
@emccue currently itβs a let
. Could be something else too. What kind of better errors would you expect? The reference information is available over there, just not used I guess in explain:
(m/explain
[:map {:registry {"ID" :int}}
[:id "ID"]]
{:id "123"})
;{:schema [:map {:registry {"ID" :int}} [:id "ID"]],
; :value {:id "123"},
; :errors (#Error{:path [:id], :in [:id], :schema :int, :value "123"})}
@willier not yet. interested in doing? π
hmm, probably need a black belt in macro-fu to do this job
Or a spec-tools
belt?
@willier you can inspect specs at runtime. spec-tools has something called a walker and coax is a library from exoscale to do coercion based on specs, I think they are doing something similar
spec-tools has both walker and a visitor π visitor is the right tool for the job i believe: https://cljdoc.org/d/metosin/spec-tools/0.10.5/doc/spec-visitor
json schma with it: https://cljdoc.org/d/metosin/spec-tools/0.10.5/doc/json-schemas
oh interesting... i will have a look into it - thanks!
also, there are progression tests in spec-tools for broken forms: if the core would be fixed: https://github.com/metosin/spec-tools/blob/master/test/cljc/spec_tools/visitor_all_test.cljc
oh, no more, tests disabled and the last form is fixed. great!
If you have a map that references other schemas, like this [:map ::some/ref ::some-other/ref]
how do you make ::some/ref
and ::some-other/ref
optional?
I tried [:and {:optional true} ::some/ref]
but that was invalid
You just have to use the sugar-free [:map [::some/ref {:optional true} ::some/ref]]
You could get clever and [:map (#(vector % {:optional true} %) ::some/ref)]
or name and reuse that little fn
[:map ::some/ref]
is just some sugar in :map
, we don't have "first-class properties" beyond that. And :optional
is specific to map entries while :maybe
does something else.
aha ok thanks π
this should also work: [:map [::some/ref {:optional true}]]
, no need to repeat the schema for refs.
nice!
If I'm walking a schema and want to convert all schemas of type ::sq/ref
into :string, but I also want to deref all other refs, how do I do this/
This is what I've got so far:
(defn output-schema
[schema]
(malli/walk
schema
(malli/schema-walker
(fn [schema]
(malli/type schema)
(cond
(= schema ::sq/ref) :string
(= (malli/type schema) :map)
.... do some other stuff
:else schema)))
{::malli/walk-schema-refs true
::malli/walk-refs true}))
But the schema is expanded by the time the check happens, so it failswhat does (malli/walk schema (malli/schema-walker identity) {::malli/walk-schema-refs true, ::malli/walk-refs true})
do?
that might expand all automatically
(m/walk
[:schema
{:registry {"Country" [:map
[:name [:enum :FI :PO]]
[:neighbors [:vector [:ref "Country"]]]]
"Burger" [:map
[:name string?]
[:description {:optional true} string?]
[:origin [:maybe "Country"]]
[:price pos-int?]]
"OrderLine" [:map
[:burger "Burger"]
[:amount int?]]
"Order" [:map
[:lines [:vector "OrderLine"]]
[:delivery [:map
[:delivered boolean?]
[:address [:map
[:street string?]
[:zip int?]
[:country "Country"]]]]]]}}
"Order"]
(m/schema-walker #(mu/update-properties % assoc :type (m/type %)))
{::m/walk-schema-refs true})
;[:schema
; {:registry {"Country" [:map [:name [:enum :FI :PO]] [:neighbors [:vector [:ref "Country"]]]],
; "Burger" [:map
; [:name string?]
; [:description {:optional true} string?]
; [:origin [:maybe "Country"]]
; [:price pos-int?]],
; "OrderLine" [:map [:burger "Burger"] [:amount int?]],
; "Order" [:map
; [:lines [:vector "OrderLine"]]
; [:delivery
; [:map [:delivered boolean?] [:address [:map [:street string?] [:zip int?] [:country "Country"]]]]]]},
; :type :schema}
; [:malli.core/schema
; {:type :malli.core/schema}
; [:map
; {:type :map}
; [:lines
; [:vector
; {:type :vector}
; [:malli.core/schema
; {:type :malli.core/schema}
; [:map
; {:type :map}
; [:burger
; [:malli.core/schema
; {:type :malli.core/schema}
; [:map
; {:type :map}
; [:name [string? {:type string?}]]
; [:description {:optional true} [string? {:type string?}]]
; [:origin
; [:maybe
; {:type :maybe}
; [:malli.core/schema
; {:type :malli.core/schema}
; [:map
; {:type :map}
; [:name [:enum {:type :enum} :FI :PO]]
; [:neighbors [:vector {:type :vector} [:ref {:type :ref} "Country"]]]]]]]
; [:price [pos-int? {:type pos-int?}]]]]]
; [:amount [int? {:type int?}]]]]]]
; [:delivery
; [:map
; {:type :map}
; [:delivered [boolean? {:type boolean?}]]
; [:address
; [:map
; {:type :map}
; [:street [string? {:type string?}]]
; [:zip [int? {:type int?}]]
; [:country
; [:malli.core/schema
; {:type :malli.core/schema}
; [:map
; {:type :map}
; [:name [:enum {:type :enum} :FI :PO]]
; [:neighbors [:vector {:type :vector} [:ref {:type :ref} "Country"]]]]]]]]]]]]]
not sure if thatβs near what you want to do.
yep, that expands all
Ah I think I said :type
wrong, the type is :malli.core/schema
, what I'm checking for is the schema itself to be ::sq/ref
So is what you're suggesting to do two walks, one to capture information and put it in the properties, and then a second to do the other transformations?
hmm
I suppose I could put some property on the ::sq/ref
schema itself, but it feels like I'm missing a trick
To do it in one traversal you would need a more Visitor-like prewalk where you manually deref the ones you want.
I think that can be done with -walk
and Walker
but only schema-walker
and walk
are documented and stable ATM
thanks
ok, I guess I missed the original point. But there are public walkers like https://github.com/metosin/malli/blob/master/src/malli/util.cljc#L37-L51
is it safe to use those functions and protocols?
> extender api: public vars, name starts with -
, e.g. malli.core/-collection-schema
. Not needed with basic use cases, might evolve during the alpha, follow https://github.com/metosin/malli/blob/master/CHANGELOG.md for details
nice, i see
doing the graphviz thing is a bit whack it turns out cos it passes through all of the constraints to be visualised and it soon gets ugly
So I guess you really have to pare back everything to the simplest of all possible models for nice visuals π
Maybe we could have custom visualization props like we have :error/message
?
yes, or like {:swagger}
if one had a Member, it would be nice for example to have {:viz/parent Org}
on the map and {:viz/type string}
on the name property
and then one could select for those properties when transforming to DOT
@ikitommi I might have been going about this wrong, so maybe this is now a better explanation of what I would like to have ...
(def Id [:re #"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"])
(def Name [:string {:min 3 :max 255}])
(def org-ref :ref)
(def org-ref-viz [:map [org-ref :string]])
(def org-ref-description "Reference (name or ID) of the organisation")
(def org-ref-valid [:map {:title org-ref-description} [org-ref Name]])
(def org-ref-swagger {:swagger {:description org-ref-description
:example "Acme tech, Houston TX"}})
(def Org-Ref [:map {:title (name org-ref)}
[org-ref org-ref-swagger Name]])
(def org-id :id)
(def org-id-viz [:map [org-id :string]])
(def org-id-description "The organisation ID")
(def org-id-valid [:map [org-id Id]])
(def org-id-swagger {:swagger {:description org-id-description
:example (->id)}})
(def Org-Id [:map {:title (name org-id)}
[org-id org-id-swagger Id]])
(def org-viz {"Org" (mu/merge org-id-viz org-ref-viz)})
(def Org (mu/merge Org-Id Org-Ref))
I can't find a way to create org-ref-full
by combining org-ref-valid
with org-ref-swagger
I have added org-ref-viz as that's what I would like to derive from org-ref-valid
At the moment I am OK with doing it the way as it is above cos there is still a lot of value but obviously there seems to be quite a bit of boiler-plate