Liking cljdoc? Tell your friends :D

OpenAPI V3 Validator

A pure-clojure library for validating ring requests & responses against OpenAPI v3 specifications.

Build status

builds.sr.ht status

Deps

Clojars Project

Goals

Portable clojure

Long term goal: Should work on any Clojure implementation.

Status: the core validator code has no dependencies and should work with minimal changes on any Clojure implementation. Clojure JVM and Babashka are currently tested as part of development.

TargetStatusRemarks
JVM ClojureDONE
BabashkaDONE
ClojureScriptTODOPatches welcome
.NET ClojureN/ANot planned, patches welcome

Schema validation

Target: Complete implementation of Ring request/response maps against OpenAPI v3.1.0 (the current version of the OpenAPI specification). We do not intent to implement v2 or earlier OpenAPI specifications.

Status: mostly complete and usable for real world scenarios. See below table.

ValidationStatusRemarks
JSON $refPARTIALSimple fragment identifiers only
JSON allOfDONEIssues of sub-schemas are concatenated
JSON anyOfDONEReports :sub-issues
JSON constDONE
JSON containsDONE
JSON containsDONEBut see unevaluatedItems
JSON dependentRequiredDONE
JSON discriminatorTODOOpenAPI Extension; never required, but should improve validation performance
JSON enumDONE
JSON formatPARTIALOnly validates "uuid"
JSON itemsDONE
JSON maxItemsDONE
JSON maxLengthDONE
JSON maxPropertiesDONE
JSON maximumDONE
JSON minItemsDONE
JSON minLengthDONE
JSON minPropertiesDONE
JSON minimumDONE
JSON multipleOfDONE
JSON notDONE
JSON oneOfDONEReports :sub-issues
JSON patternDONEUsing java.util.regex.Pattern
JSON propertiesDONE
JSON requiredDONE
JSON typeDONE
JSON unevaluatedItemsTODO
JSON uniqueItemsDONEUsing clojure uniqueness semantics
JSON xmlTODOOpenAPI Extension
OpenAPIPARTIALv3.1.0 mostly implemented
OpenAPI $refPARTIALSimple fragment identifiers only
OpenAPI CallbackN/ANot relevant
OpenAPI ComponentsDONECan be referred to using $ref
OpenAPI ContactN/ANot relevant
OpenAPI EncodingTODONot implemented
OpenAPI HeaderDONE
OpenAPI InfoN/ANot relevant
OpenAPI LicenceN/ANot relevant
OpenAPI LinkN/ANot relevant
OpenAPI Media TypeDONEAny content-type, body must be parsed before validation
OpenAPI OAuth FlowTODO
OpenAPI OAuth FlowsTODO
OpenAPI OperationDONEIncluding headers and parameters
OpenAPI ParameterPARTIALExcept allowEmptyValue, style=form + explode=true, deepObject=true
OpenAPI Path ItemPARTIALParameters in paths item are not validated
OpenAPI PathsDONE
OpenAPI Request BodyDONE
OpenAPI ResponseDONE
OpenAPI ResponsesDONE
OpenAPI SchemaPARTIALSee JSON entries in this table for status per JSON Schema keyword
OpenAPI Security RequirementTODO
OpenAPI Security SchemeTODO
OpenAPI ServerTODONot sure if relevant to validation
OpenAPI Server VariableN/ANot relevant
OpenAPI TagN/ANot relevant

Usage

Validating OpenAPI requests/responses:

