This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-09-11
Channels
- # announcements (7)
- # aws (52)
- # babashka (16)
- # beginners (49)
- # bristol-clojurians (1)
- # calva (2)
- # chlorine-clover (26)
- # cider (6)
- # clara (1)
- # clj-kondo (79)
- # cljfx (15)
- # clojure (82)
- # clojure-berlin (2)
- # clojure-czech (1)
- # clojure-europe (26)
- # clojure-france (91)
- # clojure-germany (48)
- # clojure-nl (7)
- # clojure-norway (99)
- # clojure-uk (54)
- # clojurescript (18)
- # code-reviews (9)
- # data-science (2)
- # datalog (15)
- # datomic (15)
- # depstar (20)
- # emacs (4)
- # events (1)
- # fulcro (30)
- # funcool (1)
- # graphql (1)
- # helix (5)
- # jobs (6)
- # kaocha (12)
- # leiningen (8)
- # luminus (1)
- # malli (13)
- # off-topic (73)
- # pathom (12)
- # portal (11)
- # portland-or (1)
- # re-frame (10)
- # reagent (1)
- # reitit (44)
- # remote-jobs (1)
- # ring (19)
- # shadow-cljs (64)
- # tools-deps (32)
Hello. I am playing with reitit
, creating a REST API for datahike
with swagger-ui
. I would like to create an http endpoint for querying. So i have a get
path with a q
request parameter. But when i put :parameters {:query {:q vector?}}
in my router configuration, swagger-ui
goes into endless loading-loop as soon as I open the section for this request. Putting :parameters {:query {:q string?}}`` works, but then I loose the validation. Is there a way to use more sophisticated validations then
int?
, string?
, etc.? Thank you!
(def create-investigation [:map
{:closed true}
[:source [:map
[:id [:string {:min 1 :max 64}]]
[:type [:enum {:swagger/type "string"} "VRN" "DEVICE" "POLICY" "CLAIM" "CONTRACT"]]]]
[:from [:fn {:swagger/type "string" :swagger/format "date-time" :error/message iso8601-message} date-time-parser]]
[:to {:optional true} [:fn {:swagger/type "string" :swagger/format "date-time" :error/message iso8601-message} date-time-parser]]
[:fleetIds {:optional true} [:vector string?]]
[:note {:optional true} string?]
[:tenantId string?]])
:post {:handler (create-investigation app-config)
:swagger {:produces [investigations-create-api-version]}
:parameters {:body specs/create-investigation}}}]
date-time-parser is a function that takes the "thing" and does some primitive formatting of the input to try to cope with the weird stuff that people type in 🙂
And malli is already included in reitit with swagger? Or do I need to somehow connect reitit-swagger and malli?
It seams, it is not. When I put :parameters {:query {:q [:map]}}`` , I get an error:
:reitit.exception{:cause #error { :cause "Unable to resolve spec: :map"
I see. There is reitit.coercion.malli/coercion`` . I have
reitit.coercion.spec
. But it seams not to work with swagger-ui.
@witek both the old and the new swagger-ui are bit buggy. there is https://editor.swagger.io/ to play with what works and what doesn’t. Can cook up a working swagger definition in there?
whatever works there can be created from reitit, if not using the spec|schema|malli->swagger auto converter, there is a way to manually create the swagger spec.
I switched to malli coercion. But now I can not use keyword?
parameters anymore. specifying :parameters {:path {:id keyword?}}
produces error: :malli.core/invalid-schema {:schema {:id #function[clojure.core/keyword?]}}
. How do I specify a parameter as a keyword when using malli coercion?
sadly, there isn’t a decent schema format error tool yet. there is #malli to get help, but you should read https://github.com/metosin/malli README first or just check out the working example from https://github.com/metosin/reitit/tree/master/examples/ring-malli-swagger.
there are examples of the same minalistic swagger app for each of: spec, schema and malli.
Well malli directly (`(m/validate keyword? :mykey)`) works. But providing
`:parameters {:path {:id keyword?}}`` in reitit produces error: :malli.core/invalid-schema {:schema {:id #function[clojure.core/keyword?]}}``. The examples have only
int?
parameters, no keyword?
...
Schema:
{:kikka s/Str
(s/optional-key :kukka) s/Int
s/Keyword s/Any}
Spec:
(s/def ::kikka string?)
(s/def ::kukka int?)
(s/keys :req-un [::kikka], :opt-un [:kukka])
Malli:
[:map
[:kikka string?]
[:kukka {:optional true} int?]]
there could be a reitit-side shortcut for allowing to list the keys using a normal map, but malli doesn’t have that
wrote an issue of that, comments welcome: https://github.com/metosin/reitit/issues/434
OK, now I have :parameters {:query [:map [:q [vector? {:swagger/type "string"}]]]
and swagger-ui works. But when I call the request, providing q
as a string (`[:find]`) then reitit coercion fails: "humanized": {"q": ["should be a vector"]}
. So how do I get reitit to convert the string from the request parameter to a vector which is required by my handler? Or do I have to do it manually in my handler?
Oh, I got it! My type needs to be [:fn {:swagger/type "string"} ... ]
not [vector? ...]
.
you can also add decoding logic to the schema. If you want to have a vector, this should work:
[:vector
{:swagger {:type "string", :example "1,2,3"}
:decode/json #(str/split #"," %)}
I recall there is a swagger way of saying that the parameter should be a list of stuff with delimeter x
, so the ui creates a list input out of it (and concats the values with x
)
you can set the swagger type as "array"
and use "collectionFormat"
, here's the guide: https://swagger.io/docs/specification/2-0/describing-parameters/
I'm ok with swagger taking the parameter as a string. I just want it converted to a vector when passed to the request handler. This is what now works for me: :query [:map [:q [:vector {:swagger/type "string" :decode/string edn/read-string} any?]]]
Sometimes I have an error in my request handler and it throws an Exception. The middleware exception/exception-middleware
seams to catch them and write type and class of the exception to the response. But then all other exception info including the stack trace is lost. It is not printed to the output anymore. What is the idiomatic way to get this exception info while development? What ist the idiomatic way to get it logged in production?
I have found reitit.ring.middleware.exception/wrap-log-to-console
, but how do I activate/use it?
:middleware [swagger/swagger-feature
muuntaja/format-middleware
(exceptions/exception-middleware app-config)
parameters/parameters-middleware
coercion/coerce-exceptions-middleware
coercion/coerce-request-middleware
coercion/coerce-response-middleware]}}))
(defn exception-middleware
[app-config]
(exception/create-exception-middleware
(merge
exception/default-handlers
clojure.lang.ExceptionInfo exception-info-handler
::exception/wrap (fn [handler exception request] (log/error exception) (handler exception request))}))))
(defn exception-info-handler
[exception-info _] ; exception(-info) and request (not-used) come from reitit.
(let [uuid (.toString (UUID/randomUUID))
{:keys [cause status error]} (ex-data exception-info)
{:keys [code msg]} (split-error-message (i18n [error]))
formatted-error (format-error error msg cause)
reference (first (split uuid hypen))
body {:code code :reference reference :error formatted-error}]
(condp = status
:404 {:status not-found :body body}
:409 {:status conflict :body body}
{:status bad-request :body body})))
so I return to the client a body of {:code "foo" :reference "bar" :error "blah blah formatted"}