~jomco/ring-openapi-validator

44f151a55210cb06c057a48815f1135d102c62ca — Joost Diepenmaat 1 year, 5 months ago 85b8301
Report on location of errors, cleanup some lint.
M project.clj => project.clj +23 -4
@@ 3,11 3,30 @@
  :url "https://git.sr.ht/~jomco/ring-openapi-validator"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url  "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.0" :scope "provided"]
                 [cheshire "5.10.0" :exclusions [com.fasterxml.jackson.core/jackson-core]]
                 [com.atlassian.oai/swagger-request-validator-core "2.28.1"]
  :dependencies [[com.atlassian.oai/swagger-request-validator-core "2.28.1"]
                 [io.swagger.parser.v3/swagger-parser "2.1.1"]]
  :profiles {:dev {:resource-paths ["dev-resources"]}}
  :profiles {:dev {:resource-paths ["dev-resources"]
                   :dependencies [[clj-kondo "2022.06.22"]]
                   :plugins      [[lein-kibit "0.1.8"]
                                  [lein-ancient "1.0.0-RC3"]]
                   :aliases      {"lint" ["do"
                                          ["run" "-m" "clj-kondo.main" "--lint" "src"]
                                          "kibit"]}}
             :provided {:dependencies [[org.clojure/clojure "1.11.1"]]}}

  ;; setup `vX.Y.Z` tags, deploy to clojars
  :release-tasks [["vcs" "assert-committed"]
                  ["test"]
                  ["lint"]
                  ["ancient"]
                  ["change" "version" "leiningen.release/bump-version" "release"]
                  ["vcs" "commit"]
                  ["vcs" "tag" "v"]
                  ["deploy" "clojars"]
                  ["change" "version" "leiningen.release/bump-version"]
                  ["vcs" "commit"]
                  ["vcs" "push"]]

  ;; link to git repo, also ensures that cljdoc.org finds additional markdown files
  :scm {:name "git"
        :url  "https://git.sr.ht/~jomco/ring-openapi-validator"})

M src/nl/jomco/ring_openapi_validator.clj => src/nl/jomco/ring_openapi_validator.clj +37 -28
@@ 1,17 1,12 @@
(ns nl.jomco.ring-openapi-validator
  (:require [clojure.string :as string]
            [cheshire.core :as json])
  (:require [clojure.string :as string])
  (:import com.atlassian.oai.validator.OpenApiInteractionValidator
           com.atlassian.oai.validator.model.Request
           com.atlassian.oai.validator.model.Request$Method
           com.atlassian.oai.validator.model.Response
           com.atlassian.oai.validator.model.StringBody
           com.atlassian.oai.validator.report.ValidationReport
           com.atlassian.oai.validator.report.ValidationReport$Level
           com.atlassian.oai.validator.report.ValidationReport$MessageContext$Location
           java.util.Optional
           java.nio.charset.Charset
           java.nio.charset.StandardCharsets))
           java.util.Optional))

(def ^:private ring->Method
  {:get     Request$Method/GET


@@ 44,32 39,32 @@
  [{:keys [uri request-method body query-params headers]}]
  (let [headers (normalize-headers headers)]
    (reify Request
      (getPath [this]
      (getPath [_]
        uri)
      (getMethod [this]
      (getMethod [_]
        (ring->Method request-method))
      (getBody [this]
      (getBody [_]
        (Optional/ofNullable body))
      (getQueryParameters [this]
      (getQueryParameters [_]
        (->> query-params
             keys
             (map name)))
      (getQueryParameterValues [this n]
      (getQueryParameterValues [_ n]
        (->coll (get query-params n)))
      (getHeaders [this]
      (getHeaders [_]
        headers)
      (getHeaderValues [this n]
      (getHeaderValues [_ n]
        (->coll (get headers (string/lower-case n)))))))

(defn- ring->Response
  [{:keys [status body headers] :as response}]
  [{:keys [status body headers]}]
  (let [headers (normalize-headers headers)]
    (reify Response
      (getStatus [this]
      (getStatus [_]
        status)
      (getBody [this]
      (getBody [_]
        (Optional/ofNullable body))
      (getHeaderValues [this n]
      (getHeaderValues [_ n]
        (->coll (get headers (string/lower-case n)))))))

(def ^:private Level->key


@@ 78,18 73,32 @@
   ValidationReport$Level/INFO   :info
   ValidationReport$Level/WARN   :warn})

(defn- Message->map
  [msg]
  {:key             (.getKey msg)
   :message         (.getMessage msg)
   :level           (Level->key (.getLevel msg))
   :additional-info (.getAdditionalInfo msg)
   :nested-messages (map Message->map (.getNestedMessages msg))})

(def ^:private Location->key
  {ValidationReport$MessageContext$Location/REQUEST  :request
   ValidationReport$MessageContext$Location/RESPONSE :response})

(defn- opt->val
  "Convert java.util.Optional to a nillable value."
  [x]
  (.orElse x nil))

(defn- Message->map
  [msg]
  (let [location (some-> msg
                         .getContext
                         opt->val
                         .getLocation
                         opt->val
                         Location->key)]
    (cond->
        {:key             (.getKey msg)
         :message         (.getMessage msg)
         :level           (Level->key (.getLevel msg))
         :additional-info (.getAdditionalInfo msg)
         :nested-messages (map Message->map (.getNestedMessages msg))}
      location
      (assoc :location location))))

(defn- report->coll
  [report]
  (let [coll (mapv Message->map (.getMessages report))]


@@ 102,14 111,14 @@
  `spec` is a url or path to resource describing a Swagger or OpenApi
  specification.

  `opts` is an optional map of options:
  Second argument is an optional map of options:
   - `:base-path` overrides the base path in the spec.
   - `:inline? true` indicate that `spec` is the specification body
      as a string, instead of a url or path

  If you need to customize the validator you can create a builder using
  `com.atlassian.oai.validator.OpenApiInteractionValidator/createFor`"
  ([spec {:keys [base-path inline?] :as opts}]
  ([spec {:keys [base-path inline?]}]
   (cond-> (if inline?
             (OpenApiInteractionValidator/createForInlineApiSpecification spec)
             (OpenApiInteractionValidator/createFor spec))

M test/nl/jomco/ring_openapi_validator_test.clj => test/nl/jomco/ring_openapi_validator_test.clj +5 -1
@@ 49,7 49,11 @@
                                                   ooapi-invalid-response)]
        (is (= "validation.response.body.schema.required"
               (get-in report [0 :key]))
            "Report on missing property")))
            "Report on missing property")

        (is (= :response
               (get-in report [0 :location]))
            "Report on location of error")))
    (testing "request"
      (is (nil? (validator/validate-request validator ooapi-request)))
      (is (= "validation.request.operation.notAllowed"