oliyh / pedestal-api

Easily build APIs in Pedestal using Schema and Swagger
MIT License
110 stars 13 forks source link

clojure.lang.ExceptionInfo: Input to annotate does not match schema #24

Open mynomoto opened 5 years ago

mynomoto commented 5 years ago
clojure.lang.ExceptionInfo: Input to annotate does not match schema: 

                                   [(named {:parameters {:body-params disallowed-key}} doc) nil]  

     error: [(named {:parameters {:body-params disallowed-key}} doc) nil]
    schema: [{:schema
              {{:k :parameters}
               {{:k :body} Any,
                {:k :query} Any,
                {:k :path} Any,
                {:k :header} Any,
                {:k :formData} Any},
               {:k :responses} Any,
               Keyword Any},
              :optional? false,
              :name doc}
             {:schema Any, :optional? false, :name obj}]
      type: :schema.core/error

I'm trying to use schema.core/with-fn-validation on my tests but I'm getting the error above. I think this schema is too strict for the use on this library but changing it would need changes on https://github.com/metosin/ring-swagger Do you have a suggestion on how to fix this? Thanks!

oliyh commented 5 years ago

Hi,

Are you sure you want :body-params (which doesn't do anything in pedestal-api) and don't mean :body (the way to specify the schema for the body)?

mynomoto commented 5 years ago

I'm quite sure that the examples say to use :body-params for that after content negotiation. Trying to use body fails because it has an input stream, unless some redirection happens in another interceptor before getting to the api/coerce-request interceptor. One example at https://github.com/oliyh/pedestal-api/blob/master/example/src/pedestal_api_example/service.clj#L41 To be clear, this is for request validation/coercion, to validate responses :body should work.

oliyh commented 5 years ago

Ah yes, you are right - ring calls it :body-params and swagger calls it :body hence the mismatch. pedestal-api should take care of this for you though, when you call api/defroutes. Can you give a minimal failing example?

mynomoto commented 5 years ago

This is unrelated to using api/defroutes. Calls to api/annotate fail because of schema mismatch when running using s/with-fn-validation.

#!/bin/sh
#_(
   DEPS='
   {:deps {pedestal-api {:mvn/version "0.3.4"}
           io.pedestal/pedestal.jetty {:mvn/version "0.5.5"}
           io.pedestal/pedestal.service {:mvn/version "0.5.5"}}}

   '
   OPTS='
   -J-Xms256m -J-Xmx256m -J-client
   '

exec clojure $OPTS -Sdeps "$DEPS" "$0" "$@"
)

(require '[pedestal-api.core :as api])
(require '[schema.core :as s])
(require '[io.pedestal.interceptor :refer [interceptor]])
(import 'java.util.UUID)

(s/defschema Pet
  {:name s/Str
   :type s/Str
   :age s/Int})

(def create-pet
  (s/with-fn-validation
    (api/annotate
      {:summary     "Create a pet"
       :parameters  {:body-params Pet}
       :responses   {201 {:body {:id s/Uuid}}}
       :operationId :create-pet}
      (interceptor
        {:name ::create-pet
         :enter (fn [ctx]
                  (let [id (UUID/randomUUID)]
                    (assoc ctx :response
                           {:status 201
                            :body {:id id}})))}))))

Executing causes:

Exception in thread "main" Syntax error compiling at (pedestal-api-schema-problem.clj:29:3).
Input to annotate does not match schema: [(named {:parameters {:body-params disallowed-key}} doc) nil]
        at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3707)
        at clojure.lang.Compiler$DefExpr.eval(Compiler.java:457)
        at clojure.lang.Compiler.eval(Compiler.java:7181)
        at clojure.lang.Compiler.load(Compiler.java:7635)
        at clojure.lang.Compiler.loadFile(Compiler.java:7573)
        at clojure.main$load_script.invokeStatic(main.clj:452)
        at clojure.main$script_opt.invokeStatic(main.clj:512)
        at clojure.main$script_opt.invoke(main.clj:507)
        at clojure.main$main.invokeStatic(main.clj:598)
        at clojure.main$main.doInvoke(main.clj:561)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:705)
        at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: Input to annotate does not match schema: [(named {:parameters {:body-params disallowed-key}} doc) nil] {:type :schema.core/error, :schema [#schema.core.One{:schema {#schema.core.OptionalKey{:k :parameters} {#schema.core.OptionalKey{:k :body} Any, #schema.core.OptionalKey{:k :query} Any, #schema.core.OptionalKey{:k :path} Any, #schema.core.OptionalKey{:k :header} Any, #schema.core.OptionalKey{:k :formData} Any}, #schema.core.OptionalKey{:k :responses} Any, Keyword Any}, :optional? false, :name doc} #schema.core.One{:schema Any, :optional? false, :name obj}], :value [{:summary "Create a pet", :parameters {:body-params {:name java.lang.String, :type java.lang.String, :age Int}}, :responses {201 {:body {:id java.util.UUID}}}, :operationId :create-pet} #Interceptor{:name :user/create-pet}], :error [(named {:parameters {:body-params disallowed-key}} doc) nil]}
        at route_swagger.doc$eval3124$annotate__3129.invoke(doc.clj:9)
        at user$fn__23819$fn23820__23829$fn__23830.invoke(pedestal-api-schema-problem.clj:30)
        at user$fn__23819$fn23820__23829.invoke(pedestal-api-schema-problem.clj:29)
        at clojure.lang.AFn.applyToHelper(AFn.java:152)
        at clojure.lang.AFn.applyTo(AFn.java:144)
        at clojure.lang.AFunction$1.doInvoke(AFunction.java:31)
        at clojure.lang.RestFn.invoke(RestFn.java:397)
        at user$fn__23819.invokeStatic(pedestal-api-schema-problem.clj:29)
        at user$fn__23819.invoke(pedestal-api-schema-problem.clj:29)
        at clojure.lang.AFn.applyToHelper(AFn.java:152)
        at clojure.lang.AFn.applyTo(AFn.java:144)
        at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
        ... 12 more
john-shaffer commented 11 months ago

The issue is that the Operation schema in ring-swagger does not accept keys :body-params or :query-params.

I worked around this by simply using the annotate function with the schema removed:

(defn annotate
  "Attaches swagger documentation to an object."
  [doc obj]
  (vary-meta obj assoc ::doc doc))

The schema is not particularly useful here since it only checks for key existence. But you could certainly add in a correct schema if you choose.