metosin / metosin/compojure-api

Pure map schemas are broken

Aperta
#341 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Lingua principale
Clojure
Stelle
1.1k
Fork
146
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

Not sure if thats by design, since Swagger doesn't seem to represent well pure maps, i.e. {s/Str s/Str}, but handling of those is broken atm.

What happens is that the default implementation forces Cheshire to keywordize all incoming maps while deserializing, so instead of {s/Str s/Str}, the request winds up as {s/Keyword s/Str} :(

The good (for my use case) news is that since Compojure API is almost infinitely extensible ;) I was able to:

  1. Override the JSON deserialization
  2. Plug a custom coercer

Something like this (only the important bits):

(ns blah.core
  (:require
           ;; just to show what cs and cc below resolve to
            [compojure.api.coercion.core :as cc]
            [compojure.api.coercion.schema :as cs])

;; coercion wiring

(defn keywordize-some
  "Keywordizes only the top-level map keys that are in the ks collection"
  [ks m]
  (reduce (fn [nm k]
            (if (keyword? k)
              (if-let [existing (get nm k)]
                nm
                (let [k-str (name k)]
                  (if-let [ex2 (get nm k-str)]
                    (-> nm
                        (assoc k ex2)
                        (dissoc k-str))
                    nm)))
              nm))
          m
          ks))

(defn json-coercer [schema x]
  "Coerces a map value with a map schema - otherwise falls back to the original coercer"
  (if (map? x)
    (keywordize-some (keys schema) x)
    (if-let [fallback (cs/json-coercion-matcher schema)]
      (fallback x)
      x)))

(defn jcm [schema]
  (if (map? schema)
    (partial json-coercer schema)
    (cs/json-coercion-matcher schema)))

(defmethod cc/named-coercion :json [_] (cs/create-coercion {:body     {:default (constantly nil)
                                                                       :formats {"application/json"    jcm
                                                                                 "application/msgpack" jcm
                                                                                 "application/x-yaml"  jcm}}
                                                            :string   {:default cs/string-coercion-matcher}
                                                            :response {:default (constantly nil)}}))

(api
    ....
     ;; wire a non-keywordizing JSON deserializer
     ;; the actual keywordization will be done in a custom coercer
     :formats    (-> (:formats compojure.api.api/api-defaults)
                     (assoc-in [:formats muuntaja.format.json/json-type] {:decoder [muuntaja.format.json/make-json-decoder {}]
                                                                          :encoder [muuntaja.format.json/make-json-encoder]}))

     ;; our custom coercer registered via defmethod above
     :coercion   :json
    ...
     }

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con compojure.api.coercion.core/named-coercion e compojure.api.coercion.schema/json-coercion-matcher, quindi segui la configurazione del decoder JSON di Muuntaja mostrata nella issue. Riproduci una richiesta usando lo schema di map puro {s/Str s/Str}; il lavoro è completato quando le chiavi stringa rimangono stringhe durante la deserializzazione e la coercizione, senza compromettere le map con chiavi di schema.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
clojure, openapi
Ambito
api, backend
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
28/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.