This repository contains the OpenAPI Compliance Project, the first piece of which is the OpenAPI Specification Compliance Parser.
The oascomply
command-line tool takes an API description, as a single document or as several documents linked by JSON References ($ref
) and indicates whether or not it is syntactically and semantically valid. It can optionally serialize an RDF graph of the parsed API description to stdout
in any format supported by rdflib
, as well as a custom format intended to be more human-readable.
The oas30-schema
command-line tool is an OAS 3.0-specific JSON Schema implementation, based on the jschon
implementation.
The OAS Compliance Parser is expected to reach 1.0 status by late 2023. The current status of the project is pre-Milestone 1.
- Milestone 1: (June 1, 2023) Basic parsing and syntax validation; RDF graph construction
- Milestone 2: (mid-July, 2023) User-friendly error reporting, reasonably complete and extensible syntax validation
- Milestone 3: (late August, 2023) Basic semantic validation using the RDF graph
- Milestone 4: (October 2023) Reasonably complete and extensible semantic validation, fully documented 1.0 release publishable on PyPI
Currently, only OAS 3.0 is supported, although OAS 3.1 support is planned.
oascomply
is a Python package with several command-line interfaces:
oascomply
parses and validates OAS 3.x API descriptionsoas30-schema
validates instances against JSON Schemas that use the OpenAPI 3.0 schema dialect, including `"format" validationyaml-to-json
does what it says, as converting a YAML API description to JSON will result in substantial performance improvements if runningoascomply
on the same document repeatedlypatch-oas-schemas
is used by package maintainers to update the modified OAS 3.x JSON Schemas used byoascomply
oascomply
requires Python 3.8 or later, and must be checked out from GitHub
and installed using poetry
. See the
Installation Guide for more detailed instructions.
API descriptions consist of one or more documents.
A single, self-contained document can be validated like this:
~/src/oascomply % oascomply -f tutorial/minimal.json
Your API description is valid!
For a multi-document API description where the files have file extensions
(schemas/foo.json
) but the references do not ("$ref": "schemas/foo"
),
we need to make a distinction between two concepts for each document:
- its URL (for local files, the
file:
URL from which it was read) - its URI (the identifier used to resolve references and in the parsed graph discussed further down)
By default, oascomply
assumes that the URI is the same as the URL.
If we try that default behavior with our referencing example, it won't work:
~/src/oascomply % oascomply -f tutorial/references/openapi.json -f tutorial/references/pathitems/foo.json -f tutorial/references/schemas/bar.json
ERROR:oascomply.oas30dialect:FOUND: file:///Users/handrews/src/oascomply/tutorial/references/schemas/bar
ERROR:oascomply.apidescription:Reference without suffix attempted despite target resource being registered under a URI with suffix
The above error can be fixed either by using -x:
-x -f tutorial/references/schemas/bar.json
... or by using the two-argument form of -f:
-f tutorial/references/schemas/bar.json file:///Users/handrews/src/oascomply/tutorial/references/schemas/bar
Let's try the -x
option, which creates URIs by just stripping the suffix off, since that's a common pattern:
~/src/oascomply % oascomply -x -f tutorial/references/openapi.json -f tutorial/references/pathitems/foo.json -f tutorial/references/schemas/bar.json
ERROR:oascomply.apidescription:JSON Schema documents must pass "Schema" (without quotes) as an additional -f argument:
-x -f tutorial/references/schemas/bar.json Schema
OK, we're closer. This example has a standalone schema file, and for Reasons™
(meaning that the author ran out of time to fix it before this milestone),
oascomply
gets a bit confused. But at least it tells you how to fix it!
So let's try that:
~/src/oascomply % oascomply -x -f tutorial/references/openapi.json -f tutorial/references/pathitems/foo.json -f tutorial/references/schemas/bar.json Schema
Your API description is valid!
Now it works! But as you can see in the errors from earlier, the URI is just
a file:
URL with the .json
chopped off, which is... kinda misleading.
The reason API description authors usually leave off the .json
or .yaml
is because the documents will be made available over HTTPS, and the JSON
vs YAML format will be handled in content negotiation. We can assign
https:
URIs instead of file:
ones (but the URLs will always be file:
URLs, at least until support for validating remote descriptions is added).
The simplest way to do that is to put the URI after the file as part
of the -f
option. Let's also increase the verbosity by passing -v
,
which will show us the URIs without needing to provoke an error message:
~/src/oascomply % oascomply -f tutorial/minimal.json https://example.com/minimal -v
INFO:oascomply.apidescription:Adding document "tutorial/minimal.json" ...
INFO:oascomply.apidescription:...URL <file:///Users/handrews/src/oascomply/tutorial/minimal.json>
INFO:oascomply.apidescription:...URI <https://example.com/minimal>
INFO:oascomply.apidescription:...instantiating OAS Document <https://example.com/minimal>
INFO:oascomply.apidescription:Checking JSON Schema references in <https://example.com/minimal#>...
INFO:oascomply.apidescription:...resolving with OasJson.resolve_references()
Your API description is valid!
Here we get some information, including how URLs and URIs are associated.
Setting the URI for each file like that is fine when you only have one file,
but it can get annoying with more. The -d
option lets you associate
a directory with a URI prefix. oascomply
will replace the directory with
the prefix to create the URI, and strip off any file extension automatically.
There is no need to pass -x
with -d
, and the format is similar to -f
—
first the filesystem path, then the URI (or URI prefix in this case):
~/src/oascomply % oascomply -d tutorial/references https://example.com -f tutorial/references/openapi.json -f tutorial/references/pathitems/foo.json -f tutorial/references/schemas/bar.json Schema -v
ERROR:oascomply.apidescription:URI prefixes must include a path that ends with '/': <tutorial/references>
Oops! URI prefixes need to end with a /
to match the directory, otherwise
the behavior gets a little confusing. But that's easily fixed:
~/src/oascomply % oascomply -d tutorial/references https://example.com/ -f tutorial/references/openapi.json -f tutorial/references/pathitems/foo.json -f tutorial/references/schemas/bar.json Schema -v
INFO:oascomply.apidescription:Adding document "tutorial/references/openapi.json" ...
INFO:oascomply.apidescription:...URL <file:///Users/handrews/src/oascomply/tutorial/references/openapi.json>
INFO:oascomply.apidescription:...URI <https://example.com/openapi>
INFO:oascomply.apidescription:...instantiating OAS Document <https://example.com/openapi>
INFO:oascomply.apidescription:Adding document "tutorial/references/pathitems/foo.json" ...
INFO:oascomply.apidescription:...URL <file:///Users/handrews/src/oascomply/tutorial/references/pathitems/foo.json>
INFO:oascomply.apidescription:...URI <https://example.com/pathitems/foo>
INFO:oascomply.apidescription:...instantiating OAS Document <https://example.com/pathitems/foo>
INFO:oascomply.apidescription:Adding document "tutorial/references/schemas/bar.json" ...
INFO:oascomply.apidescription:...URL <file:///Users/handrews/src/oascomply/tutorial/references/schemas/bar.json>
INFO:oascomply.apidescription:...URI <https://example.com/schemas/bar>
INFO:oascomply.apidescription:...instantiating JSON Schema <https://example.com/schemas/bar>
INFO:oascomply.apidescription:Checking JSON Schema references in <https://example.com/openapi#>...
INFO:oascomply.apidescription:...resolving with OasJson.resolve_references()
INFO:oascomply.apidescription:Checking JSON Schema references in <https://example.com/pathitems/foo#>...
INFO:oascomply.apidescription:...resolving with OasJson.resolve_references()
INFO:oascomply.apidescription:Checking JSON Schema references in <https://example.com/schemas/bar>...
INFO:oascomply.apidescription:...already resolved by jschon.JSONSchema()
INFO:oascomply.oasgraph:Validating "example" https://example.com/openapi#/example against schema https://example.com/openapi#/components/responses/foo/content/application~1json/schema, metaschema https://spec.openapis.org/oas/v3.0/dialect/base
INFO:oascomply.oasgraph:Validating "example" https://example.com/schemas/bar#/example against schema https://example.com/schemas/bar, metaschema https://spec.openapis.org/oas/v3.0/dialect/base
INFO:oascomply.oasgraph:Validating "default" https://example.com/schemas/bar#/default against schema https://example.com/schemas/bar, metaschema https://spec.openapis.org/oas/v3.0/dialect/base
INFO:oascomply.oasgraph:Validating "example" https://example.com/openapi#/example against schema https://example.com/openapi#/components/responses/foo/content/application~1json/schema, metaschema https://spec.openapis.org/oas/v3.0/dialect/base
INFO:oascomply.oasgraph:Validating reference <https://example.com/openapi#/paths/~1foo/$ref> to <https://example.com/pathitems/foo>
INFO:oascomply.oasgraph:Validating reference <https://example.com/openapi#/components/responses/foo/content/application~1json/schema/$ref> to <https://example.com/openapi#/components/schemas/bar>
INFO:oascomply.oasgraph:Validating reference <https://example.com/openapi#/components/responses/foo/links/self/operationRef> to <https://example.com/pathitems/foo#/get>
INFO:oascomply.oasgraph:Validating reference <https://example.com/openapi#/components/schemas/bar/$ref> to <https://example.com/schemas/bar#>
INFO:oascomply.oasgraph:Validating reference <https://example.com/pathitems/foo#/get/responses/200/$ref> to <https://example.com/openapi#/components/responses/foo>
Your API description is valid!
This multi-document example also uses the example
and default
keywords, so we get informed that the values of those keywords are being validated against their schemas. This validation supports OAS 3.0-specific keywords like nullable
, and also validates some format
s (currently only certain string formats, but more will be added). Validation of examples and defaults can be disabled by passing -e false
.
We also see at the end that there is some additional validation of references, which checks that the type of the reference target matches what the reference source expects.
Getting a yes or no for validation is nice, and we'll look at the error output for the "no" case further down. But what else does this tool do?
It constructs an RDF (Resource Description Framework 1.1) graph out of the API description. This is somewhat analogous to how compilers parse programming languages into an abstract syntax tree. Don't worry, you don't need to go read the RDF spec (or the endless number of related semantic web specs) to benefit from this graph!
We need to build a graph rather than a tree in order to show how references connect different pieces of the description documents. In later milestones we will also use this graph to do much more sophisticated validation.
There are many ways to write an RDF graph to a file, and oascomply
can
produce any of the ones supported by the Python rdflib
package. But the
default, which you get by passing -o
without an argument, is a simple
line-oriented format known as
N-Triples 1.1. It is supported by
most RDF-based tools, but is also simple enough to be parsed directly
by a regular expression, which can be found in the oascomply.reparse
module.
To print the graph to stdout, use the -o
option without an argument.
Here, we redirect it to a .nt
file, which is the standard N-Triples
file extension (diagnostic messages such as
"Your API description is valid!" are printed to stderr). We'll set a short
HTTPS URI as it's less noisy than full filesystem paths:
~/src/oascomply % oascomply -f tutorial/minimal.json https://example.com/minimal -o > minimal.nt
Your API description is valid!
Let's take a look at that output file:
<https://example.com/minimal#> <https://spec.openapis.org/compliance/ontology#paths> <https://example.com/minimal#/paths> .
<https://example.com/minimal#/info> <http://www.w3.org/2000/01/rdf-schema#label> "Info" .
<https://example.com/minimal> <http://www.w3.org/2000/01/rdf-schema#label> "minimal.json" .
<https://example.com/minimal> <https://spec.openapis.org/compliance/ontology#locatedAt> "file:///Users/handrews/src/oascomply/tutorial/minimal.json"^^<http://www.w3.org/2001/XMLSchema#anyURI> .
<https://example.com/minimal> <https://spec.openapis.org/compliance/ontology#root> <https://example.com/minimal#> .
<https://example.com/minimal#/info> <https://spec.openapis.org/compliance/ontology#allowsExtensions> "true"^^<http://www.w3.org/2001/XMLSchema#boolean> .
<https://example.com/minimal#/info> <https://spec.openapis.org/compliance/ontology#parent> <https://example.com/minimal#> .
<https://example.com/minimal#/info> <https://spec.openapis.org/compliance/ontology#title> "Minimal OAS 3.0 description" .
<https://example.com/minimal#/info> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://spec.openapis.org/compliance/ontology#3.0-Info> .
<https://example.com/minimal#> <https://spec.openapis.org/compliance/ontology#allowsExtensions> "true"^^<http://www.w3.org/2001/XMLSchema#boolean> .
<https://example.com/minimal#/paths> <http://www.w3.org/2000/01/rdf-schema#label> "Paths" .
<https://example.com/minimal#/paths> <https://spec.openapis.org/compliance/ontology#allowsExtensions> "true"^^<http://www.w3.org/2001/XMLSchema#boolean> .
<https://example.com/minimal#/info> <https://spec.openapis.org/compliance/ontology#apiDescriptionVersion> "1.0.0" .
<https://example.com/minimal> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://schema.org/DigitalDocument> .
<https://example.com/minimal#> <http://www.w3.org/2000/01/rdf-schema#label> "OpenAPI" .
<https://example.com/minimal#/paths> <https://spec.openapis.org/compliance/ontology#parent> <https://example.com/minimal#> .
<https://example.com/minimal#> <https://spec.openapis.org/compliance/ontology#info> <https://example.com/minimal#/info> .
<https://example.com/minimal#> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://spec.openapis.org/compliance/ontology#3.0-OpenAPI> .
<https://example.com/minimal#/paths> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://spec.openapis.org/compliance/ontology#3.0-Paths> .
<https://example.com/minimal#> <https://spec.openapis.org/compliance/ontology#oasVersion> "3.0.3" .
That's easy for machines to parse but a bit hard for people to read.
While there are RDF serialization formats that try to be human-friendly,
they don't work well with JSON Pointer fragments. The -o
option can
take a format identifer (such as -o ttl
for Turtle or -o json-ld
for
JSON-LD) for any format supported by
rdflib
. But it also offers
a custom TOML format (-o toml
) meant for human-friendly output:
[namespaces]
rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
rdfs = "http://www.w3.org/2000/01/rdf-schema#"
xsd = "http://www.w3.org/2001/XMLSchema#"
schema = "https://schema.org/"
oas = "https://spec.openapis.org/compliance/ontology#"
"oas3.0" = "https://spec.openapis.org/compliance/ontology#3.0-"
["https://example.com/minimal"]
"rdf:type" = "schema:DigitalDocument"
"rdfs:label" = [ "minimal.json",]
"oas:locatedAt" = [ "file:///Users/handrews/src/oascomply/tutorial/minimal.json", "xsd:anyURI",]
"oas:root" = "https://example.com/minimal#"
["https://example.com/minimal#"]
"rdf:type" = "oas3.0:OpenAPI"
"rdfs:label" = [ "OpenAPI",]
"oas:allowsExtensions" = [ "true", "xsd:boolean",]
"oas:info" = "https://example.com/minimal#/info"
"oas:oasVersion" = [ "3.0.3",]
"oas:paths" = "https://example.com/minimal#/paths"
["https://example.com/minimal#/info"]
"rdf:type" = "oas3.0:Info"
"rdfs:label" = [ "Info",]
"oas:allowsExtensions" = [ "true", "xsd:boolean",]
"oas:apiDescriptionVersion" = [ "1.0.0",]
"oas:parent" = "https://example.com/minimal#"
"oas:title" = [ "Minimal OAS 3.0 description",]
["https://example.com/minimal#/paths"]
"rdf:type" = "oas3.0:Paths"
"rdfs:label" = [ "Paths",]
"oas:allowsExtensions" = [ "true", "xsd:boolean",]
"oas:parent" = "https://example.com/minimal#"
Keep in mind that for programmatic use, libraries like Python's
rdflib
can parse N-Triples
directly and offer far more powerful features for working with the data.
This TOML format is intended purely for human convenience, and is somewhat experimental. Feedback is encouraged!
We'll talk more about namespaces and how this condensed format works further down, but lets dig into the N-Triples format first.
N-Triples may look like a bit of a mess with all of the URIs, but it's really pretty straightforward. Each line represents an RDF triple, which is an edge connecting two nodes in the graph:
- Concepts, including data types, relationships, OAS object types (Path Item Object, Schema Object, etc.), and the actual typed objects from your API description, are represented by URIs in angle brackets.
- Literal values, such as OAS object properties from your API description, are quoted strings.
- Literals with a data type other than a plain string are followed by
^^
and a URI in angle brackets indicating the type.
Edges are unidirectional, from the concept on the left (the subject) to the concept or literal on the right (the object). The URI in the middle is called the predicate, and describes the relationship represented by the edge (I'll just call it the relationship from here on).
This will likely make more sense with a picture. Here is an image showing the exact data displayed above:
This visualization does not show the literal values (the tool used to generate it shows them in a side panel when you click on a node).
Every N-Triples line looks like one of the following three structures:1
<subject_concept> <relationship> <object_concept> .
<subject_concept> <relationship> "string literal value" .
<subject_concept> <relationship> "literal value"^^<datatype> .
The .
terminates the line, and is mostly there to make this format
a valid subset of several more complex formats.
Since the same node can appear in either the subject or object position in many triples, these lines in the output connect through shared nodes to form a graph:
<a> <x> <b> .
<b> <y> <c> .
<a> <z> "d" .
This is a graph with three nodes, <a>
, <b>
, and <c>
, with edges
<x>
connecting <a>
to <b>
, and <y>
connecting <b>
to <c>
.
Node <a>
is also connected to a literal string "d" by edge <z>
.2
RDF-aware tools are good at making use of the full URIs behind the scenes, but if you're reading the output directly, it's a bit much.
In most cases, the absolute URI (the part before the #
) serves as
a namespace, and the URI fragment (the part after the #
)
identifies a specific thing within that namespace.
Here are the namespaces you will see in in oascomply
output
in addition to the URI (which is by default the file:
URL for local files)
of your own document. It is typical to include the trailing #
as part of
the namespace.3
First, the namespaces defined outside of OpenAPI:
http://www.w3.org/2001/XMLSchema#
– XSD, which is used for its datatype definitions; no actual understanding of XML Schema is required!http://www.w3.org/1999/02/22-rdf-syntax-ns#
– RDF, which defines fundamental concepts and relationships such as thetype
relationship; you will notice this relationship connecting locations in your file to their OAS Object typeshttp://www.w3.org/2000/01/rdf-schema#
– RDFS (RDF Schema), which includes a few more fairly fundamental concepts and relationships; the distinction between RDF and RDFS is not very important here, andoascomply
mostly just uses thelabel
relationship which informs tools how to display nodes for humanshttps://schema.org/
– Schema.org defines many common concepts; currentlyoascomply
just uses the "DigitalDocument" concept (note that values in this namespace are part of the URI path, not the fragment)
OpenAPI concepts use the following namespace:
https://spec.openapis.org/compliance/ontology#
For OpenAPI object types, a version prefix, e.g. 3.0-Schema
, is part of the fragment. For relationships, including object fields with string, number, or boolean values, there is no version prefix. _(This may not remain the case, it will depend on feedback).
The TOML output format is an experiment in providing something a little more intuitive. Here's a portion of the earlier example for reference:
[namespaces]
rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
rdfs = "http://www.w3.org/2000/01/rdf-schema#"
xsd = "http://www.w3.org/2001/XMLSchema#"
schema = "https://schema.org/"
oas = "https://spec.openapis.org/compliance/ontology#"
"oas3.0" = "https://spec.openapis.org/compliance/ontology#3.0-"
["https://example.com/minimal"]
"rdf:type" = "schema:DigitalDocument"
"rdfs:label" = [ "minimal.json",]
"oas:locatedAt" = [ "file:///Users/handrews/src/oascomply/tutorial/minimal.json", "xsd:anyURI",]
"oas:root" = "https://example.com/minimal#"
Currently the format is as follows:
- The first section,
[namespaces]
, defines the short names for each namespace, and the URI prefix the short name represents - The remaining section headings are subjects in the RDF triples
- Under each heading, the keys are predicates (relationships) and the values are objects, forming a triple with the subject in the heading
- String literal object values are one-element arrays
- Typed literal object values are two-element arrays, with the datatype as the second element
- Multiple objects for the same subject and predicate appear as arrays
While the two different uses of arrays is somewhat ambiguous for the two-element array case, in practice datatypes tend to be very recognizable. However, feedback on this is encouraged.
User-friendly error reporting is the focus of Milestone 2, which is planned to be complete in mid-July, 2023. Currently, errors are reported in whatever structure is returned by whatever library encountered the error, or whatever structure seemed most convenient. And sometimes there's just a stack trace.
Rest assured that this is not considered good UX/DX. In the meantime, here are some current ways errors are reported.
oascomply
looks for an openapi
field in the root object of
at least one of the documents it is passed. The first document
in the argument list containing the field is the API description
that is validated. If no documents contain an openapi
field,
the program ends after logging an error:
~/src/oascomply -f tutorial/invalid/no-openapi.json
ERROR:oascomply.apidescription:No document contains an 'openapi' field!
The first step oascomply
performs is validation with the patched
version of the OAS JSON Schema. The patches can be found in the
patches/oas/v3.0
directory in this repository. They are applied
to the OAS schema found under
submodules/OpenAPI-Specification/schemas/v3.0
prior to each
milestone release, and the results checked in under the
schemas/oas/v3.0
directory.
JSON Schema validation errors are reporting using the "detailed" standard output format:
~/src/oascomply % oascomply -f tutorial/invalid/no-info.json
CRITICAL:oascomply.apidescription:JSON Schema validation of file:///Users/handrews/src/oascomply/tutorial/invalid/no-info.json failed!
{
"valid": false,
"instanceLocation": "",
"keywordLocation": "",
"absoluteKeywordLocation": "https://spec.openapis.org/compliance/schemas/oas/3.0/2023-06#",
"errors": [
{
"instanceLocation": "",
"keywordLocation": "/required",
"absoluteKeywordLocation": "https://spec.openapis.org/compliance/schemas/oas/3.0/2023-06#/required",
"error": "The object is missing required properties ['info']"
}
]
}
Example and default values are validated against the relevant schemas,
and report any errors in those validations in the same format. In this
example, there are errors from two distinct validation processes:
one for a "default"
in a Parameter Object, and one for an "example"
in a Media Type Object:
~/src/oascomply % oascomply -f tutorial/invalid/examples.yaml
ERROR:oascomply.apidescription:{
"valid": false,
"instanceLocation": "/paths/~1things/get/parameters/0/schema/default",
"keywordLocation": "",
"absoluteKeywordLocation": "file:/Users/handrews/src/oascomply/tutorial/invalid/examples.yaml#/paths/~1things/get/parameters/0/schema",
"errors": [
{
"instanceLocation": "/paths/~1things/get/parameters/0/schema/default",
"keywordLocation": "/format",
"absoluteKeywordLocation": "file:/Users/handrews/src/oascomply/tutorial/invalid/examples.yaml#/paths/~1things/get/parameters/0/schema/format",
"error": "The instance is invalid against the \"uint8\" format: -1 is outside of uint8 range"
}
]
}
ERROR:oascomply.apidescription:{
"valid": false,
"instanceLocation": "/paths/~1things/get/responses/200/content/application~1json/example",
"keywordLocation": "",
"absoluteKeywordLocation": "file:/Users/handrews/src/oascomply/tutorial/invalid/examples.yaml#/paths/~1things/get/responses/200/content/application~1json/schema",
"errors": [
{
"instanceLocation": "/paths/~1things/get/responses/200/content/application~1json/example",
"keywordLocation": "/format",
"absoluteKeywordLocation": "file:/Users/handrews/src/oascomply/tutorial/invalid/examples.yaml#/paths/~1things/get/responses/200/content/application~1json/schema/format",
"error": "The instance is invalid against the \"uri\" format: '/foo/bar' is not a valid 'URI'."
}
]
}
API description contains errors
Note that these errors come from the "format"
keyword. While not all standard
format values are supported yet, many are, and the remainder will be added
during Milestone 2.
Semantic validation using the RDF graph produce by the parser is the focus of Milestone 3, which is expected in late August 2023. Currently, a very simple example of using the graph for validation is implemented in the form of checking that reference targets are of the correct type.
In this example, a $ref for a Path Item Object points to
a Schema Object. This cannot be detected just by validating the
reference target against a JSON Schema because both a Path Item Object
and a Schema Object can be empty objects. However, the parsed graph
includes the information that objects under #/components/schemas
are Schema Objects:
~/src/oascomply % oascomply -f tutorial/invalid/wrong-ref-type.yaml
ERROR:oascomply.apidescription:{
"json_reference": "file:///Users/handrews/src/oascomply/tutorial/invalid/wrong-ref-type.yaml#/paths/~1stuff/$ref",
"reference_context": {
"node": "file:///Users/handrews/src/oascomply/tutorial/invalid/wrong-ref-type.yaml#/paths/~1stuff"
},
"reference_target": "file:///Users/handrews/src/oascomply/tutorial/invalid/wrong-ref-type.yaml#/components/schemas/stuff",
"expected": [
"file:///Users/handrews/src/oascomply/tutorial/invalid/wrong-ref-type.yaml#/components/schemas/stuff",
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
"https://spec.openapis.org/compliance/ontology#3.0-PathItem"
],
"actual": [
"file:///Users/handrews/src/oascomply/tutorial/invalid/wrong-ref-type.yaml#/components/schemas/stuff",
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
"https://spec.openapis.org/compliance/ontology#3.0-Schema"
]
}
API description contains errors
Here we see information about which reference has the error, and then the
expected and actual type information as semantic triples.
The subject of each triple is the reference target location, the predicate
(relationship) is the standard RDF:type relationship, and the object
gives the type in the OAS namespace. Here we can see that we expected
as 3.0-PathItem
but found a 3.0-Schema
.
The oascomply.oas30dialect
package adds support for the OAS 3.0
schema dialect for the Python package jschon
. The oas30-schema
command line tool uses that module to validate a given instance
against an OAS 3.0-compliant Schema Object schema, and optionally
produce annotation output in any of the JSON Schema draft 2020-12
standard output formats (even though the OAS 3.0 dialect is
based on JSON Schema draft-04):
~/src/oascomply % oas30-schema -h
usage: oas30-schema [-h] [-r REFS] [-o [{basic,detailed,verbose}]]
[-e {basic,detailed,verbose}]
instance schema
Validates the instance against schemas using the OAS 3.0 Schema Object dialect
described by the metaschema "{OAS30_DIALECT_METASCHEMA}"
positional arguments:
instance The JSON or YAML file to validate
schema The schema, in JSON or YAML format, to use
optional arguments:
-h, --help show this help message and exit
-r REFS, --referenced-schema REFS
NOT YET SUPPORTED! An additional schema from which to
resolve references; can be passed multiple times; note that
schema documents that reference each other are not currently
supported; currently, if schema A references schema B, then
schema B must be passed with -r *BEFORE* schema A
-o [{basic,detailed,verbose}], --output [{basic,detailed,verbose}]
On success, print the annotation output to stdout using the
given standard format; the default annotation format is
'basic'
-e {basic,detailed,verbose}, --error-format {basic,detailed,verbose}
Set the output format to use for error reporting; the
default format is 'detailed'
Note that the schema "https://spec.openapis.org/oas/v3.0/dialect/base" is *NOT*
provided by the OpenAPI Initiative, but is part of the oascomply package
Footnotes
-
There is a fourth structure used for language-tagged strings, but
oascomply
is not currently aware of what (human, not programming) language an API description uses, so this structure does not appear in the output. ↩ -
If you are familiar with property graphs, which are another type of graph database, literal nodes are roughly equivalent to properties in property graphs (although RDF refers to all edges as properties). ↩
-
In more complex RDF formats, namespaces are displayed as short names that substitute for a URI prefix (technically an IRI prefix, but that just means URI with unicode support). You can see this by passing
-o ttl
which produces the relatively human-readable Turtle format. It will include things likerdfs:label
in place of<http://www.w3.org/2000/01/rdf-schema#label>
. Unfortunately, when using these short prefixes, many characters used in JSON Pointer fragments, including/
and$
, are not allowed. This means most URIs for your API description end up written out in full, making the prefix concept less useful than it really should be. N-Triples was selected in part to avoid confusion around this. ↩