Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions DEPENDENCIES
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02
core https://github.com/sourcemeta/core 783d6646d159d0862755aca042dda0c426e918b2
jsonbinpack https://github.com/sourcemeta/jsonbinpack 9c6be797ea4f2a586deff6f871506e7f729f3e8e
blaze https://github.com/sourcemeta/blaze 049a75b2a45c1178e6d484e349c449fe4b36ae4d
core https://github.com/sourcemeta/core d0bf624114232b8397bddad9a0c0e192c7315deb
jsonbinpack https://github.com/sourcemeta/jsonbinpack 147050385dce7086a28ce93e18a4c3baa9719136
blaze https://github.com/sourcemeta/blaze 1b09fa0898cc986cc501322db2680d3fd6f093bf
ctrf https://github.com/ctrf-io/ctrf 93ea827d951390190171d37443bff169cf47c808
10 changes: 5 additions & 5 deletions docs/bundle.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ two spaces. Use `--indentation/-n` to set the width in either case.
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2, the `bundle` command treats it as
an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than
as a schema, and bundles it as one: every other document the description spans
is embedded into the Components Object, and every reference that reached one is
rewritten to name where it landed. Any other revision, such as v3.0, is
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `bundle` command treats
it as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema, and bundles it as one: every other document the description
spans is embedded into the Components Object, and every reference that reached
one is rewritten to name where it landed. An unsupported `openapi` revision is
rejected rather than read as a schema.
The result is ordered exactly as
[`jsonschema fmt`](./format.markdown) would order it.
Expand Down
10 changes: 5 additions & 5 deletions docs/compile.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ jsonschema compile path/to/my/schema.json --entrypoint '/$defs/MyType' > templat

### Compile a Schema Object of an OpenAPI description

If the input is an OpenAPI spec v3.1 or v3.2, the `compile` command treats it as
an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than
as a schema. Any other revision, such as v3.0, is rejected rather than read as a
schema. A description declares many schemas and no root one, so you must say
which to compile with `--entrypoint/-p`, given as a JSON Pointer into the
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `compile` command treats
it as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema. An unsupported `openapi` revision is rejected rather than read
as a schema. A description declares many schemas and no root one, so you must
say which to compile with `--entrypoint/-p`, given as a JSON Pointer into the
description or as the identifier a Schema Object declares.

