Skip to content
Draft
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/core/jsonschema/known_resolver.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ set(OPENAPI_3_1_DIR "${PROJECT_SOURCE_DIR}/vendor/openapi/oas/3.1")
file(READ "${OPENAPI_3_1_DIR}/meta/base" OPENAPI_OAS_3_1_META_BASE)
file(READ "${OPENAPI_3_1_DIR}/dialect/base" OPENAPI_OAS_3_1_DIALECT_BASE)

# OpenAPI v3.0
set(OPENAPI_3_0_DIR "${PROJECT_SOURCE_DIR}/vendor/openapi/oas/3.0")
file(READ "${OPENAPI_3_0_DIR}/schema/2024-10-18" OPENAPI_OAS_3_0_SCHEMA_2024_10_18)
file(READ "${OPENAPI_3_0_DIR}/schema/2021-09-28" OPENAPI_OAS_3_0_SCHEMA_2021_09_28)

if(NOT KNOWN_RESOLVER_INPUT)
message(FATAL_ERROR "Missing KNOWN_RESOLVER_INPUT")
endif()
Expand Down
46 changes: 46 additions & 0 deletions src/core/jsonschema/known_resolver.in.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ enum class KnownSchema : std::uint8_t {
OAS_3_1_DIALECT_BASE,
OAS_3_1_META_BASE,

OAS_3_0_DIALECT_2024_10_18,
OAS_3_0_DIALECT_2021_09_28,

UNKNOWN
};

Expand Down Expand Up @@ -419,6 +422,19 @@ static auto parse_identifier(const std::string_view identifier) -> KnownSchema {
return KnownSchema::OAS_3_1_META_BASE;
}

// OpenAPI v3.0
//
// The OpenAPI Initiative never assigned a dialect identifier to the v3.0
// Schema Object, unlike v3.1 and later, so we mint one per published release
// of the document schema that embeds it, as described in RFC 4151
if (identifier == "tag:spec.openapis.org,2024-10-18:oas/3.0/dialect") {
return KnownSchema::OAS_3_0_DIALECT_2024_10_18;
}

if (identifier == "tag:spec.openapis.org,2021-09-28:oas/3.0/dialect") {
return KnownSchema::OAS_3_0_DIALECT_2021_09_28;
}

return KnownSchema::UNKNOWN;
}

Expand Down Expand Up @@ -700,6 +716,34 @@ auto sourcemeta::core::schema_resolver(const std::string_view identifier)
sourcemeta::core::parse_json(R"EOF(@OPENAPI_OAS_3_1_META_BASE@)EOF")};
return SCHEMA;
}
// The document schema is embedded rather than registered on its own, so
// that the dialect identifier is the only thing this resolver exposes. Its
// inner identifier must be kept for the references it makes to itself to
// keep resolving against its own scope
case KnownSchema::OAS_3_0_DIALECT_2024_10_18: {
static const auto SCHEMA{sourcemeta::core::parse_json(R"EOF({
"id": "tag:spec.openapis.org,2024-10-18:oas/3.0/dialect",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "The OpenAPI v3.0.x Schema Object dialect",
"allOf": [ { "$ref": "#/definitions/openapi/definitions/Schema" } ],
"definitions": {
"openapi": @OPENAPI_OAS_3_0_SCHEMA_2024_10_18@
}
})EOF")};
return SCHEMA;
}
case KnownSchema::OAS_3_0_DIALECT_2021_09_28: {
static const auto SCHEMA{sourcemeta::core::parse_json(R"EOF({
"id": "tag:spec.openapis.org,2021-09-28:oas/3.0/dialect",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "The OpenAPI v3.0.x Schema Object dialect",
"allOf": [ { "$ref": "#/definitions/openapi/definitions/Schema" } ],
"definitions": {
"openapi": @OPENAPI_OAS_3_0_SCHEMA_2021_09_28@
}
})EOF")};
return SCHEMA;
}
case KnownSchema::UNKNOWN:
return std::nullopt;
}
Expand Down Expand Up @@ -771,6 +815,8 @@ auto sourcemeta::core::schema_is_official(
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::OAS_3_0_DIALECT_2024_10_18:
case KnownSchema::OAS_3_0_DIALECT_2021_09_28:
case KnownSchema::UNKNOWN:
return false;
}
Expand Down
47 changes: 47 additions & 0 deletions test/jsonschema/jsonschema_resolver_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,31 @@ TEST(openapi_3_2) {
EXPECT_SCHEMA("https://spec.openapis.org/oas/3.2/meta/2025-09-17");
}

