Fork me on GitHub
#ring-swagger
<
2017-06-22
>
eoliphant02:06:23

Hi I'm trying to implement a copy operation for an API. We've decided to implement it as something like POST /myresource?sourceresourceid=x with no body. I first tried creating a separate (POST) macro and that didn't work in terms of it showing up in the Swagger page as another entry. i've also tried just adding a :query-param to the existing (POST "/myresource"..) but the query param doesn't show up in the existing definition when I do that either. Is this supported?

ikitommi11:06:39

@eoliphant hi. not sure about the question, but this works:

(def app
  (api
    {:swagger
     {:ui "/"
      :spec "/swagger.json"}}

    (POST "/my-resource" []
      :query-params [sourceresourceid :- s/Str]
      (ok {:id sourceresourceid}))))

eoliphant13:06:06

Hi @ikitommi, indeed it does. I think I'd been working too long without a break and totally missed that it'd showed up lol. My original question was around doing something like the following

(POST "/my-resource" []
      :query-params [sourceresourceid :- s/Str]
      (ok {:id sourceresourceid}))

(POST "/my-resource" []
      :body [something Something]
      (ok ))

And getting both POSTs described in the swagger

eoliphant13:06:31

For example I have the following:

(POST "/roles" []
      :query-params [sourceroleid :- String]
      :summary "Copy a  role"
      (ok))
    (POST "/roles" []
      :body [role roleEntry]
      :summary "Add a new role"
      (let [retval (r/add-role role)]
        (if (f/failed? retval)
          (bad-request {:message (f/message retval)})
          (created "/role" role))))
When I do this only the first one wins in terms of what shows up in swagger. I'd hoped to be able to do it that way to simplyfy the API handling If I do this
(POST "/roles" []
      :body [role roleEntry]
      :query-params [sourceroleid :- String]
      :summary "Add a new role"
      (let [retval (r/add-role role)]
        (if (f/failed? retval)
          (bad-request {:message (f/message retval)})
          (created "/role" role))))
I guess I need to make both the body and the parameter optional, and handle the fact that you should have one or the other programmatically?

barry13:06:42

@eoliphant We've used an empty body for PUT before:

(PUT "/" request
                      :name :update-foo
                      :summary "Update a foo"
                      :return {:data {}}
                      (do ...))

barry13:06:55

When called, clients often pass "" (an empty string) as the HTTP body

barry13:06:03

you might try that with a POST as well

eoliphant13:06:30

Hmm, but is that going to work in my case. Since if it's truly a new one, then I want a valid JSON, if not then I want nothing (or an empty string, etc) and expect to see the sourceroleid param. Wouldn't the body validation fail in that case?

eoliphant13:06:30

also, SN, do you know how to specify an optional :query-param? like if I have :query-param [a :- String, b :- String] but b isn't required. It's pretty straightforward in the (defschemas) but i'm not seeing how to do in in this case

barry14:06:41

eoliphant: sorry, I missed the intervening discussion.

lambder14:06:50

hi all I’m trying to build rest api with compojure-api I have the following definition:

(context "/api" []
      :coercion :spec

      (POST "/decision" []
        :return string?
        :body [body string?]
        (ok
          {:foo body}))

...)

lambder14:06:55

when I do :

lambder14:06:34

curl -0 -v POST  \
-H 'Content-Type: text/json; charset=utf-8' \
-d @- << EOF
"foo"
EOF

lambder14:06:06

{
    "spec": "(spec-tools.core/spec {:spec clojure.core/string?, :type :string})",
    "problems": [
        {
            "path": [],
            "pred": "clojure.core/string?",
            "val": null,
            "via": [],
            "in": []
        }
    ],
    "type": "compojure.api.exception/request-validation",
    "coercion": "spec",
    "value": null,
    "in": [
        "request",
        "body-params"
    ]
}

lambder14:06:31

it looks like it expects body-params in request but there is no such key

lambder14:06:10

this is the function which throws the exception

lambder14:06:28

compojure.api.coercion/coerce-request!

lambder14:06:39

(defn coerce-request! [model in type keywordize? open? request]
  (let [transform (if keywordize? walk/keywordize-keys identity)
        value (transform (in request))]
    (if-let [coercion (-> request
                          (get-request-coercion)
                          (resolve-coercion))]
      (let [model (if open? (cc/make-open coercion model) model)
            format (some-> request :muuntaja/request :format)
            result (cc/coerce-request coercion model value type format request)]
        (if (instance? CoercionError result)
          (throw (ex-info
                   (str "Request validation failed: " (pr-str result))
                   (merge
                     (into {} result)
                     {:type ::ex/request-validation
                      :coercion coercion
                      :value value
                      :in [:request in]
                      :request request})))
          result))
      value)))

lambder14:06:01

where in is :body-params

lambder14:06:38

how can I make compojure-api to to bind the passed body to the declared variable in handler?

lambder15:06:53

even sticking to schema it seems my body is nil

(POST "/decision" []
        :return s/Str
        :body [body s/Str]
        (ok
          (foo body)))
results in:
{
    "schema": "java.lang.String",
    "errors": "(not (instance? java.lang.String nil))",
    "type": "compojure.api.exception/request-validation",
    "coercion": "schema",
    "value": null,
    "in": [
        "request",
        "body-params"
    ]
}

lambder15:06:33

what do I need to do to get POST data bind to body ?