diff --git a/collections/self/v1/schemas/api/list/response.json b/collections/self/v1/schemas/api/list/response.json index d81f9599a..de43983ae 100644 --- a/collections/self/v1/schemas/api/list/response.json +++ b/collections/self/v1/schemas/api/list/response.json @@ -5,6 +5,7 @@ "examples": [ { "health": 100, + "schemas": 42, "entries": [], "path": "/schemas", "url": "https://example.com/schemas", @@ -17,7 +18,7 @@ } ], "type": "object", - "required": [ "health", "entries", "path", "url", "breadcrumb" ], + "required": [ "health", "schemas", "entries", "path", "url", "breadcrumb" ], "properties": { "title": { "type": "string" @@ -50,7 +51,7 @@ "anyOf": [ { "type": "object", - "required": [ "health", "name", "type", "path" ], + "required": [ "health", "schemas", "name", "type", "path" ], "properties": { "title": { "type": "string" @@ -72,6 +73,10 @@ "maximum": 100, "minimum": 0 }, + "schemas": { + "type": "integer", + "minimum": 0 + }, "name": { "type": "string" }, @@ -108,6 +113,10 @@ "maximum": 100, "minimum": 0 }, + "schemas": { + "type": "integer", + "minimum": 0 + }, "path": { "$ref": "https://schemas.sourcemeta.com/sourcemeta/std/v0/ietf/uri/uri-relative" }, diff --git a/docs/api.md b/docs/api.md index 78e45037c..00686ceab 100644 --- a/docs/api.md +++ b/docs/api.md @@ -73,11 +73,13 @@ navigation and discovery purposes. | `/email` | String | No | The e-mail address associated with the directory | | `/github` | String | No | The GitHub organisation or repository associated with the directory | | `/website` | String | No | The external URL associated with the directory | + | `/schemas` | Integer | Yes | The recursive count of schemas in this directory | | `/entries` | Array | Yes | The entries inside the directory | | `/entries/*/type` | String | Yes | The type of the entry (`schema` or `directory`) | | `/entries/*/name` | String | Yes | The last URL path segment of the entry | | `/entries/*/path` | String | Yes | The relative URL of the entry | | `/entries/*/health` | Integer | No | The aggregated health of the entry | + | `/entries/*/schemas` | Integer | No | For `directory` entries, the recursive count of schemas in the directory | | `/entries/*/title` | String | No | The title associated with the entry | | `/entries/*/description` | String | No | The description associated with the entry | | `/entries/*/email` | String | No | For `directory` entries, the e-mail address associated with the entry | diff --git a/enterprise/e2e/html/hurl/list.hurl b/enterprise/e2e/html/hurl/list.hurl index f9c3f6a4d..5a9c7348f 100644 --- a/enterprise/e2e/html/hurl/list.hurl +++ b/enterprise/e2e/html/hurl/list.hurl @@ -12,14 +12,17 @@ header "Last-Modified" exists jsonpath "$.path" == "/" jsonpath "$.url" == "{{base}}" jsonpath "$.breadcrumb" count == 0 +jsonpath "$.schemas" == 32 jsonpath "$.entries" count == 2 jsonpath "$.entries[0].name" == "self" jsonpath "$.entries[0].type" == "directory" jsonpath "$.entries[0].health" == 100 +jsonpath "$.entries[0].schemas" == 31 jsonpath "$.entries[0].path" == "/self/" jsonpath "$.entries[1].name" == "test" jsonpath "$.entries[1].type" == "directory" jsonpath "$.entries[1].health" == 67 +jsonpath "$.entries[1].schemas" == 1 jsonpath "$.entries[1].path" == "/test/" POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}} diff --git a/src/index/explorer.h b/src/index/explorer.h index 103abb03f..f75fc844a 100644 --- a/src/index/explorer.h +++ b/src/index/explorer.h @@ -122,6 +122,7 @@ struct MetapackExplorerSchemaExtension { struct MetapackDirectoryExtension { MetapackVersionInfo version; std::int64_t health; + std::int64_t schemas; std::uint16_t path_length; std::uint16_t title_length; std::uint16_t description_length; @@ -157,10 +158,10 @@ inline auto directory_extension_string(const MetapackDirectoryExtension *, static auto make_directory_extension( const MetapackVersionInfo &version, const std::int64_t health, - const std::string_view path, const std::string_view title, - const std::string_view description, const std::string_view email, - const std::string_view github, const std::string_view website) - -> std::vector { + const std::int64_t schemas, const std::string_view path, + const std::string_view title, const std::string_view description, + const std::string_view email, const std::string_view github, + const std::string_view website) -> std::vector { assert(path.size() <= std::numeric_limits::max()); assert(title.size() <= std::numeric_limits::max()); assert(description.size() <= std::numeric_limits::max()); @@ -176,6 +177,7 @@ static auto make_directory_extension( MetapackDirectoryExtension header{}; header.version = version; header.health = health; + header.schemas = schemas; header.path_length = static_cast(path.size()); header.title_length = static_cast(title.size()); header.description_length = static_cast(description.size()); @@ -573,6 +575,7 @@ struct GENERATE_EXPLORER_DIRECTORY_LIST { const auto timestamp_start{std::chrono::steady_clock::now()}; auto entries{sourcemeta::core::JSON::make_array()}; std::vector scores; + std::int64_t child_schemas_total{0}; const auto directory_path{action.destination.parent_path().parent_path()}; std::filesystem::path relative_path; @@ -630,6 +633,10 @@ struct GENERATE_EXPLORER_DIRECTORY_LIST { if (old_entry.defines("health")) { scores.emplace_back(old_entry.at("health").to_integer()); } + if (old_entry.defines("schemas")) { + assert(old_entry.at("schemas").is_positive()); + child_schemas_total += old_entry.at("schemas").to_integer(); + } directory_entries.push_back( {old_entry, parse_version_info(child_name), child_name}); continue; @@ -656,6 +663,10 @@ struct GENERATE_EXPLORER_DIRECTORY_LIST { entry_json.assign( "health", sourcemeta::core::JSON{directory_extension->health}); scores.emplace_back(directory_extension->health); + assert(directory_extension->schemas >= 0); + entry_json.assign( + "schemas", sourcemeta::core::JSON{directory_extension->schemas}); + child_schemas_total += directory_extension->schemas; const auto directory_path_string{ directory_extension_string(directory_extension, directory_base, 0, @@ -725,6 +736,11 @@ struct GENERATE_EXPLORER_DIRECTORY_LIST { scores.emplace_back(directory_json.at("health").to_integer()); entry_json.assign("health", directory_json.at("health")); + assert(directory_json.defines("schemas")); + assert(directory_json.at("schemas").is_integer()); + assert(directory_json.at("schemas").is_positive()); + entry_json.assign("schemas", directory_json.at("schemas")); + child_schemas_total += directory_json.at("schemas").to_integer(); assert(directory_json.defines("path")); entry_json.assign("path", sourcemeta::core::JSON{ @@ -880,6 +896,11 @@ struct GENERATE_EXPLORER_DIRECTORY_LIST { meta.assign("health", sourcemeta::core::JSON{0}); } + const auto total_schemas{static_cast(schema_entries.size()) + + child_schemas_total}; + assert(total_schemas >= 0); + meta.assign("schemas", sourcemeta::core::JSON{total_schemas}); + meta.assign("entries", std::move(entries)); if (relative_path == ".") { @@ -898,7 +919,7 @@ struct GENERATE_EXPLORER_DIRECTORY_LIST { action.destination.parent_path().parent_path().filename().string()}; const auto directory_extension_bytes{make_directory_extension( parse_version_info(directory_name), meta.at("health").to_integer(), - meta.at("path").to_string(), + meta.at("schemas").to_integer(), meta.at("path").to_string(), meta.defines("title") ? meta.at("title").to_string() : "", meta.defines("description") ? meta.at("description").to_string() : "", meta.defines("email") ? meta.at("email").to_string() : "", diff --git a/src/web/helpers.h b/src/web/helpers.h index 7d58c899a..4b780816e 100644 --- a/src/web/helpers.h +++ b/src/web/helpers.h @@ -254,6 +254,13 @@ inline auto make_file_manager_row(sourcemeta::core::HTMLWriter &writer, entry.defines("description") ? entry.at("description").to_string() : "-"); writer.close(); + // Schemas column + writer.td(); + writer.small(entry.defines("schemas") + ? std::to_string(entry.at("schemas").to_integer()) + : "-"); + writer.close(); + // Dependencies column writer.td(); writer.small(entry.defines("dependencies") @@ -287,6 +294,9 @@ inline auto make_file_manager_table_header(sourcemeta::core::HTMLWriter &writer) writer.text("Description"); writer.close(); writer.th().attribute("scope", "col"); + writer.text("Schemas"); + writer.close(); + writer.th().attribute("scope", "col"); writer.text("Dependencies"); writer.close(); writer.th().attribute("scope", "col").attribute("style", "width: 150px"); diff --git a/test/e2e/headless/hurl/list.hurl b/test/e2e/headless/hurl/list.hurl index 311338971..8cbbfc18c 100644 --- a/test/e2e/headless/hurl/list.hurl +++ b/test/e2e/headless/hurl/list.hurl @@ -12,17 +12,20 @@ header "Last-Modified" exists jsonpath "$.path" == "/" jsonpath "$.url" == "{{base}}" jsonpath "$.breadcrumb" count == 0 +jsonpath "$.schemas" == 45 jsonpath "$.entries" count >= 2 jsonpath "$.entries[0].name" == "self" jsonpath "$.entries[0].title" == "Self" jsonpath "$.entries[0].description" == "The schemas that define the current version of this instance" jsonpath "$.entries[0].type" == "directory" jsonpath "$.entries[0].health" == 100 +jsonpath "$.entries[0].schemas" == 31 jsonpath "$.entries[0].path" == "/self/" jsonpath "$.entries[1].name" == "test" jsonpath "$.entries[1].title" == "Test" jsonpath "$.entries[1].description" == "A directory full of testing schemas" jsonpath "$.entries[1].type" == "directory" +jsonpath "$.entries[1].schemas" == 14 jsonpath "$.entries[1].path" == "/test/" POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}} @@ -52,6 +55,7 @@ jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/" jsonpath "$.description" == "A directory full of testing schemas" jsonpath "$.title" == "Test" +jsonpath "$.schemas" == 14 jsonpath "$.entries" count == 3 POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}} diff --git a/test/e2e/html/hurl/list.hurl b/test/e2e/html/hurl/list.hurl index b42938368..32f2f0dce 100644 --- a/test/e2e/html/hurl/list.hurl +++ b/test/e2e/html/hurl/list.hurl @@ -12,18 +12,21 @@ header "Last-Modified" exists jsonpath "$.path" == "/" jsonpath "$.url" == "{{base}}" jsonpath "$.breadcrumb" count == 0 +jsonpath "$.schemas" == 71 jsonpath "$.entries" count >= 2 jsonpath "$.entries[0].name" == "self" jsonpath "$.entries[0].title" == "Self" jsonpath "$.entries[0].description" == "The schemas that define the current version of this instance" jsonpath "$.entries[0].type" == "directory" jsonpath "$.entries[0].health" == 100 +jsonpath "$.entries[0].schemas" == 31 jsonpath "$.entries[0].path" == "/self/" jsonpath "$.entries[1].name" == "test" jsonpath "$.entries[1].title" == "Test" jsonpath "$.entries[1].description" == "A directory full of testing schemas" jsonpath "$.entries[1].type" == "directory" jsonpath "$.entries[1].health" == 14 +jsonpath "$.entries[1].schemas" == 40 jsonpath "$.entries[1].path" == "/test/" POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}} @@ -53,22 +56,27 @@ jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/" jsonpath "$.description" == "A directory full of testing schemas" jsonpath "$.title" == "Test" +jsonpath "$.schemas" == 40 jsonpath "$.entries" count == 10 jsonpath "$.entries[0].name" == "v2.0" jsonpath "$.entries[0].type" == "directory" jsonpath "$.entries[0].health" == 0 +jsonpath "$.entries[0].schemas" == 1 jsonpath "$.entries[0].path" == "/test/v2.0/" jsonpath "$.entries[1].name" == "bundling" jsonpath "$.entries[1].type" == "directory" jsonpath "$.entries[1].health" == 59 +jsonpath "$.entries[1].schemas" == 2 jsonpath "$.entries[1].path" == "/test/bundling/" jsonpath "$.entries[2].name" == "camelcase" jsonpath "$.entries[2].type" == "directory" jsonpath "$.entries[2].health" == 25 +jsonpath "$.entries[2].schemas" == 2 jsonpath "$.entries[2].path" == "/test/camelcase/" jsonpath "$.entries[3].name" == "doc" jsonpath "$.entries[3].type" == "directory" jsonpath "$.entries[3].health" == 0 +jsonpath "$.entries[3].schemas" == 2 jsonpath "$.entries[3].path" == "/test/doc/" jsonpath "$.entries[3].title" == "A sample schema folder" jsonpath "$.entries[3].description" == "For testing purposes" @@ -76,26 +84,32 @@ jsonpath "$.entries[3].github" == "sourcemeta/one" jsonpath "$.entries[4].name" == "extension" jsonpath "$.entries[4].type" == "directory" jsonpath "$.entries[4].health" == 17 +jsonpath "$.entries[4].schemas" == 3 jsonpath "$.entries[4].path" == "/test/extension/" jsonpath "$.entries[5].name" == "hyper" jsonpath "$.entries[5].type" == "directory" jsonpath "$.entries[5].health" == 34 +jsonpath "$.entries[5].schemas" == 1 jsonpath "$.entries[5].path" == "/test/hyper/" jsonpath "$.entries[6].name" == "no-base" jsonpath "$.entries[6].type" == "directory" jsonpath "$.entries[6].health" == 0 +jsonpath "$.entries[6].schemas" == 2 jsonpath "$.entries[6].path" == "/test/no-base/" jsonpath "$.entries[7].name" == "no-blaze" jsonpath "$.entries[7].type" == "directory" jsonpath "$.entries[7].health" == 0 +jsonpath "$.entries[7].schemas" == 1 jsonpath "$.entries[7].path" == "/test/no-blaze/" jsonpath "$.entries[8].name" == "same" jsonpath "$.entries[8].type" == "directory" jsonpath "$.entries[8].health" == 0 +jsonpath "$.entries[8].schemas" == 1 jsonpath "$.entries[8].path" == "/test/same/" jsonpath "$.entries[9].name" == "schemas" jsonpath "$.entries[9].type" == "directory" jsonpath "$.entries[9].health" == 7 +jsonpath "$.entries[9].schemas" == 25 jsonpath "$.entries[9].path" == "/test/schemas/" POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}} @@ -120,6 +134,7 @@ header "ETag" exists header "Last-Modified" exists jsonpath "$.path" == "/test/v2.0" jsonpath "$.url" == "{{base}}/test/v2.0" +jsonpath "$.schemas" == 1 jsonpath "$.breadcrumb" count == 2 jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/" @@ -183,6 +198,7 @@ header "Last-Modified" exists jsonpath "$.path" == "/test/schemas/versions" jsonpath "$.url" == "{{base}}/test/schemas/versions" jsonpath "$.health" == 0 +jsonpath "$.schemas" == 1 jsonpath "$.breadcrumb" count == 3 jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/" @@ -226,6 +242,7 @@ header "Last-Modified" exists jsonpath "$.path" == "/test/schemas/clash/foo" jsonpath "$.url" == "{{base}}/test/schemas/clash/foo" jsonpath "$.health" == 0 +jsonpath "$.schemas" == 1 jsonpath "$.breadcrumb" count == 4 jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/" @@ -271,6 +288,7 @@ header "Last-Modified" exists jsonpath "$.path" == "/test/schemas/clash" jsonpath "$.url" == "{{base}}/test/schemas/clash" jsonpath "$.health" == 0 +jsonpath "$.schemas" == 2 jsonpath "$.breadcrumb" count == 3 jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/" @@ -282,6 +300,7 @@ jsonpath "$.entries" count == 2 jsonpath "$.entries[0].name" == "foo" jsonpath "$.entries[0].type" == "directory" jsonpath "$.entries[0].health" == 0 +jsonpath "$.entries[0].schemas" == 1 jsonpath "$.entries[0].path" == "/test/schemas/clash/foo/" jsonpath "$.entries[1].name" == "foo" jsonpath "$.entries[1].type" == "schema" @@ -318,6 +337,7 @@ header "Last-Modified" exists jsonpath "$.path" == "/test/extension" jsonpath "$.url" == "{{base}}/test/extension" jsonpath "$.health" == 17 +jsonpath "$.schemas" == 3 jsonpath "$.breadcrumb" count == 2 jsonpath "$.breadcrumb[0].name" == "test" jsonpath "$.breadcrumb[0].path" == "/test/"