TEST(openapi_3_0) {
EXPECT_SCHEMA("tag:spec.openapis.org,2024-10-18:oas/3.0/dialect");
EXPECT_SCHEMA("tag:spec.openapis.org,2021-09-28:oas/3.0/dialect");
}

TEST(openapi_3_0_document_schema_is_encapsulated) {
EXPECT_FALSE(sourcemeta::core::schema_resolver(
"https://spec.openapis.org/oas/3.0/schema/2024-10-18")
.has_value());
EXPECT_FALSE(sourcemeta::core::schema_resolver(
"https://spec.openapis.org/oas/3.0/schema/2021-09-28")
.has_value());
}

TEST(openapi_3_0_dialect_points_to_the_schema_object) {
const auto result{sourcemeta::core::schema_resolver(
"tag:spec.openapis.org,2024-10-18:oas/3.0/dialect")};
EXPECT_TRUE(result.has_value());
const sourcemeta::core::JSON &document{result.value()};
EXPECT_EQ(document.at("allOf").at(0).at("$ref").to_string(),
"#/definitions/openapi/definitions/Schema");
EXPECT_EQ(document.at("definitions").at("openapi").at("id").to_string(),
"https://spec.openapis.org/oas/3.0/schema/2024-10-18");
}

TEST(idempotency) {
EXPECT_SCHEMA("https://json-schema.org/draft/2020-12/schema");
EXPECT_SCHEMA("https://json-schema.org/draft/2020-12/schema");
Expand Down Expand Up @@ -582,6 +607,21 @@ TEST(is_known_schema_openapi_3_2) {
.has_value());
}

TEST(is_known_schema_openapi_3_0) {
EXPECT_TRUE(sourcemeta::core::schema_is_known(
"tag:spec.openapis.org,2024-10-18:oas/3.0/dialect"));
EXPECT_TRUE(sourcemeta::core::schema_resolver(
"tag:spec.openapis.org,2024-10-18:oas/3.0/dialect")
.has_value());
EXPECT_TRUE(sourcemeta::core::schema_is_known(
"tag:spec.openapis.org,2021-09-28:oas/3.0/dialect"));
EXPECT_TRUE(sourcemeta::core::schema_resolver(
"tag:spec.openapis.org,2021-09-28:oas/3.0/dialect")
.has_value());
EXPECT_FALSE(sourcemeta::core::schema_is_known(
"https://spec.openapis.org/oas/3.0/schema/2024-10-18"));
}

TEST(is_known_schema_unknown) {
EXPECT_FALSE(sourcemeta::core::schema_is_known("https://example.com/foobar"));
EXPECT_FALSE(sourcemeta::core::schema_resolver("https://example.com/foobar")
Expand Down Expand Up @@ -746,6 +786,13 @@ TEST(is_official_schema_openapi_3_2) {
"https://spec.openapis.org/oas/3.2/meta/2025-09-17"));
}

TEST(is_official_schema_openapi_3_0) {
EXPECT_FALSE(sourcemeta::core::schema_is_official(
"tag:spec.openapis.org,2024-10-18:oas/3.0/dialect"));
EXPECT_FALSE(sourcemeta::core::schema_is_official(
"tag:spec.openapis.org,2021-09-28:oas/3.0/dialect"));
}

TEST(is_official_schema_unknown) {
EXPECT_FALSE(
sourcemeta::core::schema_is_official("https://example.com/foobar"));
Expand Down
Loading