Support `s/keys` key-groups in openapi spec
jimpil opened this issue · comments
Hello,
I was under the impression that specs like the one below, didn't show correctly on the swagger-v2 schema because it doesn't support this kind of one-of
semantics. I believe that openapi-v3 does support it, but unfortunately, the same thing happens - all the keys show up as required (i.e. with a red asterisk).
(s/keys :req-un [(or ::foo ::bar ::baz)])
Any thoughts?
[EDIT]:
Sorry, I should have mentioned that I am using the recently released 0.7.0-alpha1
.
spec-tools isn't sophisticated enough to know how to convert that into json-schema.
Hi again.
I may be able to help on this after all - here is some initial (somewhat encouraging) info:
The whole thing seemed impossible when I first looked at it (several months back), but I've had two significant realisations since...
- First of all, I realised that the whole
key-groups
thing really only applies for a single case - and that is theouter OR, with potentially nesting ANDs
- i.e.(or ::foo (and ::bar ::baz))
. In other words, it is meaningless to have an outer AND (as that is already implied). This makes finding these (at the top-level) super easy. - Secondly, I randomly stumbled upon this last week, and I immediately thought of this issue! This is basically the other piece of the puzzle - instead of emitting a flat
{:required [...]}
, reitit could emit an:allOf
with (potentially) nestedoneOf
s. That's actually very easy to do inaccept-spec 'clojure.spec.alpha/keys
, but it does mean thatimpl/parse-keys
needs to be re-worked to return either{:required [x]}
(for the base case of non-group), or{:oneOf [x y z]}
(for any top-levelor
groups). I guess what I'm trying to say is thatimpl/parse-keys
is the main thing that needs to touched (any changes inaccept-spec 'clojure.spec.alpha/keys
should be very straight -forward - e.g. replacing:required
with:allOf
).
In any case, as I said I'll try to have a stub at this of not this week, then the following after it... 👍
Ok, so here is an attempt at discovering key-groups:
(defn- parse-required-with [f x]
(if (seq? x) ;; key-group
(let [k (condp = (first x)
'or :oneOf ;; or :anyOf ?
'and :allOf
(throw
(IllegalArgumentException. "unsupported key-group expression")))]
{k (mapv (partial parse-required-with f) (next x))})
{:required [(f x)]}))
(def parse-req (partial parse-required-with impl/qualified-name))
(def parse-req-un (partial parse-required-with name))
(parse-req-un '(or :foo (and :bar :baz))) ;; => {:oneOf [{:required ["foo"]} {:allOf [{:required ["bar"]} {:required ["baz"]}]}]}
You can get a list of those maps by simply mapping over the req(-un) vectors, and wrapping everything in top level {:allOf [...]}
.
Leaving this here for future reference - as I said, I'll try to find some time to prepare a real PR sometime this coming week or the next...