Linter rulesAsyncAPIschema-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
The AsyncAPI Schema Object is a superset of JSON Schema draft-07, yet schemas copied from OpenAPI
3.1 or from JSON Schema 2020-12 documents bring keywords of the later drafts along. AsyncAPI
tools, draft-07 validators 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;codegenwhen omitted.allow: keywords never reported, for the ones your tools are known to support.
Configuration Example 1. Find the keywords AsyncAPI’s draft-07 dialect doesn’t have.
lint:
asyncapi:
rules:
schema-keyword-support:
- warning
- draft7
Configuration Example 2. Check against draft-07 validators and the code generators, accepting
not.
lint:
asyncapi:
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 not an AsyncAPI keyword, and draft-07 validators and most code
generators ignore it, 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 AsyncAPI 2.x and 3.x.