Fork me on GitHub
#reitit
<
2020-02-12
>
bartuka00:02:27

I usually have some validation in the spec itself if I plan to teach my consumers about some strict rules of the system

bartuka00:02:39

for example, in my case, I cannot have a credit contract that its future value is smaller than the acquisition value. This sort of validation usually is a mistake from the users and I deal with it in the very beginning.

caio00:02:24

makes sense. I guess I get a bit confused at where I should draw the line between contract validation and an internal validation logic (assuming contract validation can get arbitrarily complex) and that makes me think if I should be doing validation at the contract level at all

caio00:02:55

another question: is it possible to spec a response with the headers you expect it to return?

bartuka00:02:56

I have not tried yet, but I imagine that if you try :responses {:headers (s/keys :req-un [::my-key])} I would not be so impressed that it works 😃

ikitommi07:02:12

@boyanb if you use something like Integrant REPL, it should do it for you. There is an integrant sample project.

ikitommi07:02:01

@plins no trivial way to do that, but you can always add a custom middleware/interceptor for the swagger-endpoint and walk & transform all the models in the return. asked many times, happy to hear suggestions how to do this better (or and example to the docs mw to do this)

plins18:02:21

Ill try to do so, and If I succeed Ill post it here to see if its ok then add to the docs or something but If I try to do a request from the swager interface, do you think it will send to the server the kebab-case instead of the camelCase?

plins18:02:16

just to have an idea of the amount of work involved

plins18:02:54

if the swagger js client isnt smart enough than the only option I can think of is adding a middleware (before the validation) to convert all the keys not sure if this is efficient

plins12:02:36

where should I put it?

ikitommi14:02:10

hmm.. the coercion does both transformation & validation, and there is no hook to do anything in between those two steps, but if you want to do it before coercion, add the mw before coercion/coerce-request-middleware. For responses, a separate mw before coercion/coerce-response-middleware.

ikitommi14:02:26

so, 2 mw, one effecting the request, another the response.

ikitommi14:02:00

oh, but for the request-part… the parameters are copied under :parameters by the request-coercion-mddleware, so any step before that, you would need to read those from the original location, e.g. :body-params from :body-parameters

ikitommi14:02:45

I think #malli solved this nicely: you can plug in a chain of transformers and they happen at right time.

ikitommi14:02:40

with malli, you can create a transformer chain like:

(mt/transformer
  ;; first run schema-based transformations named :before
  {:name :before}
  ;; then strip out extra keys based on schema
  (mt/strip-extra-keys-transformer)
  ;; run json-things
  (mt/json-transformer)
  ;; run custom things
  {:name :after}
  ;; add default values
  (mt/default-value-transformer))
… and one can plug in :before and :after transformations for both request and response:
[:and 
  {:decode/before str/lower
   :decode/after (partial str ".}
   :encode/after str/upper} string?]
ping @plins

ikitommi14:02:00

there is a option to print out diffs in the request/response chain by the mw, if that helps: https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj#L71

plins18:02:00

ok thanks very much for the input, I think Ill be able to come up with a working version soon so we can discuss whats the best way to do this? I came up with 3 middlewares (on the transform the swagger object, the other one to transform the request and the last to transform the response) should I put them in some module like middleware/swagger and create a PR ? or you prefer to dont incorporate that into reitit and a simple working example on the wiki is enough? or both?

plins20:02:16

https://gist.github.com/pablo-develop/76eb66f6ea9f4db19a86aadf59a4d188 here is an initial draft, any feedback would be awesome, not sure if it cover all cases but I created some complex endpoints and everything seems fine

ikitommi14:02:40

with malli, you can create a transformer chain like:

(mt/transformer
  ;; first run schema-based transformations named :before
  {:name :before}
  ;; then strip out extra keys based on schema
  (mt/strip-extra-keys-transformer)
  ;; run json-things
  (mt/json-transformer)
  ;; run custom things
  {:name :after}
  ;; add default values
  (mt/default-value-transformer))
… and one can plug in :before and :after transformations for both request and response:
[:and 
  {:decode/before str/lower
   :decode/after (partial str ".}
   :encode/after str/upper} string?]
ping @plins