metosin / compojure-api

Sweet web apis with Compojure & Swagger
http://metosin.github.io/compojure-api/doc/
Eclipse Public License 1.0
1.11k stars 149 forks source link

Can't get Swagger UI to provide interface for arbitrary query params #455

Open aneilbaboo opened 2 years ago

aneilbaboo commented 2 years ago

Library Version(s)

2.0.0-alpha31

Problem

Swagger can display a UI for setting dynamic query parameters, but I can't figure out how to do it with compojure-api.

I have a /find endpoint which needs to receive arbitrary query parameters.

E.g., Given /api/find?x=1&y=2&z=3 I want to receive a map {:x 1, :y 2, :3} somewhere in my handler. The keys are chosen by the requester.

(ns myns (:require [schema.core :as scm]))

(def myapp
   (api ...
       (GET "/find" [& fields]
                  :return scm/Any
           (ok fields))

This works:

 curl 'localhost:8080/api/runs/find?a=1&b=2'
{"a":"1","b":"2"}%     

But the Swagger UI does not provide a way to set the query parameters: image

I've tried variants of the route, providing

:query [fields scm/Any]

And many other things. I can't seem to find documentation for the arguments to :query. (Also, does anyone understand what :- is for?)

If I use a schema that starts with schema.core/maybe...

(scm/defschema QueryArgs (scm/maybe (scm/cond-pre scm/Num scm/Str scm/Bool scm/Keyword scm/Uuid)))

and provide this to :query...

  (GET "/find" [& fields]
         :return scm/Any
         :query [fields QueryArgs]
      (ok))

Swagger at least shows a box that allows me to assign several values inside another parameter, but it's not the one I want: image

It produces a URL like this:

curl -X GET --header 'Accept: application/json' 'http://localhost:8080/api/runs/find?schemas=a%3D1&schemas=b%3D2'

But I want

curl -X GET --header 'Accept: application/json' 'http://localhost:8080/api/runs/find?a=1&b=2'
aneilbaboo commented 1 year ago

@ikitommi - This issue was a bit long. Here's a TL;DR. Thanks in advance for any help!

I'm trying to write a search API where query parameters are dynamic.

GET /docs?foo=1&bar=2

Where foo and bar are runtime-generated keys.

Any thoughts on how to get a map of all query parametrs passed by the user, and present a UI in Swagger that allows setting them?

aneilbaboo commented 1 year ago

FWIW, for anyone else encountering this issue, I solved it by capturing query parameters two different ways, one capturing the raw URL query parameters, and another which enables inputing values through the swagger UI. In the box, the user will enter key-value pairs like:

foo=123
baz=abc

Which in the following behaves the same as a query using /my-route?foo=123&baz=abc

The handler looks like this.:

  (GET "/" [& params] ;; fields captures URL query params
    ;; query enables an input box in the Swagger UI
    :query-params [{query :- [scm/Str] []}]  
    (let [params (dissoc params query)
            processed-query  (into {} (map #(let [[lhs rhs] (str/split % #"=")] [(keyword lhs) rhs]) query))
            params (merge params query)]
       ;; here params is the same for normal query parameters or the Swagger UI

The var params will be a map containing the query variables:

(println params) ; => {:foo "123" :baz "abc"}

Coercion is left up to you. Hope that helps.