(require '[nl.jomco.openapi.v3.validator :as validator])
(require '[clojure.data.json :as json])
(require '[clojure.java.io :as io])

(def ooapi-spec
  (json/read (io/reader (io/file "ooapiv5.json"))))

(def validate
  (-> ooapi-spec
      (validator/validator-context nil)
      validator/interaction-validator))


(validate {:request  {:method       :get
                      :uri          "/courses"
                      :query-params {"pageNumber" "foo"
                                     "sort"       "something,-name"}}
           :response {:status  200
                      :headers {"content-type" "application/json; charset=utf8"}
                      :body    {"pagesize"        0
                                "pageNumber"      1
                                "hasPreviousPage" false
                                "hasNextPage"     "true"
                                "items"           []}}}
          [])

;; =>

[{:canonical-schema-path ["components" "parameters" "pageNumber"],
  :instance              "foo",
  :issue                 "coercion-error",
  :path                  [:request :query-params "pageNumber"],
  :schema                {"type" "integer"},
  :schema-path           ["paths" "/courses" "get" "parameters" 2 "schema"]}
 {:instance              "something",
  :path                  [:request :query-params "sort" 0],
  :schema-path           ["paths" "/courses" "get" "parameters" 8 "schema" "items" "enum"],
  :canonical-schema-path ["paths" "/courses" "get" "parameters" 8 "schema" "items" "enum"],
  :schema-keyword        "enum",
  :schema                {"enum" ["courseId" "name" "-courseId" "-name"]},
  :issue                 "schema-validation-error"}
 {:instance              {"pagesize"        0,
                          "pageNumber"      1,
                          "hasPreviousPage" false,
                          "hasNextPage"     "true",
                          "items"           []},
  :path                  [:response :body],
  :schema-path           ["paths" "/courses" "get" "responses" "200" "content"
                          "application/json" "schema" "allOf" 0 "required"],
  :canonical-schema-path ["components" "schemas" "Pagination" "required"],
  :hints                 {:missing ["pageSize"]},
  :schema-keyword        "required",
  :schema                {"required" ["pageSize" "pageNumber" "hasPreviousPage"
                                      "hasNextPage" "items"]},
  :issue                 "schema-validation-error"}
 {:instance              "true",
  :path                  [:response :body "hasNextPage"],
  :schema-path           ["paths" "/courses" "get" "responses" "200" "content"
                          "application/json" "schema" "allOf" 0 "properties" "hasNextPage" "type"],
  :canonical-schema-path ["components" "schemas" "Pagination" "properties" "hasNextPage" "type"],
  :schema-keyword        "type",
  :schema                {"type" "boolean"},
  :issue                 "schema-validation-error"}]

Validating JSON Schemas:

(require '[nl.jomco.openapi.v3.schema-validator :as validator])

(def validate
   (validator/schema-validator
      (validator/validator-context specification)
      ["path" "to" "schema" "in" "spec"]))

(def issues
  (validate instance ["path" "of" "instance"]))

Validation issue format

Validations result in nil when no issues are present, or a collection of issues.

Issues are maps with a key :issue with one of the following values:

  • "schema-validation-error" - instance (body or parameter part) did not validate according to the JSON Schema specification.
  • "coercion-error" - can't coerce parameter to the correct type
  • "method-error" - the request method did not match the specification
  • "uri-error" - the uri path did not match the specification
  • "content-type-error" - the request/response content type did not match the specification.
  • "status-error" - the response status code did not match the specification.

An issue can optionally have one or more of the following keys:

  • :instance - the part of the document that failed to validate.
  • :path - the absolute path to instance
  • :schema-path the path in the specification that resolved to the failing validation.
  • :canonical-schema-path the absolute path to the schema of the failing validation.
  • :schema the relevant parts of the schema that did not validate. Usually a map.
  • :hints a map with additional information depending on the error.
  • :sub-issues - for combining schemas (oneOf and anyOf), the collections of results of the sub schema validations.
  • :schema-keyword - for JSON Schema keyword validations, the "main" schema keyword of the failing validation.

Paths are vectors of keys (strings, keywords and integers).

Interaction / request / response format

Requests and resposes follow ring format:

  • :uri is the path of the request
  • :request-params is a map with string keys
  • :headers is a map of with string keys
  • :method is a keyword :get, :put, :post etc
  • :body if present should be a parsed document (probably JSON) and have string keys.

Interactions are maps with :request and :response keys

TODO

Test Schema validator against JSON Schema Test-Suite

License

Copyright © 2022-2023 Joost Diepenmaat, Jomco BV

This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.

This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.

Can you improve this documentation?Edit on sourcehut

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close