```sh
Expand Down
11 changes: 6 additions & 5 deletions docs/format.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ check their adherence on a continuous integration environment.
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2, the `fmt` command treats it as an
[OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than as
a schema. The fields of the description are ordered following an OpenAPI aware
convention, and every Schema Object it holds is formatted as a schema would be.
Any other revision, such as v3.0, is rejected rather than read as a schema.
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `fmt` command treats it
as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema. The fields of the description are ordered following an OpenAPI
aware convention, and every Schema Object it holds is formatted as a schema
would be. An unsupported `openapi` revision is rejected rather than read as a
schema.

`--keep-ordering/-k` applies to a description as it does to a schema, leaving
every key where it was written.
Expand Down
10 changes: 5 additions & 5 deletions docs/inspect.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ evaluation step. This is often useful for debugging purposes.
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2, the `inspect` command treats it as
an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than
as a schema, and inspects it in a similar fashion, reporting on the Objects it
holds, the operations it exposes, and the Schema Objects within it. Any other
revision, such as v3.0, is rejected rather than read as a schema.
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `inspect` command treats
it as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema, and inspects it in a similar fashion, reporting on the Objects
it holds, the operations it exposes, and the Schema Objects within it. An
unsupported `openapi` revision is rejected rather than read as a schema.

Examples
--------
Expand Down
10 changes: 5 additions & 5 deletions docs/lint.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,13 @@ object entry with `topLevel` set to `true` marks the rule as top-level only.
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2 (v3.0 and older are NOT fully
compliant with the JSON Schema specification), the `lint` command treats it as
an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than
as a schema. Every Schema Object in the description (for example, the ones
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `lint` command treats
it as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema. Every Schema Object in the description (for example, the ones
under `components/schemas` or the `schema` of a parameter, request body, or
response) is linted, and every warning reports its location from the root of
the description.
the description. An unsupported `openapi` revision is rejected rather than read
as a schema.

With `--format/-m`, the whole description is formatted, not only the Schema
Objects the rules ran against.
Expand Down
10 changes: 5 additions & 5 deletions docs/metaschema.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ code 2.
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2, the `metaschema` command treats it
as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema, and checks every Schema Object it holds against the
meta-schema of the dialect that schema is written in. Any other revision, such
as v3.0, is rejected rather than read as a schema.
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `metaschema` command
treats it as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description
rather than as a schema, and checks every Schema Object it holds against the
meta-schema of the dialect that schema is written in. An unsupported `openapi`
revision is rejected rather than read as a schema.

A Schema Object that declares its own `$schema` is held to that dialect rather
than to the one the description has in force, so a description mixing dialects
Expand Down
19 changes: 16 additions & 3 deletions docs/rdf.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,22 @@ The resulting document would look like this:
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2, the `rdf` command treats it as an
[OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than as a
schema. Any other revision, such as v3.0, is rejected rather than read as a schema.
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `rdf` command treats it
as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema. An unsupported `openapi` revision is rejected rather than read
as a schema.

A v3.0 description is read as a description, but its Schema Objects cannot be
used here. The `x-jsonld-*` keywords rely on annotation collection, which JSON
Schema only introduced in 2019-09, and the v3.0 Schema Object dialect predates
it. Use [`jsonschema upgrade`](./upgrade.markdown) to move such a Schema Object
onto a newer dialect first. That command reads schemas rather than
descriptions, so point it at the Schema Object itself:

```sh
jsonschema upgrade path/to/my/schema.json \
--default-dialect tag:spec.openapis.org,2024-10-18:oas/3.0/dialect
```

A description declares many schemas and no root one, so you must say which to
use with `--entrypoint/-p`, given as a JSON Pointer into the description or as
Expand Down
2 changes: 1 addition & 1 deletion docs/test.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ OpenAPI Descriptions

A `target` names the schema under test wherever it lives, so the document at
its base does not have to be a schema itself. If it is an
[OpenAPI](https://spec.openapis.org/oas/latest.html) spec v3.1 or v3.2
[OpenAPI](https://spec.openapis.org/oas/latest.html) spec v3.0, v3.1 or v3.2
description, the `test` command reads it as one and runs the test cases against
the Schema Object that the fragment names:

Expand Down
17 changes: 14 additions & 3 deletions docs/upgrade.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,20 @@ output.
The dialects it walks through are the official JSON Schema ones up to 2020-12,
and then the OpenAPI Schema Object dialects, each of which is 2020-12 plus a
vocabulary of its own. Those sit past the default, so a schema lands on one of
them only when it is asked for. Note this upgrades a *schema* onto an OpenAPI
dialect, and is a separate matter from reading an OpenAPI description, which
this command does not do.
them only when it is asked for. The v3.0 Schema Object is not among them: it is
a dialect of its own rather than 2020-12 with something added, so there is no
ladder leading onto it. Note this upgrades a *schema* onto an OpenAPI dialect,
and is a separate matter from reading an OpenAPI description, which this
command does not do.

A v3.0 Schema Object is a dialect this command upgrades *from*, however. Since
v3.0 has no `$schema` keyword to declare itself with, name the dialect with
`--default-dialect/-d`:

```sh
jsonschema upgrade path/to/my/schema.json \
--default-dialect tag:spec.openapis.org,2024-10-18:oas/3.0/dialect
```

The result is printed in the format the input was written in: a YAML schema
upgrades into YAML and a JSON schema into JSON. A YAML result keeps the width
Expand Down
8 changes: 4 additions & 4 deletions docs/validate.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ non-sense results.
OpenAPI Descriptions
--------------------

If the input is an OpenAPI spec v3.1 or v3.2, the `validate` command treats it as
an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather than
as a schema, and validates the instance against one of the Schema Objects it
holds. Any other revision, such as v3.0, is rejected rather than read as a
If the input is an OpenAPI spec v3.0, v3.1 or v3.2, the `validate` command treats
it as an [OpenAPI](https://spec.openapis.org/oas/latest.html) description rather
than as a schema, and validates the instance against one of the Schema Objects
it holds. An unsupported `openapi` revision is rejected rather than read as a
schema.

A description declares many schemas and no root one, so you must say which to
Expand Down
12 changes: 12 additions & 0 deletions src/command_bundle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options)
throw sourcemeta::core::FileError<
sourcemeta::core::SchemaAnchorCollisionError>(schema_display_path,
error);
} catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
const auto position{parsed_schema.positions.get(error.location())};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This can report an external Schema Object's dialect error at an unrelated line in the entry document when its local pointer matches one there. Preserve the error's document provenance before mapping it to source positions, or avoid adding an entry-document position for errors from resolved schemas.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/command_bundle.cc, line 192:

<comment>This can report an external Schema Object's dialect error at an unrelated line in the entry document when its local pointer matches one there. Preserve the error's document provenance before mapping it to source positions, or avoid adding an entry-document position for errors from resolved schemas.</comment>

<file context>
@@ -188,6 +188,18 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options)
         sourcemeta::core::SchemaAnchorCollisionError>(schema_display_path,
                                                       error);
+  } catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
+    const auto position{parsed_schema.positions.get(error.location())};
+    if (position.has_value()) {
+      throw PositionError<sourcemeta::core::FileError<
</file context>

if (position.has_value()) {
throw PositionError<sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>>(
std::get<0>(position.value()), std::get<1>(position.value()),
schema_display_path, error);
}

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>(schema_display_path,
error);
} catch (const sourcemeta::core::SchemaReferenceError &error) {
const auto position{parsed_schema.positions.get(error.location())};
if (position.has_value()) {
Expand Down
17 changes: 17 additions & 0 deletions src/command_codegen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ auto sourcemeta::jsonschema::codegen(const sourcemeta::core::Options &options)
const std::filesystem::path schema_path{options.positional().front()};
auto parsed_schema{read_file(schema_path)};

if (parsed_schema.multidocument) {
throw MultiDocumentInputError{
"This command does not support input with multiple documents",
schema_path};
}

// An OpenAPI description is not a schema, so it is turned down rather than
// read as one, which would fail over the dialect it does not declare
if (is_openapi_document(parsed_schema.document)) {
Expand Down Expand Up @@ -121,6 +127,17 @@ auto sourcemeta::jsonschema::codegen(const sourcemeta::core::Options &options)

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaAnchorCollisionError>(schema_path, error);
} catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
const auto position{parsed_schema.positions.get(error.location())};
if (position.has_value()) {
throw PositionError<sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>>(
std::get<0>(position.value()), std::get<1>(position.value()),
schema_path, error);
}

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>(schema_path, error);
} catch (const sourcemeta::core::SchemaResolutionError &error) {
throw sourcemeta::core::FileError<sourcemeta::core::SchemaResolutionError>(
schema_path, error);
Expand Down
17 changes: 17 additions & 0 deletions src/command_compile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ auto sourcemeta::jsonschema::compile(const sourcemeta::core::Options &options)
const auto is_openapi{is_openapi_document(parsed_schema.document)};
const std::filesystem::path display_path{schema_path};

if (parsed_schema.multidocument) {
throw MultiDocumentInputError{
"This command does not support input with multiple documents",
display_path};
}

reject_unsupported_openapi(parsed_schema.document, display_path);

if (!is_openapi && !parsed_schema.document.is_object() &&
Expand Down Expand Up @@ -169,6 +175,17 @@ auto sourcemeta::jsonschema::compile(const sourcemeta::core::Options &options)

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaAnchorCollisionError>(schema_path, error);
} catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
const auto position{parsed_schema.positions.get(error.location())};
if (position.has_value()) {
throw PositionError<sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>>(
std::get<0>(position.value()), std::get<1>(position.value()),
schema_path, error);
}

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>(schema_path, error);
} catch (const sourcemeta::core::SchemaReferenceError &error) {
const auto position{parsed_schema.positions.get(error.location())};
if (position.has_value()) {
Expand Down
23 changes: 23 additions & 0 deletions src/command_fmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,17 @@ auto sourcemeta::jsonschema::fmt(const sourcemeta::core::Options &options)

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaAnchorCollisionError>(display_path, error);
} catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
const auto position{parsed.positions.get(error.location())};
if (position.has_value()) {
throw PositionError<sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>>(
std::get<0>(position.value()), std::get<1>(position.value()),
display_path, error);
}

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>(display_path, error);
} catch (const sourcemeta::core::SchemaRelativeMetaschemaResolutionError
&error) {
throw sourcemeta::core::FileError<
Expand Down Expand Up @@ -371,6 +382,18 @@ auto sourcemeta::jsonschema::fmt(const sourcemeta::core::Options &options)
throw sourcemeta::core::FileError<
sourcemeta::core::SchemaAnchorCollisionError>(entry.resolution_base,
error);
} catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
const auto position{entry.positions.get(error.location())};
if (position.has_value()) {
throw PositionError<sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>>(
std::get<0>(position.value()), std::get<1>(position.value()),
entry.resolution_base, error);
}

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>(entry.resolution_base,
error);
} catch (const sourcemeta::core::SchemaRelativeMetaschemaResolutionError
&error) {
throw sourcemeta::core::FileError<
Expand Down
35 changes: 24 additions & 11 deletions src/command_inspect.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include <sourcemeta/core/json.h>
#include <sourcemeta/core/jsonschema.h>
#include <sourcemeta/core/openapi.h>
#include <sourcemeta/core/yaml.h>

#include <cctype> // std::toupper
#include <iostream> // std::cout
Expand Down Expand Up @@ -481,17 +480,13 @@ auto sourcemeta::jsonschema::inspect(const sourcemeta::core::Options &options)

sourcemeta::core::PointerPositionTracker positions;
auto property_storage = std::make_shared<std::deque<std::string>>();
bool multidocument{false};
const sourcemeta::core::JSON schema{[&]() {
if (schema_from_stdin) {
auto parsed{read_from_stdin()};
positions = std::move(parsed.positions);
property_storage = std::move(parsed.property_storage);
return std::move(parsed.document);
}
sourcemeta::core::JSON document{sourcemeta::core::JSON{nullptr}};
auto callback = make_position_callback(positions, property_storage);
sourcemeta::core::read_yaml_or_json(schema_path, document, callback);
return document;
auto parsed{schema_from_stdin ? read_from_stdin() : read_file(schema_path)};
positions = std::move(parsed.positions);
property_storage = std::move(parsed.property_storage);
multidocument = parsed.multidocument;
return std::move(parsed.document);
}()};

// An OpenAPI description is not a schema, so what it goes by wherever we
Expand All @@ -502,6 +497,12 @@ auto sourcemeta::jsonschema::inspect(const sourcemeta::core::Options &options)
schema_from_stdin ? (is_openapi ? openapi_stdin_path() : stdin_path())
: schema_path};

if (multidocument) {
throw MultiDocumentInputError{
"This command does not support input with multiple documents",
schema_resolution_base};
}

reject_unsupported_openapi(schema, schema_resolution_base, &positions);

if (!is_openapi && !schema.is_object() && !schema.is_boolean()) {
Expand Down Expand Up @@ -552,6 +553,18 @@ auto sourcemeta::jsonschema::inspect(const sourcemeta::core::Options &options)
throw sourcemeta::core::FileError<
sourcemeta::core::SchemaAnchorCollisionError>(schema_resolution_base,
error);
} catch (const sourcemeta::core::SchemaDialectImpossibleError &error) {
const auto position{positions.get(error.location())};
if (position.has_value()) {
throw PositionError<sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>>(
std::get<0>(position.value()), std::get<1>(position.value()),
schema_resolution_base, error);
}

throw sourcemeta::core::FileError<
sourcemeta::core::SchemaDialectImpossibleError>(schema_resolution_base,
error);
} catch (const sourcemeta::core::SchemaReferenceError &error) {
const auto position{positions.get(error.location())};
if (position.has_value()) {
Expand Down
Loading
Loading