metosin / malli

High-performance data-driven data specification library for Clojure/Script.
Eclipse Public License 2.0
1.44k stars 204 forks source link

PlantUML doesn't support keyword schemas #916

Open jtrunick opened 1 year ago

jtrunick commented 1 year ago

I changed the Address example so that Burger is a keyword, the generated plant uml gives an error when rendering.

(def Address [: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"]]]]]]}}

tvaisanen commented 9 months ago

It looks like when using the keyword as a reference the schema resolves the type differently compared to a string reference or a qualified keyword reference:

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

  (def s [:schema
          {:registry {:a :any
                      :a/b :any
                      "c" :any
                      "d" [:map
                           [:x :a]
                           [:y :a/b]
                           [:z "c"]]}}
          "d"])

  (m/schema s {})

  ;; returns the following
  [:schema
   {:registry
    {:a :any,
     :a/b :any,
     "c" :any,
     "d" [:map
          [:x :any] ;; keyword is resolved
          [:y :a/b] ;; qualified keyword is referenced
          [:z "c"]] ;; string is referenced
     }}
   "d"]

In the case a keyword ref is used we generate an abstract class representing the referenced value.

  (require '[malli.plantuml :as p])

  (p/transform
   (m/schema
    [:schema
     {:registry {"b" :any
                 "d" [:map [:x "b"]]
                 :a :any
                 :b [:map [:x :a]] ;; this breaks the rendering
                 :c [:map [:y :b]]}}
     "d"]
    {}))
@startuml
entity :a {
 :any
}
entity :b {
 :x :any
}
entity :c {
 :y ":c$Y"
}
abstract :c$Y {
 :x :any
}
entity b {
 :any
}
entity d {
 :x "b"
}
:c *-- :c$Y /' this will cause the render to fail since colon is reserved character '/
d o-- b : colon used for labels
@enduml

By removing the invalid format line the graph can be rendered without the edge.

sample_2

But now the graph has an object :c$Y that should just be :b. and because of this just stripping the : from the name during plantuml generation wouldn't work as a solution and it would consider :b and "b" as equal.

To my understanding what is happening right now malli.plantuml/transform generates a new abstract class instead of referencing the type :b, since it is not recognized as a reference by malli.core/-reference?.


To summarize, AFAIK : can not be used in node names when defining edges in PlantUML and this causes the rendering to fail. I need to do some more investigating if it'd be possible to encode the colon into the edge definitions.

tvaisanen commented 9 months ago

It is possible to alias the entity names to avoid the rendering error.

@startuml
entity :a {
 :any
}
entity :b {
 :x :any
}
entity ":c" as COLON_c {
 :y ":c$Y"
}
abstract ":c$Y" as COLON_c$Y {
 :x :any
}
entity b {
 :any
}
entity d {
 :x "b"
}
COLON_c *-- COLON_c$Y
d o-- b : colon used for labels
@enduml

Which results in:

plantuml-ok

I'll create a PR for this.