Skip to content

Instantly share code, notes, and snippets.

@pablo-develop
Created February 19, 2020 20:23
Show Gist options
  • Save pablo-develop/76eb66f6ea9f4db19a86aadf59a4d188 to your computer and use it in GitHub Desktop.
Save pablo-develop/76eb66f6ea9f4db19a86aadf59a4d188 to your computer and use it in GitHub Desktop.
auto-convert-casing.clj
(ns example.server
(:require [camel-snake-kebab.core :as csk]
[clojure.spec.alpha :as s]
[clojure.string :as str]
[clojure.walk :as walk]
[muuntaja.core :as m]
[reitit.coercion.spec :as spec]
[reitit.dev.pretty :as pretty]
[reitit.ring :as ring]
[reitit.ring.coercion :as coercion]
[reitit.ring.middleware.exception :as exception]
[reitit.ring.middleware.multipart :as multipart]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.parameters :as parameters]
[reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui]
[ring.adapter.jetty :as jetty]))
;;;;;;;;;;;;;;;;;;;;; swagger endpoint middleware
(defn transform-map [tk tv m]
(->> m
(map (fn [[k v]]
[(tk k); postwalk?
(tv v)]))
(into {})))
(defn transform-schema [tf {:keys [type] :as schema}]
(case type
"object"
(-> schema
(update :required #(map tf %))
(update :properties (partial transform-map tf (partial transform-schema tf))))
"array"
;(update schema :items transform-schema)
(update schema :items (partial transform-schema tf))
schema))
(defn transform-params [tf path-data]
(transform-map identity
(fn [method-data]
(-> method-data
(update :parameters
#(map (fn [{:keys [in] :as param}]
(cond
(.contains ["query" "path"] in)
(update param :name tf)
(= "body" in)
(update param :schema (partial transform-schema tf))))
%))
(update :responses (partial transform-map
identity
#(update % :schema (partial transform-schema tf))))))
path-data))
(defn transform-request-path [tf path]
(str/replace path #"(?<=\{).+?(?=\})" tf))
(defn wrap-transform-keys [tf]
(fn [handler]
(fn [request]
(let [response (handler request)]
(update-in response
[:body :paths]
(partial transform-map
(partial transform-request-path tf)
(partial transform-params tf)))))))
;;;;;;;;;;;;;;;;;;;;; transform request keys
(defn transform-body [tf coll]
(let [f (fn [[k v]] [(tf k) v])]
(walk/postwalk #(if (map? %)
(into {} (map f %))
%)
coll)))
(defn transform-request-case [tf]
(fn [handler]
(fn [request]
(handler
(-> request
(update :body-params (partial transform-body tf))
(update :query-params (partial transform-body tf)))))))
;;;;;;;;;;;;;;;;;;;;; transform response keys
(defn transform-response-case [tf]
(fn [handler]
(fn [request]
(let [response (handler request)]
(update response :body (partial transform-body tf))))))
(def app
(ring/ring-handler
(ring/router
[["/swagger.json"
{:get {:no-doc true
:swagger {:info {:title "my-api"}}
:middleware [(wrap-transform-keys csk/->camelCase)]
:handler (swagger/create-swagger-handler)}}]
["/endpoint/{p-param}"
{:post {:summary "minus with spec query parameters"
:parameters {:query {:q-param int?}
:path {:p-param int?}
:body {:foo-bar int?
:bar-foo int?
:object-one {:x-tudo int?
:parameter-y (s/coll-of int?)
:object-three {:uhul-meu-fii int? :j int?}
:object-two {:f-ahahah-boo int? :i int?}}}}
:responses {200 {:body {:list (s/coll-of int?)
:obj-n {:obj-name string?}
:foo int?}}}
:handler
(fn [{params :parameters}]
{:status 200
:body {:list [1 2 3 4]
:obj-n {:obj-name "nested-object"}
:foo 10}})}}]]
{;;:reitit.middleware/transform dev/print-request-diffs ;; pretty diffs
;;:validate spec/validate ;; enable spec validation for route data
;;:reitit.spec/wrap spell/closed ;; strict top-level validation
:exception pretty/exception
:data {:coercion reitit.coercion.spec/coercion
:muuntaja m/instance
:middleware [;; swagger feature
swagger/swagger-feature
;; query-params & form-params
parameters/parameters-middleware
;; content-negotiation
muuntaja/format-negotiate-middleware
;; encoding response body
muuntaja/format-response-middleware
;; converts all incoming requests to camelCase
(transform-response-case csk/->camelCase)
;; exception handling
exception/exception-middleware
;; decoding request body
muuntaja/format-request-middleware
;; coercing response bodys
coercion/coerce-response-middleware
;; converts all incoming requests to kebab-case
(transform-request-case csk/->kebab-case-keyword)
;; coercing request parameters
coercion/coerce-request-middleware
;; multipart
multipart/multipart-middleware]}})
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-default-handler))))
(defn start []
(jetty/run-jetty #'app {:port 3000, :join? false})
(println "server running in port 3000"))
(comment
(start))
@raymcdermott
Copy link

hits this issue with csk

@raymcdermott
Copy link

fix is this

(defn transform-body [tf coll]
  (let [f (fn [[k v]]
            (if (number? k)
              [k v]
              [(tf k) v]))]
    (walk/postwalk #(if (map? %)
                      (into {} (map f %))
                      %)
                   coll)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment