Last active
May 20, 2023 19:21
-
-
Save skinkade/fb89aa3772047f5dea660d6c74c7b418 to your computer and use it in GitHub Desktop.
Clojure reitit REST API - Automatic camelCase conversion, including swagger support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Copyright 2023 Shawn Kinkade | |
;; | |
;; Licensed under the Apache License, Version 2.0 (the "License"); | |
;; you may not use this file except in compliance with the License. | |
;; You may obtain a copy of the License at | |
;; | |
;; http://www.apache.org/licenses/LICENSE-2.0 | |
;; | |
;; Unless required by applicable law or agreed to in writing, software | |
;; distributed under the License is distributed on an "AS IS" BASIS, | |
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
;; See the License for the specific language governing permissions and | |
;; limitations under the License. | |
(ns yourapp.middleware.field-names | |
(:require | |
[clojure.walk :refer [postwalk]] | |
[camel-snake-kebab.core :refer [->kebab-case-keyword ->camelCase]] | |
[camel-snake-kebab.extras :refer [transform-keys]] | |
[camel-snake-kebab.internals.alter-name :refer [AlterName]])) | |
;; avoids some conversion errors | |
(extend-protocol AlterName | |
Long | |
(alter-name [this _f] | |
this)) | |
;; This is a bit hacky, but from some general testing seems to work alright | |
;; order middleware like so: | |
;; muuntaja/format-response-middleware | |
;; resp->camel | |
;; (exception/create-exception-middleware ...) | |
;; muuntaja/format-request-middleware | |
;; req->kebab | |
;; wrap swagger json handler like so: | |
;; ["/api/swagger.json" {:no-doc true | |
;; :get (swagger->camel (swagger/create-swagger-handler))}] | |
(defn req->kebab | |
[handler] | |
(fn [req] | |
(let [xf (partial transform-keys ->kebab-case-keyword) | |
updated (-> req | |
(update :body-params xf) | |
(update :query-params xf) | |
(update :body xf) | |
(update :params xf))] | |
(handler updated)))) | |
(defn resp->camel | |
[handler] | |
(fn [req] | |
;; your swagger endpoint here | |
(if (= "/api/swagger.json" (:uri req)) | |
(handler req) | |
(let [resp (handler req)] | |
(update resp | |
:body | |
(partial transform-keys ->camelCase)))))) | |
;; swagger uses x-[value] for specific keys like x-nullable, | |
;; so we can't use use a general transformation without some breakage. | |
;; Additionally, we need to transform name and required property values. | |
(defn- swagger-walker | |
[[k v]] | |
(cond | |
(and (= :name k) | |
(keyword? v)) | |
[k (->camelCase v)] | |
(and (= :required k) | |
(vector? v)) | |
[k (mapv ->camelCase v)] | |
;; don't transform special keys | |
(and (keyword? k) | |
(= "x-" (-> k name (subs 0 2)))) | |
[k v] | |
;; avoids some conversion errors | |
(not (or (string? k) | |
(keyword? k))) | |
[k v] | |
:else | |
[(->camelCase k) v])) | |
(defn swagger->camel | |
[handler] | |
(fn [req] | |
(let [resp (handler req) | |
updated (postwalk | |
(fn [x] | |
(if (map? x) | |
(into {} (map swagger-walker x)) | |
x)) | |
(:body resp))] | |
(assoc resp :body updated)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment