metosin / compojure-api

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

sequential query params? #115

Closed jtmarmon closed 9 years ago

jtmarmon commented 9 years ago

Is there a way to accept sequential query params? If yes, can you please document it? If not, would you accept a PR?

possible (opinionated) syntax:

:query-params [ids :- [String]]

url: localhost:3000/api/route?ids[]=1&ids[]=22

ikitommi commented 9 years ago

lein new compojure-api kikka

change handler.clj to:

(ns kikka.handler
  (:require [compojure.api.sweet :refer :all]
            [ring.util.http-response :refer :all]))

(defapi app
  (swagger-ui)
  (swagger-docs)
  (GET* "/" []
    :query-params [names :- [String]]
    (ok {:names names)}))

works with http://localhost:3000/hello?name=abba&name=jabba&name=wasp

, but the swagger-ui doesn't understand the format. There are multiple way to present query string arrays, grep collectionFormat from https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md.

PR to make this really work would be welcome!

jtmarmon commented 9 years ago

weird. i'm getting (not (sequential? "param"))

clojure.lang.ExceptionInfo: throw+: #schema.utils.ErrorContainer{:error {:names (not (sequential? "hi"))}, :type :ring.swagger.schema/validation}

i'm on compojure-api v. 0.20.4

EDIT: ah never mind. I wasn't properly escaping my &'s in curl >.>

jtmarmon commented 9 years ago

@ikitommi - it is, however, throwing an error when there's only one value for "names". Shouldn't it just get coerced to a list based on the params spec even if there's only one value received ?

ikitommi commented 9 years ago

Should coerce to vectors & sets. And the swagger-ui should work too. PR(s) welcome.

jtmarmon commented 9 years ago
(GET* "/abc" []
    :query-params [names :- [String]]
    (ok {:names names}))

image image

vs

image

image

ikitommi commented 9 years ago

Ok. I should think before writing.. I was trying to say "yes, it doesn't work but it should". Do you want to try to fix this? Might need a change to ring-swagger too. On May 27, 2015 23:34, "Jason Marmon" notifications@github.com wrote:

(GET* "/abc" [] :query-params [names :- [String]](ok {:names names}))

[image: image] https://cloud.githubusercontent.com/assets/4528076/7845704/982baf52-0486-11e5-90a2-fced3af61abf.png [image: image] https://cloud.githubusercontent.com/assets/4528076/7845707/9bb92cee-0486-11e5-98c8-8fe8d9435624.png

vs

[image: image] https://cloud.githubusercontent.com/assets/4528076/7845714/a6a0d22e-0486-11e5-8ddc-5fd34b960690.png

[image: image] https://cloud.githubusercontent.com/assets/4528076/7845718/ad311d06-0486-11e5-8956-b0a87891f276.png

— Reply to this email directly or view it on GitHub https://github.com/metosin/compojure-api/issues/115#issuecomment-106049432 .

jtmarmon commented 9 years ago

so for fixing swagger ui, I've changed https://github.com/metosin/ring-swagger/blob/master/src/ring/swagger/json_schema.clj#L68-L74 these two methods to have :collectionFormat "multi"

I've also changed the tests to all expect :collectionFormat "multi" when there is an array

i'm stuck on this last error here:

FAIL "more complete spec" at (swagger2_test.clj:122)
    Expected: nil
      Actual: {:output-errors ({:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/f"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]} {:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/g"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]} {:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/h"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]} {:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/o"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]})}

FAIL "transforming subschemas" at (swagger2_test.clj:208)
    Expected: nil
      Actual: {:output-errors ({:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "oneOf", :level "error", :matched 0, :message "instance failed to match exactly one schema (matched 0 out of 2)", :nrSchemas 2, :reports {:/definitions/parametersList/items/oneOf/0 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "oneOf", :level "error", :matched 0, :message "instance failed to match exactly one schema (matched 0 out of 2)", :nrSchemas 2, :reports {:/definitions/parameter/oneOf/0 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0/schema"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]}], :/definitions/parameter/oneOf/1 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "oneOf", :level "error", :matched 0, :message "instance failed to match exactly one schema (matched 0 out of 4)", :nrSchemas 4, :reports {:/definitions/nonBodyParameter/oneOf/0 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/headerParameterSubSchema"}, :unwanted ["schema"]}], :/definitions/nonBodyParameter/oneOf/1 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/formDataParameterSubSchema"}, :unwanted ["schema"]}], :/definitions/nonBodyParameter/oneOf/2 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/queryParameterSubSchema"}, :unwanted ["schema"]}], :/definitions/nonBodyParameter/oneOf/3 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/pathParameterSubSchema"}, :unwanted ["schema"]}]}, :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/nonBodyParameter"}} {:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "required", :level "error", :message "object has missing required properties ([\"type\"])", :missing ["type"], :required ["in" "name" "type"], :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/nonBodyParameter"}}]}, :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/parameter"}}], :/definitions/parametersList/items/oneOf/1 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"description\",\"in\",\"name\",\"required\",\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/jsonReference"}, :unwanted ["description" "in" "name" "required" "schema"]}]}, :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/parametersList/items"}})}FAIL "more complete spec" at (swagger2_test.clj:122)
    Expected: nil
      Actual: {:output-errors ({:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/f"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]} {:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/g"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]} {:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/h"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]} {:domain "validation", :instance {:pointer "/definitions/Body12453E/properties/o"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]})}

FAIL "transforming subschemas" at (swagger2_test.clj:208)
    Expected: nil
      Actual: {:output-errors ({:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "oneOf", :level "error", :matched 0, :message "instance failed to match exactly one schema (matched 0 out of 2)", :nrSchemas 2, :reports {:/definitions/parametersList/items/oneOf/0 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "oneOf", :level "error", :matched 0, :message "instance failed to match exactly one schema (matched 0 out of 2)", :nrSchemas 2, :reports {:/definitions/parameter/oneOf/0 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0/schema"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"collectionFormat\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/schema"}, :unwanted ["collectionFormat"]}], :/definitions/parameter/oneOf/1 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "oneOf", :level "error", :matched 0, :message "instance failed to match exactly one schema (matched 0 out of 4)", :nrSchemas 4, :reports {:/definitions/nonBodyParameter/oneOf/0 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/headerParameterSubSchema"}, :unwanted ["schema"]}], :/definitions/nonBodyParameter/oneOf/1 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/formDataParameterSubSchema"}, :unwanted ["schema"]}], :/definitions/nonBodyParameter/oneOf/2 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/queryParameterSubSchema"}, :unwanted ["schema"]}], :/definitions/nonBodyParameter/oneOf/3 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/pathParameterSubSchema"}, :unwanted ["schema"]}]}, :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/nonBodyParameter"}} {:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "required", :level "error", :message "object has missing required properties ([\"type\"])", :missing ["type"], :required ["in" "name" "type"], :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/nonBodyParameter"}}]}, :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/parameter"}}], :/definitions/parametersList/items/oneOf/1 [{:domain "validation", :instance {:pointer "/paths/~1body2/post/parameters/0"}, :keyword "additionalProperties", :level "error", :message "object instance has properties which are not allowed by the schema: [\"description\",\"in\",\"name\",\"required\",\"schema\"]", :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/jsonReference"}, :unwanted ["description" "in" "name" "required" "schema"]}]}, :schema {:loadingURI "http://swagger.io/v2/schema.json#", :pointer "/definitions/parametersList/items"}})}

perhaps you have an idea of what these means? i'm not super acquainted with the code here.

working on a fix for the coercion now .

ikitommi commented 9 years ago

In Swagger, the properties and parameters have small differences. We hadn't done anything for this, as 95% they are the same. @Deraen is working on having separate multimethods &/ protocol for defining those separately. So, the root cause for errors is that in properties the :collectionFormat is illegal.

ikitommi commented 9 years ago

Latest ring-swagger fixes this. Defaults to Ring's multi-handling (https://github.com/mmcgrana/ring/wiki/Parameters), except that the new coercion matcher also handles the single-value case (wraps it into a vector).