diff --git a/DEPENDENCIES b/DEPENDENCIES index 8f324d8..83b97f2 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,6 +1,6 @@ vendorpull https://github.com/sourcemeta/vendorpull dea311b5bfb53b6926a4140267959ae334d3ecf4 noa https://github.com/sourcemeta/noa 924f5cc8549af7f12227869dcbab4259029ac650 -jsontoolkit https://github.com/sourcemeta/jsontoolkit 5ad87d03d7c1dde6e66a047e8e76d8291114627a +jsontoolkit https://github.com/sourcemeta/jsontoolkit d1843916a7a3203285365e3ee393552385ffce3c blaze https://github.com/sourcemeta/blaze a906d6601bc5d7afd4ddb6cbf9f64326aeee1b0c hydra https://github.com/sourcemeta/hydra c31b5b612beb3e6a870deb2581ae437fc5183f47 bootstrap https://github.com/twbs/bootstrap v5.3.3 diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index ad3dea7..89ad4b4 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -6,6 +6,7 @@ set_target_properties(schema_registry_index PROPERTIES OUTPUT_NAME sourcemeta-re target_link_libraries(schema_registry_index PRIVATE sourcemeta::jsontoolkit::uri) target_link_libraries(schema_registry_index PRIVATE sourcemeta::jsontoolkit::json) target_link_libraries(schema_registry_index PRIVATE sourcemeta::jsontoolkit::jsonschema) +target_link_libraries(schema_registry_index PRIVATE sourcemeta::jsontoolkit::yaml) target_link_libraries(schema_registry_index PRIVATE sourcemeta::blaze::compiler) target_link_libraries(schema_registry_index PRIVATE sourcemeta::blaze::evaluator) diff --git a/src/index/index.cc b/src/index/index.cc index cc0fa18..c40d2e0 100644 --- a/src/index/index.cc +++ b/src/index/index.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,20 @@ static auto write_lower_except_trailing(T &stream, const std::string &input, } } +static auto is_yaml(const std::filesystem::path &path) -> bool { + return path.extension() == ".yaml" || path.extension() == ".yml"; +} + +static auto is_schema_file(const std::filesystem::path &path) -> bool { + return is_yaml(path) || path.extension() == ".json"; +} + +static auto schema_reader(const std::filesystem::path &path) + -> sourcemeta::jsontoolkit::JSON { + return is_yaml(path) ? sourcemeta::jsontoolkit::from_yaml(path) + : sourcemeta::jsontoolkit::from_file(path); +} + static auto url_join(const std::string &first, const std::string &second, const std::string &third, const std::string &extension) -> std::string { @@ -112,7 +127,7 @@ static auto index(sourcemeta::jsontoolkit::FlatFileSchemaResolver &resolver, for (const auto &entry : std::filesystem::recursive_directory_iterator{collection_path}) { - if (!entry.is_regular_file() || entry.path().extension() != ".json" || + if (!entry.is_regular_file() || !is_schema_file(entry.path()) || entry.path().stem().string().starts_with(".")) { continue; } @@ -133,9 +148,9 @@ static auto index(sourcemeta::jsontoolkit::FlatFileSchemaResolver &resolver, default_identifier << relative_path; - const auto ¤t_identifier{resolver.add( - entry.path(), sourcemeta::jsontoolkit::from_file(entry.path()), - default_dialect, default_identifier.str())}; + const auto ¤t_identifier{resolver.add(entry.path(), default_dialect, + default_identifier.str(), + schema_reader)}; auto identifier_uri{ sourcemeta::jsontoolkit::URI{current_identifier}.canonicalize()}; std::cerr << identifier_uri.recompose(); diff --git a/test/e2e/common/yaml.hurl b/test/e2e/common/yaml.hurl new file mode 100644 index 0000000..27dfe44 --- /dev/null +++ b/test/e2e/common/yaml.hurl @@ -0,0 +1,37 @@ +GET {{base}}/example/schemas/yaml.yaml +HTTP 404 + +GET {{base}}/example/schemas/yaml.yaml.json +HTTP 200 +Content-Type: application/schema+json +Link: ; rel="describedby" +[Asserts] +header "ETag" exists +header "X-Request-id" exists + +GET {{base}}/example/schemas/yaml.yaml.json +HTTP 200 +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "{{base}}/example/schemas/yaml.yaml.json", + "type": "string" +} + +GET {{base}}/example/schemas/yaml.yml +HTTP 404 + +GET {{base}}/example/schemas/yml.yml.json +HTTP 200 +Content-Type: application/schema+json +Link: ; rel="describedby" +[Asserts] +header "ETag" exists +header "X-Request-id" exists + +GET {{base}}/example/schemas/yml.yml.json +HTTP 200 +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "{{base}}/example/schemas/yml.yml.json", + "type": "string" +} diff --git a/test/sandbox/manifest-ce.txt b/test/sandbox/manifest-ce.txt index 267c900..978cfb9 100644 --- a/test/sandbox/manifest-ce.txt +++ b/test/sandbox/manifest-ce.txt @@ -16,6 +16,8 @@ ./bundles/example/schemas/no-schema-nor-id.json ./bundles/example/schemas/no-schema.json ./bundles/example/schemas/string.json +./bundles/example/schemas/yaml.yaml.json +./bundles/example/schemas/yml.yml.json ./bundles/example/v2.0 ./bundles/example/v2.0/schema.json ./bundles/hyper @@ -39,6 +41,8 @@ ./schemas/example/schemas/no-schema-nor-id.json ./schemas/example/schemas/no-schema.json ./schemas/example/schemas/string.json +./schemas/example/schemas/yaml.yaml.json +./schemas/example/schemas/yml.yml.json ./schemas/example/v2.0 ./schemas/example/v2.0/schema.json ./schemas/hyper @@ -61,6 +65,8 @@ ./unidentified/example/schemas/no-schema-nor-id.json ./unidentified/example/schemas/no-schema.json ./unidentified/example/schemas/string.json +./unidentified/example/schemas/yaml.yaml.json +./unidentified/example/schemas/yml.yml.json ./unidentified/example/v2.0 ./unidentified/example/v2.0/schema.json ./unidentified/hyper diff --git a/test/sandbox/manifest-ee.txt b/test/sandbox/manifest-ee.txt index c13163e..6a676a9 100644 --- a/test/sandbox/manifest-ee.txt +++ b/test/sandbox/manifest-ee.txt @@ -16,6 +16,8 @@ ./bundles/example/schemas/no-schema-nor-id.json ./bundles/example/schemas/no-schema.json ./bundles/example/schemas/string.json +./bundles/example/schemas/yaml.yaml.json +./bundles/example/schemas/yml.yml.json ./bundles/example/v2.0 ./bundles/example/v2.0/schema.json ./bundles/hyper @@ -65,6 +67,8 @@ ./schemas/example/schemas/no-schema-nor-id.json ./schemas/example/schemas/no-schema.json ./schemas/example/schemas/string.json +./schemas/example/schemas/yaml.yaml.json +./schemas/example/schemas/yml.yml.json ./schemas/example/v2.0 ./schemas/example/v2.0/schema.json ./schemas/hyper @@ -87,6 +91,8 @@ ./unidentified/example/schemas/no-schema-nor-id.json ./unidentified/example/schemas/no-schema.json ./unidentified/example/schemas/string.json +./unidentified/example/schemas/yaml.yaml.json +./unidentified/example/schemas/yml.yml.json ./unidentified/example/v2.0 ./unidentified/example/v2.0/schema.json ./unidentified/hyper diff --git a/test/sandbox/schemas/example/folder/yaml.yaml b/test/sandbox/schemas/example/folder/yaml.yaml new file mode 100644 index 0000000..9dfc65f --- /dev/null +++ b/test/sandbox/schemas/example/folder/yaml.yaml @@ -0,0 +1,3 @@ +$schema: 'http://json-schema.org/draft-07/schema#' +$id: https://example.com/schemas/yaml.yaml +type: string diff --git a/test/sandbox/schemas/example/folder/yml.yml b/test/sandbox/schemas/example/folder/yml.yml new file mode 100644 index 0000000..a4a6e78 --- /dev/null +++ b/test/sandbox/schemas/example/folder/yml.yml @@ -0,0 +1,3 @@ +$schema: 'http://json-schema.org/draft-07/schema#' +$id: https://example.com/schemas/yml.yml +type: string diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h index d881b77..726aa0b 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h @@ -121,18 +121,16 @@ class SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT FlatFileSchemaResolver { /// Construct an empty resolver that has another schema resolver as a fallback FlatFileSchemaResolver(const SchemaResolver &resolver); + /// Determines how to access the registered file entry, letting you hook + /// into how schemas are read to support other file formats, like YAML + using Reader = std::function; + /// Register a schema to the flat file resolver, returning the detected /// identifier for the schema - auto add(const std::filesystem::path &path, const JSON &schema, - const std::optional &default_dialect = std::nullopt, - const std::optional &default_id = std::nullopt) - -> const std::string &; - - /// Read and register a schema to the flat file resolver, returning the - /// detected identifier for the schema auto add(const std::filesystem::path &path, const std::optional &default_dialect = std::nullopt, - const std::optional &default_id = std::nullopt) + const std::optional &default_id = std::nullopt, + const Reader &reader = sourcemeta::jsontoolkit::from_file) -> const std::string &; // Change the identifier of a registered schema @@ -153,6 +151,7 @@ class SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT FlatFileSchemaResolver { std::filesystem::path path; std::optional default_dialect; std::string original_identifier; + Reader reader; }; private: diff --git a/vendor/jsontoolkit/src/jsonschema/resolver.cc b/vendor/jsontoolkit/src/jsonschema/resolver.cc index 55af471..6887573 100644 --- a/vendor/jsontoolkit/src/jsonschema/resolver.cc +++ b/vendor/jsontoolkit/src/jsonschema/resolver.cc @@ -91,10 +91,12 @@ static auto to_lowercase(const std::string_view input) -> std::string { } auto FlatFileSchemaResolver::add( - const std::filesystem::path &path, const JSON &schema, + const std::filesystem::path &path, const std::optional &default_dialect, - const std::optional &default_id) -> const std::string & { - const auto canonical{std::filesystem::canonical(path)}; + const std::optional &default_id, const Reader &reader) + -> const std::string & { + const auto canonical{std::filesystem::weakly_canonical(path)}; + const auto schema{reader(canonical)}; assert(sourcemeta::jsontoolkit::is_schema(schema)); const auto identifier{sourcemeta::jsontoolkit::identify( schema, *this, IdentificationStrategy::Loose, default_dialect, @@ -111,7 +113,7 @@ auto FlatFileSchemaResolver::add( const auto result{this->schemas.emplace( effective_identifier, - Entry{canonical, default_dialect, effective_identifier})}; + Entry{canonical, default_dialect, effective_identifier, reader})}; if (!result.second && result.first->second.path != canonical) { std::ostringstream error; error << "Cannot register the same identifier twice: " @@ -122,14 +124,6 @@ auto FlatFileSchemaResolver::add( return result.first->first; } -auto FlatFileSchemaResolver::add( - const std::filesystem::path &path, - const std::optional &default_dialect, - const std::optional &default_id) -> const std::string & { - return this->add(path, sourcemeta::jsontoolkit::from_file(path), - default_dialect, default_id); -} - auto FlatFileSchemaResolver::reidentify(const std::string &schema, const std::string &new_identifier) -> void { @@ -145,7 +139,7 @@ auto FlatFileSchemaResolver::operator()(std::string_view identifier) const const std::string string_identifier{to_lowercase(identifier)}; const auto result{this->schemas.find(string_identifier)}; if (result != this->schemas.cend()) { - auto schema{sourcemeta::jsontoolkit::from_file(result->second.path)}; + auto schema{result->second.reader(result->second.path)}; assert(sourcemeta::jsontoolkit::is_schema(schema)); if (schema.is_object() && !schema.defines("$schema") && result->second.default_dialect.has_value()) {