malli icon indicating copy to clipboard operation
malli copied to clipboard

Unexpected reference while generating swagger specs with custom types

Open souenzzo opened this issue 2 years ago • 5 comments

I'm trying to use custom types and customize its swagger spec output.

On this example, I simplified a lot. In my actual code, :my/int is created via malli.core/-simple-schema.

But we can see the unexpected behavior in this code:

(require '[malli.core :as m]
         '[malli.swagger :as ms])
(ms/transform [:map
               [:ok :int]
               [:notok :my/int]]
  {:registry (assoc (m/default-schemas)
               :my/int (m/-simple-schema {:type          :my/int
                                          :pred          int?
                                          :property-pred (m/-min-max-pred nil)}))})
=> {:type        "object",
    :properties  {:ok {:type   "integer"
                       :format "int64"}
                  :notok
                  ;; expected
                  #_{:type "integer", :format "int64"}
                  ;; actual
                  {:$ref "#/definitions/:my/int"}}
    :required    [:ok :notok]
    ;; not expected
    :definitions {:my/int {:type "integer", :format "int64"}}}

I don't want to generate a ref to this type. I just want to put it "in-place"

souenzzo avatar Aug 12 '22 12:08 souenzzo

It seems Malli puts "in-place" only non-qualified keywords, no idea why

(malli.swagger/transform
   (mu/closed-schema
    [:map {:registry {:eita/schema :int}}
     [:a :eita/schema]]))
;; =>
{:type "object",
 :properties {:a {:type "integer", :format "int64"}},
 :required [:a]}


(malli.swagger/transform
   [:map {:registry {:eita/schema :int}}
    [:a :eita/schema]])
;; =>
{:type "object",
 :properties {:a {:$ref "#/definitions/:eita/schema"}},
 :required [:a],
 :definitions #:eita{:schema {:type "integer", :format "int64"}}}

pfeodrippe avatar Aug 12 '22 20:08 pfeodrippe

I see. Ideas welcome on how to change this in an elegant way.

ikitommi avatar Aug 28 '22 14:08 ikitommi

More wired behaviors. Still trying to understand the root cause.

(def opts
  {:registry (assoc (m/default-schemas)
               :my/int (m/-simple-schema
                         ;; exactly the same as :int
                         {:type          :my/int
                          :pred          int?
                          :property-pred (m/-min-max-pred nil)}))})
(m/ast :int opts) #_#_=> {:type :int}
(m/ast [:int] opts) #_#_=> {:type :int}
;; wired: m/ast behaves differently between equivalent notations
;; root cause: https://github.com/metosin/malli/blob/0.8.9/src/malli/core.cljc#L1994
;; seems that -reference? function is the problem
(m/ast :my/int opts) #_#_=> {:type :malli.core/schema, :value :my/int}
(m/ast [:my/int] opts) #_#_=> {:type :my/int}

;; transform with kw: not what I expect
(ms/transform :my/int opts) #_#_=> {:$ref "#/definitions/:my/int", :definitions {:my/int {}}}
;; transform with []: wrong
(ms/transform [:my/int] opts) #_#_=> {}

souenzzo avatar Aug 31 '22 13:08 souenzzo

Digged into this. I think the bug is actually how the non-qualified types are used, e.g.

(m/form [:schema {:registry {:my-int :int}} :my-int])
; => [:schema {:registry {:my-int :int}} :int]

.. the last child is of type :malli.core/schema but due to malli internals to form will show in inlined value. This is not good as we are losing information. This should be fixed.

Another - how do define qualified keys (or string) that are not reference values, just values. I think a good way would be to change internals so that any value in the registry that is a IntoSchema instance, should not be handled as a reference, just a type instead. This would allow tools like the swagger/openapi transformers not to create references out of those.

ikitommi avatar Sep 03 '22 12:09 ikitommi

https://github.com/metosin/malli/issues/749

ikitommi avatar Sep 03 '22 12:09 ikitommi