Linter rulesOpenAPIschema-keyword-support

schema-keyword-support

Since 0.11.0

This rule reports schema keywords that the tools consuming the document are unlikely to support, and suggests the rewrite where one exists.

Rule Details

OpenAPI 3.1 schemas are JSON Schema 2020-12, but much of the tooling around them is not. Validators of older drafts, OpenAPI 3.0 tools and most code generators ignore the keywords they don’t know, so unevaluatedProperties: false stops rejecting anything, a prefixItems tuple turns into an untyped array and an if/then constraint silently disappears from the generated client. The rule checks each schema against one or more targets and reports, once per keyword, the first target that does not support it:

Target Who it stands for
draft4 Validators stuck at draft-04, e.g. NJsonSchema’s default dialect, older PHP and Python libraries
draft6 Draft-06 validators
draft7 Validators stopping at draft-07: gojsonschema, everit-org/json-schema, Ajv’s default export; also the default schema dialect of AsyncAPI 2.x and 3.0
draft2019-09 Validators without draft 2020-12
openapi30 The Schema Object of OpenAPI 3.0 and the tools built for it, e.g. NSwag
codegen The keywords most code generators ignore or can’t express in types (the default)

What each target lacks, and the suggested rewrite:

Keyword or form Not supported by Suggested rewrite
unevaluatedProperties drafts 4 to 7, openapi30, codegen merge the allOf members into one schema and use additionalProperties
unevaluatedItems drafts 4 to 7 additionalItems next to the array form of items
unevaluatedItems openapi30, codegen none
prefixItems drafts 4 to 2019-09 the array form of items, additionalItems for the rest
prefixItems openapi30, codegen an object with named properties
$dynamicRef, $recursiveRef drafts 4 to 7, openapi30, codegen $ref to the schema it resolves to
$dynamicRef draft2019-09 $recursiveRef
$dynamicAnchor, $recursiveAnchor drafts 4 to 7, openapi30, codegen drop it once the reference to it is a $ref
$anchor drafts 4 to 7, openapi30, codegen reference the schema by a JSON pointer
$vocabulary drafts 4 to 7, openapi30, codegen drop it
$defs drafts 4 to 7 definitions
$defs openapi30 move the schemas to components/schemas
dependentRequired drafts 4 to 7 dependencies with an array of property names
dependentSchemas drafts 4 to 7 dependencies with a schema
dependentRequired, dependentSchemas, dependencies openapi30, codegen none
dependencies draft2019-09 dependentRequired or dependentSchemas
if (with then/else) draft4, draft6, openapi30, codegen oneOf with one alternative per case
contentSchema, minContains, maxContains drafts 4 to 7, openapi30, codegen none
contains draft4, openapi30, codegen none
propertyNames draft4, openapi30 none
const draft4, openapi30 an enum with one value
patternProperties openapi30 properties for fixed names, additionalProperties for the rest
patternProperties codegen additionalProperties
not codegen none
additionalItems openapi30 none
$id, $schema, $comment, examples, contentEncoding, contentMediaType openapi30 drop $id and $schema; description for $comment; example; format: byte or format: binary
$id draft4 id
a type array openapi30 one type with nullable: true, or anyOf
a type array of several non-null types codegen oneOf with one schema per type
type: null openapi30 nullable: true
a numeric exclusiveMinimum/exclusiveMaximum draft4, openapi30 minimum/maximum with the boolean form
a boolean exclusiveMinimum/exclusiveMaximum drafts 6 to 2019-09 the bound as the value
nullable: true drafts 4 to 2019-09 add "null" to type
$ref with keywords that constrain the value drafts 4 to 7, openapi30, codegen wrap the reference in allOf; the siblings are ignored otherwise

Annotations a validator ignores without harm (description, examples, readOnly, deprecated, $comment, the content keywords) are reported only for openapi30, whose Schema Object allows no keywords besides its own and x- extensions. then and else are covered by the report on if. A $ref next to annotations only (description, summary, example, …) is not reported.

apigenie convert --from jsonschema --to draft7|draft4|openapi30 performs most of these rewrites on a standalone JSON Schema document, including the allOf flattening that replaces unevaluatedProperties.

Configuration

The options are a target name, an array of target names, or an object with:

  • targets: a target name or an array of target names; codegen when omitted.
  • allow: keywords never reported, for the ones your tools are known to support.

Configuration Example 1. Keep a 3.1 document usable by OpenAPI 3.0 tooling.

lint:
  rules:
    schema-keyword-support:
      - warning
      - openapi30

Configuration Example 2. Check against draft-07 validators and the code generators, accepting not.

lint:
  rules:
    schema-keyword-support:
      - warning
      - targets: [draft7, codegen]
        allow: [not]

Examples

Good. A closed object that every target understands.

components:
  schemas:
    Pet:
      type: object
      required: [name]
      properties:
        name:
          type: string
      additionalProperties: false

Bad. unevaluatedProperties is ignored by OpenAPI 3.0 tools, draft-07 validators and most code generators, so they accept any extra property.

components:
  schemas:
    Pet:
      allOf:
        - $ref: '#/components/schemas/Animal'
      properties:
        name:
          type: string
      unevaluatedProperties: false

When Not to Use It

When every consumer of the document is known to support JSON Schema 2020-12 in full, e.g. a validator such as Ajv 2020, networknt, santhosh-tekuri/jsonschema or Python’s jsonschema, and no code is generated from it.

Compatibility

This rule is compatible with all OpenAPI 3.x versions. In OpenAPI 3.0 documents it finds keywords the Schema Object does not have (with the openapi30 target) and the OpenAPI forms that JSON Schema validators don’t understand (with the draft targets).

Related rules

Further reading