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

muuntaja encoder modifying swagger.json output #449

Closed dgiagio closed 3 years ago

dgiagio commented 3 years ago

Library Version(s)

2.0.0-alpha31

Problem

api function with :formats set to a custom muuntaja instance changes swagger.json output making it invalid. E.g.:

(def ^:private mu
  (muuntaja/create
    (-> muuntaja/default-options
        (assoc-in
          [:formats "application/json" :encoder-opts]
          {:encode-key-fn ->snake_case_string})
        (assoc-in
          [:formats "application/json" :decoder-opts]
          {:decode-key-fn ->kebab-case-keyword}))))
(api
  :formats mu
  ...
)

The above will translate swagger's basePath to base-path, making the spec invalid.

How can one skip custom format encoding for specific paths, e.g. /swagger.json? I believe that could solve this issue while keeping the desired encoding for the remaining API.

Thank you.

dgiagio commented 3 years ago

I created a middleware that uses the default muuntaja instance to encode the JSON returned by Swagger only, leaving the custom encoder for all other responses. It's a hack, but seems to work well.

This is the middleware:

(defn wrap-disable-format-for-swagger [handler]
  "This middleware prevents the formatting of Swagger's json, which would render the spec invalid.
  This is a hack, but seems to work well."
  (fn [request]
    (let [swagger (get-in request [:compojure.api.request/lookup :compojure.api.swagger/swagger])]
      (if (contains? swagger (:uri request))
        (let [response (-> (handler request)
                           (assoc-in [:headers "Content-Type"] "application/json"))
              body (:body response)
              encoded-body (muuntaja/encode "application/json" body)]
          (assoc response :body encoded-body))
        (handler request)))))

And this is how to add it to your API:

(defn- make-app []
  (api
    {:swagger    {:spec "/swagger.json"
                  :data {:produces ["application/json"]
                         :consumes ["application/json"]
                         :basePath "/aaas"
                         :info     {:title   "My API"
                                    :version "1.0"}}}
     :middleware [wrap-disable-format-for-swagger]
...