Support `s/keys` key-groups in openapi spec
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.
See here: https://github.com/metosin/spec-tools/blob/8a3284f1a1d43636648f28a7f32e6122c4a1912a/src/spec_tools/json_schema.cljc#L187
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-groupsthing 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:allOfwith (potentially) nestedoneOfs. That's actually very easy to do inaccept-spec 'clojure.spec.alpha/keys, but it does mean thatimpl/parse-keysneeds to be re-worked to return either{:required [x]}(for the base case of non-group), or{:oneOf [x y z]}(for any top-levelorgroups). I guess what I'm trying to say is thatimpl/parse-keysis the main thing that needs to touched (any changes inaccept-spec 'clojure.spec.alpha/keysshould be very straight -forward - e.g. replacing:requiredwith: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... :+1:
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...