diff --git a/src/core/jsonschema/bundle.cc b/src/core/jsonschema/bundle.cc index bbdd91427..f28414a60 100644 --- a/src/core/jsonschema/bundle.cc +++ b/src/core/jsonschema/bundle.cc @@ -19,7 +19,7 @@ auto is_official_metaschema_reference( assert(!pointer.empty()); assert(pointer.back().is_property()); return pointer.back().to_property() == "$schema" && - sourcemeta::core::is_known_schema(destination); + sourcemeta::core::is_official_schema(destination); } auto dependencies_internal(const sourcemeta::core::JSON &schema, diff --git a/src/core/jsonschema/include/sourcemeta/core/jsonschema.h b/src/core/jsonschema/include/sourcemeta/core/jsonschema.h index 5f6a23796..658168beb 100644 --- a/src/core/jsonschema/include/sourcemeta/core/jsonschema.h +++ b/src/core/jsonschema/include/sourcemeta/core/jsonschema.h @@ -44,6 +44,12 @@ auto schema_resolver(const std::string_view identifier) -> std::optional; SOURCEMETA_CORE_JSONSCHEMA_EXPORT auto is_known_schema(const std::string_view identifier) noexcept -> bool; +/// @ingroup jsonschema +/// Check if a given URI corresponds to an official schema released by the +/// JSON Schema organisation +SOURCEMETA_CORE_JSONSCHEMA_EXPORT +auto is_official_schema(const std::string_view identifier) noexcept -> bool; + /// @ingroup jsonschema /// A default schema walker with support for a wide range of drafts SOURCEMETA_CORE_JSONSCHEMA_EXPORT diff --git a/src/core/jsonschema/known_resolver.in.cc b/src/core/jsonschema/known_resolver.in.cc index a2c1d48ba..2ece4db5c 100644 --- a/src/core/jsonschema/known_resolver.in.cc +++ b/src/core/jsonschema/known_resolver.in.cc @@ -536,3 +536,69 @@ auto sourcemeta::core::is_known_schema( const std::string_view identifier) noexcept -> bool { return parse_identifier(identifier) != KnownSchema::UNKNOWN; } + +auto sourcemeta::core::is_official_schema( + const std::string_view identifier) noexcept -> bool { + switch (parse_identifier(identifier)) { + case KnownSchema::JSONSCHEMA_2020_12: + case KnownSchema::HYPERSCHEMA_2020_12: + case KnownSchema::JSONSCHEMA_2020_12_APPLICATOR: + case KnownSchema::JSONSCHEMA_2020_12_CONTENT: + case KnownSchema::JSONSCHEMA_2020_12_CORE: + case KnownSchema::JSONSCHEMA_2020_12_FORMAT_ANNOTATION: + case KnownSchema::JSONSCHEMA_2020_12_FORMAT_ASSERTION: + case KnownSchema::JSONSCHEMA_2020_12_HYPER_SCHEMA: + case KnownSchema::JSONSCHEMA_2020_12_META_DATA: + case KnownSchema::JSONSCHEMA_2020_12_UNEVALUATED: + case KnownSchema::JSONSCHEMA_2020_12_VALIDATION: + case KnownSchema::LINKS_2020_12: + case KnownSchema::JSONSCHEMA_2020_12_OUTPUT: + case KnownSchema::JSONSCHEMA_2019_09: + case KnownSchema::HYPERSCHEMA_2019_09: + case KnownSchema::JSONSCHEMA_2019_09_APPLICATOR: + case KnownSchema::JSONSCHEMA_2019_09_CONTENT: + case KnownSchema::JSONSCHEMA_2019_09_CORE: + case KnownSchema::JSONSCHEMA_2019_09_FORMAT: + case KnownSchema::JSONSCHEMA_2019_09_HYPER_SCHEMA: + case KnownSchema::JSONSCHEMA_2019_09_META_DATA: + case KnownSchema::JSONSCHEMA_2019_09_VALIDATION: + case KnownSchema::LINKS_2019_09: + case KnownSchema::JSONSCHEMA_2019_09_OUTPUT: + case KnownSchema::HYPERSCHEMA_2019_09_OUTPUT: + case KnownSchema::JSONSCHEMA_DRAFT7: + case KnownSchema::HYPERSCHEMA_DRAFT7: + case KnownSchema::LINKS_DRAFT7: + case KnownSchema::HYPERSCHEMA_DRAFT7_OUTPUT: + case KnownSchema::JSONSCHEMA_DRAFT6: + case KnownSchema::HYPERSCHEMA_DRAFT6: + case KnownSchema::LINKS_DRAFT6: + case KnownSchema::JSONSCHEMA_DRAFT4: + case KnownSchema::HYPERSCHEMA_DRAFT4: + case KnownSchema::LINKS_DRAFT4: + case KnownSchema::JSONSCHEMA_DRAFT3: + case KnownSchema::HYPERSCHEMA_DRAFT3: + case KnownSchema::LINKS_DRAFT3: + case KnownSchema::JSON_REF_DRAFT3: + case KnownSchema::JSONSCHEMA_DRAFT2: + case KnownSchema::HYPERSCHEMA_DRAFT2: + case KnownSchema::LINKS_DRAFT2: + case KnownSchema::JSON_REF_DRAFT2: + case KnownSchema::JSONSCHEMA_DRAFT1: + case KnownSchema::HYPERSCHEMA_DRAFT1: + case KnownSchema::LINKS_DRAFT1: + case KnownSchema::JSON_REF_DRAFT1: + case KnownSchema::JSONSCHEMA_DRAFT0: + case KnownSchema::HYPERSCHEMA_DRAFT0: + case KnownSchema::LINKS_DRAFT0: + case KnownSchema::JSON_REF_DRAFT0: + return true; + case KnownSchema::OAS_3_2_DIALECT_2025_09_17: + case KnownSchema::OAS_3_2_META_2025_09_17: + case KnownSchema::OAS_3_1_DIALECT_BASE: + case KnownSchema::OAS_3_1_META_BASE: + case KnownSchema::UNKNOWN: + return false; + } + + return false; +} diff --git a/test/jsonschema/jsonschema_bundle_2020_12_test.cc b/test/jsonschema/jsonschema_bundle_2020_12_test.cc index 44d097cee..3ceb7ec87 100644 --- a/test/jsonschema/jsonschema_bundle_2020_12_test.cc +++ b/test/jsonschema/jsonschema_bundle_2020_12_test.cc @@ -727,6 +727,73 @@ TEST(JSONSchema_bundle_2020_12, metaschema) { EXPECT_EQ(document, expected); } +TEST(JSONSchema_bundle_2020_12, openapi_3_1_dialect) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://spec.openapis.org/oas/3.1/dialect/base", + "type": "object" + })JSON"); + + sourcemeta::core::bundle(document, sourcemeta::core::schema_walker, + test_resolver); + + EXPECT_EQ(document.at("$schema").to_string(), + "https://spec.openapis.org/oas/3.1/dialect/base"); + EXPECT_TRUE(document.defines("$defs")); + EXPECT_TRUE(document.at("$defs").is_object()); + EXPECT_EQ(document.at("$defs").size(), 10); + + EXPECT_EQ( + document.at("$defs").at("https://spec.openapis.org/oas/3.1/dialect/base"), + sourcemeta::core::schema_resolver( + "https://spec.openapis.org/oas/3.1/dialect/base") + .value()); + EXPECT_EQ( + document.at("$defs").at("https://spec.openapis.org/oas/3.1/meta/base"), + sourcemeta::core::schema_resolver( + "https://spec.openapis.org/oas/3.1/meta/base") + .value()); + EXPECT_EQ( + document.at("$defs").at("https://json-schema.org/draft/2020-12/schema"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/schema") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/core"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/core") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/applicator"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/applicator") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/unevaluated"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/unevaluated") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/validation"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/validation") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/meta-data"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/meta-data") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/format-annotation"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/format-annotation") + .value()); + EXPECT_EQ(document.at("$defs").at( + "https://json-schema.org/draft/2020-12/meta/content"), + sourcemeta::core::schema_resolver( + "https://json-schema.org/draft/2020-12/meta/content") + .value()); +} + TEST(JSONSchema_bundle_2020_12, hyperschema_smoke) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/test/jsonschema/jsonschema_resolver_test.cc b/test/jsonschema/jsonschema_resolver_test.cc index 0d00870b3..6fc242fa8 100644 --- a/test/jsonschema/jsonschema_resolver_test.cc +++ b/test/jsonschema/jsonschema_resolver_test.cc @@ -577,3 +577,162 @@ TEST(JSONSchema_resolver, is_known_schema_unknown) { "http://json-schema.org/draft-99/schema") .has_value()); } + +TEST(JSONSchema_resolver, is_official_schema_2020_12) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft/2020-12/schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/hyper-schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/applicator")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/core")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/format-annotation")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/format-assertion")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/hyper-schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/meta-data")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/unevaluated")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/meta/validation")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/links")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2020-12/output/schema")); +} + +TEST(JSONSchema_resolver, is_official_schema_2019_09) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft/2019-09/schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/hyper-schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/meta/applicator")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/meta/core")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/meta/format")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/meta/hyper-schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/meta/meta-data")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/meta/validation")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/links")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/output/schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft/2019-09/output/hyper-schema")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft7) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-07/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-07/schema")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "https://json-schema.org/draft-07/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-07/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-07/links#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-07/hyper-schema-output")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft6) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-06/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-06/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-06/links#")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft4) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-04/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-04/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-04/links#")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft3) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-03/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-03/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-03/links#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-03/json-ref#")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft2) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-02/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-02/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-02/links#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-02/json-ref#")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft1) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-01/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-01/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-01/links#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-01/json-ref#")); +} + +TEST(JSONSchema_resolver, is_official_schema_draft0) { + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-00/schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-00/hyper-schema#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-00/links#")); + EXPECT_TRUE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-00/json-ref#")); +} + +TEST(JSONSchema_resolver, is_official_schema_openapi_3_1) { + EXPECT_FALSE(sourcemeta::core::is_official_schema( + "https://spec.openapis.org/oas/3.1/dialect/base")); + EXPECT_FALSE(sourcemeta::core::is_official_schema( + "https://spec.openapis.org/oas/3.1/meta/base")); +} + +TEST(JSONSchema_resolver, is_official_schema_openapi_3_2) { + EXPECT_FALSE(sourcemeta::core::is_official_schema( + "https://spec.openapis.org/oas/3.2/dialect/2025-09-17")); + EXPECT_FALSE(sourcemeta::core::is_official_schema( + "https://spec.openapis.org/oas/3.2/meta/2025-09-17")); +} + +TEST(JSONSchema_resolver, is_official_schema_unknown) { + EXPECT_FALSE( + sourcemeta::core::is_official_schema("https://example.com/foobar")); + EXPECT_FALSE(sourcemeta::core::is_official_schema("")); + EXPECT_FALSE(sourcemeta::core::is_official_schema( + "http://json-schema.org/draft-99/schema")); +}