From 39befc68de2c0d7ffaa6e6dacfaaa0966b7bb097 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 3 Sep 2025 11:09:14 -0300 Subject: [PATCH 01/24] Introduce products.yml as the central resource to catalogue primary product metadata, and substitution support for them --- config/products.yml | 187 ++++++++++++++++++ config/versions.yml | 47 ++++- docs-builder.sln | 1 + docs/syntax/substitutions.md | 29 ++- .../Builder/ConfigurationFile.cs | 15 +- .../Builder/Products.cs | 51 ----- .../ConfigurationFileProvider.cs | 3 + ...Elastic.Documentation.Configuration.csproj | 1 + .../Serialization/YamlStaticContext.cs | 2 + .../Versions/VersionConfiguration.cs | 61 +++++- .../VersionsConfigurationExtensions.cs | 28 ++- .../AppliesTo/ApplicableToYamlConverter.cs | 9 +- .../Exporters/LlmMarkdownExporter.cs | 10 +- src/Elastic.Markdown/HtmlWriter.cs | 5 +- src/Elastic.Markdown/IO/MarkdownFile.cs | 2 +- .../CodeBlocks/EnhancedCodeBlockParser.cs | 2 +- .../Myst/Directives/DirectiveHtmlRenderer.cs | 2 +- .../Myst/FrontMatter/FrontMatterParser.cs | 2 +- .../Myst/FrontMatter/Products.cs | 23 +-- .../Myst/Roles/AppliesTo/AppliesToRole.cs | 4 +- .../Myst/YamlSerialization.cs | 8 +- .../Exporters/ConfigurationExporter.cs | 4 + .../Elastic.ApiExplorer.Tests/TestHelpers.cs | 11 ++ tests/Elastic.Markdown.Tests/TestHelpers.cs | 27 +++ tests/authoring/Framework/Setup.fs | 9 +- .../AssemblerConfigurationTests.cs | 1 + .../src/docs-assembler.Tests/TestHelpers.cs | 11 ++ 27 files changed, 449 insertions(+), 106 deletions(-) create mode 100644 config/products.yml delete mode 100644 src/Elastic.Documentation.Configuration/Builder/Products.cs diff --git a/config/products.yml b/config/products.yml new file mode 100644 index 000000000..6f51d7969 --- /dev/null +++ b/config/products.yml @@ -0,0 +1,187 @@ +products: + apm: + display: 'APM' + versioning: 'stack' + apm-agent: + display: 'APM Agent' + versioning: 'stack' + apm-agent-android: + display: 'APM Agent for Android' + versioning: 'apm_agent_android' + apm-agent-dotnet: + display: 'APM Agent for .NET' + versioning: 'apm_agent_dotnet' + apm-agent-go: + display: 'APM Agent for Go' + versioning: 'apm_agent_go' + apm-agent-java: + display: 'APM Agent for Java' + versioning: 'apm_agent_java' + apm-agent-node: + display: 'APM Agent for Node' + versioning: 'apm_agent_node' + apm-agent-php: + display: 'APM Agent for PHP' + versioning: 'apm_agent_php' + apm-agent-python: + display: 'APM Agent for Python' + versioning: 'apm_agent_python' + apm-agent-ruby: + display: 'APM Agent for Ruby' + versioning: 'apm_agent_ruby' + apm-agent-rum-js: + display: 'APM RUM JavaScript agent' + versioning: 'apm_agent_rum' + auditbeat: + display: 'Auditbeat' + versioning: 'stack' + beats: + display: 'Beats' + versioning: 'stack' + cloud-control-ecctl: + display: 'Elastic Cloud Control ECCTL' + versioning: 'stack' + cloud-enterprise: + display: 'Elastic Cloud Enterprise' + versioning: 'stack' + cloud-hosted: + display: 'Elastic Cloud Hosted' + versioning: 'stack' + cloud-kubernetes: + display: 'Elastic Cloud Kubernetes' + versioning: 'stack' + cloud-serverless: + display: 'Elastic Cloud Serverless' + versioning: 'stack' + cloud-terraform: + display: 'Elastic Cloud Terraform' + versioning: 'stack' + curator: + display: 'Elasticsearch Curator' + versioning: 'curator' + ecctl: + display: 'Elastic Cloud Control ECCTL' + versioning: 'ecctl' + ece: + display: 'Elastic Cloud Enterprise' + versioning: 'ece' + ech: + display: 'Elastic Cloud Hosted' + versioning: 'all' + eck: + display: 'Elastic Cloud on Kubernetes' + versioning: 'eck' + ecs: + display: 'Elastic Common Schema (ECS)' + versioning: 'stack' + ecs-logging: + display: 'ECS Logging' + versioning: 'stack' + edot-cf: + display: 'EDOT Cloud Forwarder' + versioning: 'stack' + edot-sdk: + display: 'Elastic Distribution of OpenTelemetry SDK' + versioning: 'stack' + edot-collector: + display: 'Elastic Distribution of OpenTelemetry Collector' + versioning: 'stack' + edot-ios: + display: 'Elastic Distribution of OpenTelemetry iOS' + versioning: 'edot_ios' + edot-android: + display: 'Elastic Distribution of OpenTelemetry Android' + versioning: 'edot_android' + edot-dotnet: + display: 'Elastic Distribution of OpenTelemetry .NET' + versioning: 'edot_dotnet' + edot-java: + display: 'Elastic Distribution of OpenTelemetry Java' + versioning: 'edot_java' + edot-node: + display: 'Elastic Distribution of OpenTelemetry Node' + versioning: 'edot_node' + edot-php: + display: 'Elastic Distribution of OpenTelemetry PHP' + versioning: 'edot_php' + edot-python: + display: 'Elastic Distribution of OpenTelemetry Python' + versioning: 'edot_python' + edot-cf-aws: + display: 'EDOT Cloud Forwarder for AWS' + versioning: 'edot_cf_aws' + elastic-agent: + display: 'Elastic Agent' + versioning: 'stack' + elastic-serverless-forwarder: + display: 'Elastic Serverless Forwarder' + versioning: 'stack' + elastic-stack: + display: 'Elastic Stack' + versioning: 'stack' + elasticsearch: + display: 'Elasticsearch' + versioning: 'stack' + elasticsearch-client: + display: 'Elasticsearch Client' + versioning: 'stack' + ess: + display: 'Elasticsearch Service' + versioning: 'all' + filebeat: + display: 'Filebeat' + versioning: 'stack' + fleet: + display: 'Fleet' + versioning: 'stack' + heartbeat: + display: 'Heartbeat' + versioning: 'stack' + integrations: + display: 'Integrations' + versioning: 'stack' + kibana: + display: 'Kibana' + versioning: 'stack' + logstash: + display: 'Logstash' + versioning: 'stack' + machine-learning: + display: 'Machine Learning' + versioning: 'stack' + metricbeat: + display: 'Metricbeat' + versioning: 'stack' + observability: + display: 'Elastic Observability' + versioning: 'stack' + packetbeat: + display: 'Packetbeat' + versioning: 'stack' + painless: + display: 'Elasticsearch Painless scripting language' + versioning: 'stack' + search-ui: + display: 'Search UI' + versioning: 'stack' + security: + display: 'Elastic Security' + versioning: 'stack' + self: + display: 'Elastic Stack' + versioning: 'stack' + serverless: + display: 'Elastic Serverless' + versioning: 'all' + serverless-elasticsearch: + display: 'Elasticsearch' + versioning: 'all' + serverless-observability: + display: 'Elastic Observability' + versioning: 'all' + serverless-security: + display: 'Elastic Security' + versioning: 'all' + winlogbeat: + display: 'Winlogbeat' + versioning: 'stack' \ No newline at end of file diff --git a/config/versions.yml b/config/versions.yml index 7fd29b36f..4093de247 100644 --- a/config/versions.yml +++ b/config/versions.yml @@ -36,9 +36,9 @@ versioning_systems: security: *all # APM agents - # apm_agent_android: - # base: 1.0 - # current: 1.0.0 + apm_agent_android: + base: 1.0 + current: 1.2.0 apm_agent_dotnet: base: 1.0 current: 1.33.0 @@ -63,6 +63,12 @@ versioning_systems: apm_agent_rum: base: 5.0 current: 5.17.0 + apm_attacher: + base: 1.0 + current: 1.1.3 + apm_lambda: + base: 1.0 + current: 1.5.8 # EDOTs edot_collector: @@ -92,3 +98,38 @@ versioning_systems: edot_cf_aws: base: 0.1 current: 0.1.6 + + # Logging + ecs_logging_dotnet: + base: 8.0 + current: 8.18.1 + ecs_logging_go_logrus: + base: 1.0 + current: 1.0.0 + ecs_logging_go_zap: + base: 1.0 + current: 1.0.3 + ecs_logging_go_zerolog: + base: 0.2 + current: 0.2.0 + ecs_logging_java: + base: 1.0 + current: 1.7.0 + ecs_logging_nodejs: + base: 1.0 + current: 1.5.3 + ecs_logging_php: + base: 2.0 + current: 2.0.0 + ecs_logging_python: + base: 2.0 + current: 2.2.0 + ecs_logging_ruby: + base: 1.0 + current: 1.0.0 + esf: + base: 1.0 + current: 1.20.1 + search_ui: + base: 1.0 + current: 1.24.0 \ No newline at end of file diff --git a/docs-builder.sln b/docs-builder.sln index 7af8a0bd8..0824dc491 100644 --- a/docs-builder.sln +++ b/docs-builder.sln @@ -121,6 +121,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{6FAB56 config\assembler.yml = config\assembler.yml config\legacy-url-mappings.yml = config\legacy-url-mappings.yml config\navigation.yml = config\navigation.yml + config\products.yml = config\products.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests-integration", "tests-integration", "{BCAD38D5-6C83-46E2-8398-4BE463931098}" diff --git a/docs/syntax/substitutions.md b/docs/syntax/substitutions.md index 3e7624e69..b3661e876 100644 --- a/docs/syntax/substitutions.md +++ b/docs/syntax/substitutions.md @@ -36,11 +36,13 @@ To use the variables in your files, surround them in curly brackets (`{{variable Here are some variable substitutions: -| Variable | Defined in | -|-----------------------|--------------| -| {{frontmatter_key}} | Front Matter | -| {{a-key-with-dashes}} | Front Matter | -| {{a-global-variable}} | `docset.yml` | +| Variable | Defined in | +|-----------------------|----------------| +| {{frontmatter_key}} | Front Matter | +| {{a-key-with-dashes}} | Front Matter | +| {{a-global-variable}} | `docset.yml` | +| {{product.kibana}} | `products.yml` | +| {{.kibana}} | `products.yml` | ## Mutations @@ -217,3 +219,20 @@ With mutations: {subs=true}`version {{version.stack | M.M}}` :::{note} Regular inline code (without the `{subs}` role) will not process substitutions and will display the variable placeholders as-is. ::: + +## Products + +Product substitutions use `products.yml` to determine what will be displayed. Use the product's key to get its display name. + +```yaml +# products.yml + +id: + display: {value} + ... +# example: + apm-agent-dotnet: + display: 'APM Agent for .NET' +``` + +A shorthand format is also available. Using `{{.id}}` is equivalent to `{{product.id}}`. diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index 3529782a1..dd8e21b7f 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -36,7 +36,7 @@ public record ConfigurationFile : ITableOfContentsScope public Dictionary? Redirects { get; } - public HashSet Products { get; } = new(StringComparer.Ordinal); + public HashSet Products { get; } = new(new ProductEqualityComparer()); public HashSet ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase); @@ -133,6 +133,7 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration foreach (var node in sequence.Children.OfType()) { YamlScalarNode? productId = null; + foreach (var child in node.Children) { if (child is { Key: YamlScalarNode { Value: "id" }, Value: YamlScalarNode scalarNode }) @@ -147,10 +148,10 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration break; } - if (!Builder.Products.AllById.ContainsKey(productId.Value)) - reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(Builder.Products.All.Select(p => p.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); + if (!Product.AllById(versionsConfig).ContainsKey(productId.Value)) + reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(Product.All(versionsConfig).Select(p => p.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); else - _ = Products.Add(productId.Value); + _ = Products.Add(versionsConfig.Products[productId.Value]); } break; case "features": @@ -175,6 +176,12 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration _substitutions[key] = system.Base; } + foreach (var (id, product) in versionsConfig.Products) + { + _substitutions[$"product.{id}"] = product.DisplayName; + _substitutions[$".{id}"] = product.DisplayName; + } + var toc = new TableOfContentsConfiguration(this, sourceFile, ScopeDirectory, _context, 0, ""); TableOfContents = toc.TableOfContents; Files = toc.Files; diff --git a/src/Elastic.Documentation.Configuration/Builder/Products.cs b/src/Elastic.Documentation.Configuration/Builder/Products.cs deleted file mode 100644 index 86208364c..000000000 --- a/src/Elastic.Documentation.Configuration/Builder/Products.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System.Collections.Frozen; - -namespace Elastic.Documentation.Configuration.Builder; - -public record Product(string Id, string DisplayName); - -public static class Products -{ - public static FrozenSet All { get; } = [ - new("apm", "APM"), - new("apm-agent", "APM Agent"), - new("auditbeat", "Auditbeat"), - new("beats", "Beats"), - new("cloud-control-ecctl", "Elastic Cloud Control ECCTL"), - new("cloud-enterprise", "Elastic Cloud Enterprise"), - new("cloud-hosted", "Elastic Cloud Hosted"), - new("cloud-kubernetes", "Elastic Cloud Kubernetes"), - new("cloud-serverless", "Elastic Cloud Serverless"), - new("cloud-terraform", "Elastic Cloud Terraform"), - new("ecs", "Elastic Common Schema (ECS)"), - new("ecs-logging", "ECS Logging"), - new("edot-cf", "EDOT Cloud Forwarder"), - new("edot-sdk", "Elastic Distribution of OpenTelemetry SDK"), - new("edot-collector", "Elastic Distribution of OpenTelemetry Collector"), - new("elastic-agent", "Elastic Agent"), - new("elastic-serverless-forwarder", "Elastic Serverless Forwarder"), - new("elastic-stack", "Elastic Stack"), - new("elasticsearch", "Elasticsearch"), - new("elasticsearch-client", "Elasticsearch Client"), - new("filebeat", "Filebeat"), - new("fleet", "Fleet"), - new("heartbeat", "Heartbeat"), - new("integrations", "Integrations"), - new("kibana", "Kibana"), - new("logstash", "Logstash"), - new("machine-learning", "Machine Learning"), - new("metricbeat", "Metricbeat"), - new("observability", "Elastic Observability"), - new("packetbeat", "Packetbeat"), - new("painless", "Elasticsearch Painless scripting language"), - new("search-ui", "Search UI"), - new("security", "Elastic Security"), - new("winlogbeat", "Winlogbeat"), - ]; - - public static FrozenDictionary AllById { get; } = All.ToDictionary(p => p.Id, StringComparer.Ordinal).ToFrozenDictionary(); -} diff --git a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs index 0b94a0784..16d0e0caa 100644 --- a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs +++ b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs @@ -34,6 +34,7 @@ public ConfigurationFileProvider(IFileSystem fileSystem, bool skipPrivateReposit TemporaryDirectory = fileSystem.Directory.CreateTempSubdirectory("docs-builder-config"); VersionFile = CreateTemporaryConfigurationFile("versions.yml"); + ProductsFile = CreateTemporaryConfigurationFile("products.yml"); AssemblerFile = CreateTemporaryConfigurationFile("assembler.yml"); NavigationFile = CreateTemporaryConfigurationFile("navigation.yml"); LegacyUrlMappingsFile = CreateTemporaryConfigurationFile("legacy-url-mappings.yml"); @@ -50,6 +51,8 @@ public ConfigurationFileProvider(IFileSystem fileSystem, bool skipPrivateReposit public IFileInfo VersionFile { get; } + public IFileInfo ProductsFile { get; } + public IFileInfo AssemblerFile { get; } public IFileInfo LegacyUrlMappingsFile { get; } diff --git a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj index abc882b8f..b612393bc 100644 --- a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj +++ b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index 3bc8769c0..e10cce493 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -16,5 +16,7 @@ namespace Elastic.Documentation.Configuration.Serialization; [YamlSerializable(typeof(GoogleTagManager))] [YamlSerializable(typeof(ContentSource))] [YamlSerializable(typeof(VersionsConfigDto))] +[YamlSerializable(typeof(ProductConfigDto))] [YamlSerializable(typeof(VersioningSystemDto))] +[YamlSerializable(typeof(ProductDto))] public partial class YamlStaticContext; diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs index a1f6525c7..fa349b402 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs @@ -11,6 +11,7 @@ namespace Elastic.Documentation.Configuration.Versions; [YamlSerializable] public record VersionsConfiguration { + public required IReadOnlyDictionary Products { get; init; } public required IReadOnlyDictionary VersioningSystems { get; init; } public VersioningSystem GetVersioningSystem(VersioningSystemId versioningSystem) { @@ -69,6 +70,30 @@ public enum VersioningSystemId ApmAgentRuby, [Display(Name = "apm_agent_rum")] ApmAgentRum, + [Display(Name = "apm_attacher")] + ApmAttacher, + [Display(Name = "apm_lambda")] + ApmLambda, + [Display(Name = "ecs_logging_dotnet")] + EcsLoggingDotnet, + [Display(Name = "ecs_logging_go_logrus")] + EcsLoggingGoLogrus, + [Display(Name = "ecs_logging_go_zap")] + EcsLoggingGoZap, + [Display(Name = "ecs_logging_go_zerolog")] + EcsLoggingGoZerolog, + [Display(Name = "ecs_logging_java")] + EcsLoggingJava, + [Display(Name = "ecs_logging_nodejs")] + EcsLoggingNodeJs, + [Display(Name = "ecs_logging_php")] + EcsLoggingPhp, + [Display(Name = "ecs_logging_python")] + EcsLoggingPython, + [Display(Name = "ecs_logging_ruby")] + EcsLoggingRuby, + [Display(Name = "esf")] + Esf, [Display(Name = "edot_ios")] EdotIos, [Display(Name = "edot_android")] @@ -86,7 +111,9 @@ public enum VersioningSystemId [Display(Name = "edot_cf_aws")] EdotCfAws, [Display(Name = "edot_collector")] - EdotCollector + EdotCollector, + [Display(Name = "search_ui")] + SearchUI } [YamlSerializable] @@ -100,3 +127,35 @@ public record VersioningSystem [YamlMember(Alias = "current")] public required SemVersion Current { get; init; } } + +[YamlSerializable] +public record Product +{ + public required string Id { get; init; } + public required string DisplayName { get; init; } + public VersioningSystemId? VersionSystem { get; init; } + + public static IReadOnlyCollection All(VersionsConfiguration versions) => [.. versions.Products.Values]; + public static IReadOnlyDictionary AllById(VersionsConfiguration versions) => versions.Products; +} + +public sealed class ProductEqualityComparer : IEqualityComparer, IComparer +{ + public bool Equals(Product? x, Product? y) => x?.Id == y?.Id; + public int GetHashCode(Product obj) => obj.Id.GetHashCode(); + + public int Compare(Product? x, Product? y) + { + if (ReferenceEquals(x, y)) + return 0; + if (y is null) + return 1; + if (x is null) + return -1; + var idComparison = string.Compare(x.Id, y.Id, StringComparison.OrdinalIgnoreCase); + if (idComparison != 0) + return idComparison; + var displayNameComparison = string.Compare(x.DisplayName, y.DisplayName, StringComparison.OrdinalIgnoreCase); + return displayNameComparison != 0 ? displayNameComparison : Nullable.Compare(x.VersionSystem, y.VersionSystem); + } +} diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs index 25c0f2f0f..59886a8b6 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs @@ -12,15 +12,17 @@ public static class VersionsConfigurationExtensions { public static VersionsConfiguration CreateVersionConfiguration(this ConfigurationFileProvider provider) { - var path = provider.VersionFile; + var versionFilePath = provider.VersionFile; + var productsFilePath = provider.ProductsFile; var deserializer = new StaticDeserializerBuilder(new YamlStaticContext()) .WithNamingConvention(UnderscoredNamingConvention.Instance) .Build(); - var dto = deserializer.Deserialize(path.OpenText()); + var versionsDto = deserializer.Deserialize(versionFilePath.OpenText()); + var productsDto = deserializer.Deserialize(productsFilePath.OpenText()); - var versions = dto.VersioningSystems.ToDictionary( + var versions = versionsDto.VersioningSystems.ToDictionary( kvp => ToVersioningSystemId(kvp.Key), kvp => new VersioningSystem { @@ -28,7 +30,15 @@ public static VersionsConfiguration CreateVersionConfiguration(this Configuratio Base = ToSemVersion(kvp.Value.Base), Current = ToSemVersion(kvp.Value.Current) }); - var config = new VersionsConfiguration { VersioningSystems = versions }; + var products = productsDto.Products.ToDictionary( + kvp => kvp.Key, + kvp => new Product + { + Id = kvp.Key, + DisplayName = kvp.Value.Display, + VersionSystem = ToVersioningSystemId(kvp.Value.Versioning) + }); + var config = new VersionsConfiguration { Products = products, VersioningSystems = versions }; return config; } @@ -66,3 +76,13 @@ internal sealed record VersioningSystemDto public string Base { get; set; } = string.Empty; public string Current { get; set; } = string.Empty; } + +internal sealed record ProductConfigDto +{ + public Dictionary Products { get; set; } = []; +} +internal sealed record ProductDto +{ + public string Display { get; set; } = string.Empty; + public string Versioning { get; set; } = string.Empty; +} diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index e736e1bb9..df1a90afd 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -10,16 +10,17 @@ namespace Elastic.Documentation.AppliesTo; -public class ApplicableToYamlConverter : IYamlTypeConverter +public class ApplicableToYamlConverter(IReadOnlyCollection productKeys) : IYamlTypeConverter { - private static readonly string[] KnownKeys = + private readonly string[] _knownKeys = [ "stack", "deployment", "serverless", "product", "ece", "eck", "ess", "self", "elasticsearch", "observability", "security", "ecctl", "curator", "apm_agent_android","apm_agent_dotnet", "apm_agent_go", "apm_agent_ios", "apm_agent_java", "apm_agent_node", "apm_agent_php", "apm_agent_python", "apm_agent_ruby", "apm_agent_rum", - "edot_ios", "edot_android", "edot_dotnet", "edot_java", "edot_node", "edot_php", "edot_python", "edot_cf_aws" + "edot_ios", "edot_android", "edot_dotnet", "edot_java", "edot_node", "edot_php", "edot_python", "edot_cf_aws", + .. productKeys ]; public bool Accepts(Type type) => type == typeof(ApplicableTo); @@ -49,7 +50,7 @@ public class ApplicableToYamlConverter : IYamlTypeConverter var oldStyleKeys = keys.Where(k => k.StartsWith(':')).ToList(); if (oldStyleKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not use valid yaml keys: {string.Join(", ", oldStyleKeys)}")); - var unknownKeys = keys.Except(KnownKeys).Except(oldStyleKeys).ToList(); + var unknownKeys = keys.Except(_knownKeys).Except(oldStyleKeys).ToList(); if (unknownKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not support the following keys: {string.Join(", ", unknownKeys)}")); diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs index ea54b9520..f39adb039 100644 --- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs @@ -6,7 +6,7 @@ using System.IO.Compression; using System.Text; using Elastic.Documentation.Configuration; -using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.Helpers; using Markdig.Syntax; @@ -116,9 +116,9 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s var configProducts = context.BuildContext.Configuration.Products.Select(p => { - if (Products.AllById.TryGetValue(p, out var product)) + if (Product.AllById(context.BuildContext.VersionsConfiguration).TryGetValue(p.Id, out var product)) return product; - throw new ArgumentException($"Invalid product id: {p}"); + throw new ArgumentException($"Invalid product id: {p.Id}"); }); var frontMatterProducts = sourceFile.YamlFrontMatter?.Products ?? []; var allProducts = frontMatterProducts @@ -128,8 +128,8 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s if (allProducts.Count > 0) { _ = metadata.AppendLine("products:"); - foreach (var product in allProducts.Select(p => p.DisplayName).Order()) - _ = metadata.AppendLine($" - {product}"); + foreach (var item in allProducts.Select(p => p.DisplayName).Order()) + _ = metadata.AppendLine($" - {item}"); } _ = metadata.AppendLine("---"); diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index e940fa79b..be9156bfd 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -4,7 +4,7 @@ using System.IO.Abstractions; using Elastic.Documentation; -using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; @@ -88,7 +88,7 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc var configProducts = DocumentationSet.Configuration.Products.Select(p => { - if (Products.AllById.TryGetValue(p, out var product)) + if (Product.AllById(DocumentationSet.Context.VersionsConfiguration).TryGetValue(p.Id, out var product)) return product; throw new ArgumentException($"Invalid product id: {p}"); }); @@ -113,7 +113,6 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc fullNavigationRenderResult ); - var slice = Page.Index.Create(new IndexViewModel { IsAssemblerBuild = DocumentationSet.Context.AssemblerBuild, diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index b5b738d56..33fbb2a88 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -379,7 +379,7 @@ private YamlFrontMatter ReadYamlFrontMatter(string raw) { try { - return YamlSerialization.Deserialize(raw); + return YamlSerialization.Deserialize(raw, _set.Context.VersionsConfiguration, [.. _set.Context.VersionsConfiguration.Products.Keys]); } catch (InvalidProductException e) { diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index 8b34aca0f..c54359156 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -124,7 +124,7 @@ private static void ProcessAppliesToDirective(AppliesToDirective appliesToDirect try { - var applicableTo = YamlSerialization.Deserialize(yaml); + var applicableTo = YamlSerialization.Deserialize(yaml, appliesToDirective.Build.VersionsConfiguration, [.. appliesToDirective.Build.VersionsConfiguration.Products.Keys]); appliesToDirective.AppliesTo = applicableTo; if (appliesToDirective.AppliesTo.Diagnostics is null) return; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 1e890eb6b..4a05e043f 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -316,7 +316,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc try { var yaml = file.FileSystem.File.ReadAllText(file.FullName); - settings = YamlSerialization.Deserialize(yaml); + settings = YamlSerialization.Deserialize(yaml, block.Context.Build.VersionsConfiguration, [.. block.Context.Build.VersionsConfiguration.Products.Keys]); } catch (YamlException e) { diff --git a/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs b/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs index f58ba115d..d1fdd57a2 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.AppliesTo; -using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; namespace Elastic.Markdown.Myst.FrontMatter; diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index 40335b5e4..3057ec9cc 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -2,16 +2,15 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using System.Collections.Frozen; -using Elastic.Documentation.Configuration.Builder; using Elastic.Documentation.Configuration.Suggestions; +using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; namespace Elastic.Markdown.Myst.FrontMatter; -public class ProductConverter : IYamlTypeConverter +public class ProductConverter(VersionsConfiguration versions) : IYamlTypeConverter { public bool Accepts(Type type) => type == typeof(Product); @@ -20,7 +19,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria if (parser.Current is Scalar) { var value = parser.Consume().Value; - throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm"); + throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm", versions); } _ = parser.Consume(); @@ -38,25 +37,19 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria _ = parser.Consume(); if (string.IsNullOrWhiteSpace(productId)) - throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm"); + throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm", versions); - if (Products.AllById.TryGetValue(productId, out var product)) + if (Product.AllById(versions).TryGetValue(productId, out var product)) return product; - throw new InvalidProductException(productId); + throw new InvalidProductException(productId, versions); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) => serializer.Invoke(value, type); } -public class InvalidProductException(string invalidValue) +public class InvalidProductException(string invalidValue, VersionsConfiguration versions) : Exception( $"Invalid products frontmatter value: \"{invalidValue}\"." + - (!string.IsNullOrWhiteSpace(invalidValue) ? " " + new Suggestion(ProductExtensions.GetProductIds(), invalidValue).GetSuggestionQuestion() : "") + + (!string.IsNullOrWhiteSpace(invalidValue) ? " " + new Suggestion(Product.All(versions).Select(p => p.Id).ToHashSet(), invalidValue).GetSuggestionQuestion() : "") + "\nYou can find the full list at https://docs-v3-preview.elastic.dev/elastic/docs-builder/tree/main/syntax/frontmatter#products."); - -public static class ProductExtensions -{ - public static IReadOnlySet GetProductIds() => - Products.All.Select(p => p.Id).ToFrozenSet(); -} diff --git a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs index ad2e13627..9e4f0c527 100644 --- a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs +++ b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs @@ -20,8 +20,8 @@ public class AppliesToRole : RoleLeaf, IApplicableToElement { public AppliesToRole(string role, string content, InlineProcessor parserContext) : base(role, content) { - AppliesTo = ParseApplicableTo(content, parserContext); BuildContext = parserContext.GetContext().Build; + AppliesTo = ParseApplicableTo(content, parserContext); } public ApplicableTo? AppliesTo { get; } @@ -32,7 +32,7 @@ public AppliesToRole(string role, string content, InlineProcessor parserContext) { try { - var applicableTo = YamlSerialization.Deserialize(yaml); + var applicableTo = YamlSerialization.Deserialize(yaml, BuildContext.VersionsConfiguration, [.. BuildContext.VersionsConfiguration.Products.Keys]); if (applicableTo.Diagnostics is null) return applicableTo; foreach (var (severity, message) in applicableTo.Diagnostics) diff --git a/src/Elastic.Markdown/Myst/YamlSerialization.cs b/src/Elastic.Markdown/Myst/YamlSerialization.cs index 857439a3c..9a27941c2 100644 --- a/src/Elastic.Markdown/Myst/YamlSerialization.cs +++ b/src/Elastic.Markdown/Myst/YamlSerialization.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.AppliesTo; +using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.Myst.Directives.Settings; using Elastic.Markdown.Myst.FrontMatter; using YamlDotNet.Serialization; @@ -12,7 +13,7 @@ namespace Elastic.Markdown.Myst; public static class YamlSerialization { - public static T Deserialize(string yaml) + public static T Deserialize(string yaml, VersionsConfiguration versions, IReadOnlyCollection productKeys) { var input = new StringReader(yaml); @@ -20,13 +21,12 @@ public static T Deserialize(string yaml) .IgnoreUnmatchedProperties() .WithEnumNamingConvention(HyphenatedNamingConvention.Instance) .WithTypeConverter(new SemVersionConverter()) - .WithTypeConverter(new ProductConverter()) - .WithTypeConverter(new ApplicableToYamlConverter()) + .WithTypeConverter(new ProductConverter(versions)) + .WithTypeConverter(new ApplicableToYamlConverter(productKeys)) .Build(); var frontMatter = deserializer.Deserialize(input); return frontMatter; - } } diff --git a/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs b/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs index 4cf0926cc..7d59b76d1 100644 --- a/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs +++ b/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs @@ -54,6 +54,10 @@ public ValueTask FinishExportAsync(IDirectoryInfo outputFolder, Cancellati _logger.LogInformation("Exporting {Name} to {ConfigFolder}", versionsConfig.Name, configFolder.FullName); fs.File.Copy(versionsConfig.FullName, Path.Combine(configFolder.FullName, versionsConfig.Name), true); + var productsConfig = configurationFileProvider.ProductsFile; + _logger.LogInformation("Exporting {Name} to {ConfigFolder}", productsConfig.Name, configFolder.FullName); + fs.File.Copy(productsConfig.FullName, Path.Combine(configFolder.FullName, productsConfig.Name), true); + return default; } } diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index 732366b6c..0c389ef61 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -25,6 +25,17 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Base = new SemVersion(8, 0, 0) } } + }, + Products = new Dictionary + { + { + "elasticsearch", new Product + { + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersionSystem = VersioningSystemId.Stack + } + } } }; return new ConfigurationContext diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index 1d40f4318..38674fb42 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -25,6 +25,33 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Base = new SemVersion(8, 0, 0) } } + }, + Products = new Dictionary + { + { + "elasticsearch", new Product + { + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersionSystem = VersioningSystemId.Stack + } + }, + { + "apm", new Product + { + Id = "apm", + DisplayName = "APM", + VersionSystem = VersioningSystemId.Stack + } + }, + { + "apm-agent", new Product + { + Id = "apm-agent", + DisplayName = "APM Agent", + VersionSystem = VersioningSystemId.Stack + } + } } }; return new ConfigurationContext diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index cae2c210e..2622a4583 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -209,7 +209,14 @@ type Setup = Base = SemVersion(8, 0, 0) ) ) - let versionConfig = VersionsConfiguration(VersioningSystems = versioningSystems) + let products = Dictionary() + products.Add("elasticsearch", Product( + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersionSystem = VersioningSystemId.ElasticsearchProject) + ) + + let versionConfig = VersionsConfiguration(VersioningSystems = versioningSystems, Products = products) let configurationFileProvider = ConfigurationFileProvider(fileSystem) let configurationContext = ConfigurationContext( VersionsConfiguration = versionConfig, diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs index 9cc16ea4f..732ef142f 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs @@ -65,6 +65,7 @@ public AssemblerConfigurationTests() public void ReadsConfigurationFiles() { Context.ConfigurationFileProvider.VersionFile.Name.Should().Be("versions.yml"); + Context.ConfigurationFileProvider.ProductsFile.Name.Should().Be("products.yml"); Context.ConfigurationFileProvider.NavigationFile.Name.Should().Be("navigation.yml"); Context.ConfigurationFileProvider.AssemblerFile.Name.Should().Be("assembler.yml"); Context.ConfigurationFileProvider.LegacyUrlMappingsFile.Name.Should().Be("legacy-url-mappings.yml"); diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs index d6445c297..f90cf622c 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs @@ -30,6 +30,17 @@ public static IConfigurationContext CreateConfigurationContext( Base = new SemVersion(8, 0, 0) } } + }, + Products = new Dictionary + { + { + "elasticsearch", new Product + { + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersionSystem = VersioningSystemId.Stack + } + } } }; return new ConfigurationContext From a2ebf39be83b96bc0433df8954fa20ce9f9793e9 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 3 Sep 2025 11:14:09 -0300 Subject: [PATCH 02/24] Adjust legacy URL mapping to use Product metadata --- .../Builder/ProductVersionMapper.cs | 58 +++++++++++++++++++ .../Legacy/ILegacyUrlMapper.cs | 4 +- src/Elastic.Markdown/HtmlWriter.cs | 7 +-- src/Elastic.Markdown/Page/Index.cshtml | 2 +- src/Elastic.Markdown/Page/IndexViewModel.cs | 50 ++++++++-------- .../docs-assembler/Cli/RepositoryCommands.cs | 4 +- .../Legacy/PageLegacyUrlMapper.cs | 46 ++++++++------- 7 files changed, 119 insertions(+), 52 deletions(-) create mode 100644 src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs diff --git a/src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs b/src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs new file mode 100644 index 000000000..c29965d91 --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs @@ -0,0 +1,58 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Frozen; +using Elastic.Documentation.Configuration.Versions; + +namespace Elastic.Documentation.Configuration.Builder; + +public static class ProductVersionMapper +{ + public static FrozenDictionary> CreateHistoryVersionMapping(VersionsConfiguration versions, FrozenDictionary> historyMappings) + { + var productIdToLegacyKeyMap = new Dictionary(); + foreach (var legacyKey in historyMappings.Keys) + { + _ = productIdToLegacyKeyMap.TryAdd(InferProductId(legacyKey), legacyKey); + } + + var resolvedProducts = new Dictionary> + { + ["docs-content"] = historyMappings.GetValueOrDefault(productIdToLegacyKeyMap[InferProductId("docs-content")]) ?? [] + }; + + foreach (var id in versions.Products.Keys) + { + IReadOnlyCollection legacyVersions = []; + if (productIdToLegacyKeyMap.TryGetValue(id, out var legacyKey)) + { + legacyVersions = historyMappings.GetValueOrDefault(legacyKey) ?? []; + } + resolvedProducts[id] = legacyVersions; + } + + return resolvedProducts.ToFrozenDictionary(); + + static string InferProductId(string url) + { + var parts = url.Trim('/').Split('/'); + if (parts.Length > 1 && parts[0] == "en") + { + var potentialProduct = parts[1]; + return potentialProduct switch + { + "ecs-logging" when parts.Length > 2 => $"ecs_logging_{parts[2].Replace("-", "_")}", + "apm" when parts.Length > 3 && parts[2] == "agent" => $"apm_agent_{parts[3].Replace("-", "_")}", + "apm" when parts.Length > 2 && parts[2] != "agent" => $"apm_{parts[2].Replace("-", "_")}", + "beats" when parts.Length > 2 => parts[2], + "elasticsearch" when parts.Length > 2 && parts[2] == "client" => parts.Length > 3 + ? parts[3] + : "elasticsearch-client", + _ => potentialProduct + }; + } + return url.Equals("docs-content", StringComparison.OrdinalIgnoreCase) ? "elastic-stack" : url; + } + } +} diff --git a/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs b/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs index 390d7d3eb..c07dddac6 100644 --- a/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs +++ b/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs @@ -11,10 +11,10 @@ public record LegacyPageMapping(string RawUrl, string Version, bool Exists) public interface ILegacyUrlMapper { - IReadOnlyCollection? MapLegacyUrl(IReadOnlyCollection? mappedPages); + IReadOnlyCollection? MapLegacyUrl(string productId, IReadOnlyCollection? mappedPages); } public record NoopLegacyUrlMapper : ILegacyUrlMapper { - public IReadOnlyCollection MapLegacyUrl(IReadOnlyCollection? mappedPages) => []; + public IReadOnlyCollection MapLegacyUrl(string productId, IReadOnlyCollection? mappedPages) => []; } diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index be9156bfd..558ee3233 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -83,8 +83,7 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc var reportUrl = $"https://github.com/elastic/docs-content/issues/new?template=issue-report.yaml&link={reportLinkParameter}&labels=source:web"; var siteName = DocumentationSet.Tree.Index.Title ?? "Elastic Documentation"; - - var legacyPages = LegacyUrlMapper.MapLegacyUrl(markdown.YamlFrontMatter?.MappedPages); + var legacyPages = LegacyUrlMapper.MapLegacyUrl(DocumentationSet.Name, markdown.YamlFrontMatter?.MappedPages); var configProducts = DocumentationSet.Configuration.Products.Select(p => { @@ -141,10 +140,10 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc Features = DocumentationSet.Configuration.Features, StaticFileContentHashProvider = StaticFileContentHashProvider, ReportIssueUrl = reportUrl, - CurrentVersion = legacyPages?.Count > 0 ? legacyPages.ElementAt(0).Version : "9.0+", + CurrentVersion = legacyPages?.Count > 0 ? legacyPages.ElementAt(0).Version : $"{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Minor}+", AllVersionsUrl = allVersionsUrl, LegacyPages = legacyPages?.Skip(1).ToArray(), - VersionDropdownItems = VersionDrownDownItemViewModel.FromLegacyPageMappings(legacyPages?.Skip(1).ToArray()), + VersionDropdownItems = VersionDropDownItemViewModel.FromLegacyPageMappings(legacyPages?.Skip(1).ToArray()), Products = allProducts, VersionsConfig = DocumentationSet.Context.VersionsConfiguration }); diff --git a/src/Elastic.Markdown/Page/Index.cshtml b/src/Elastic.Markdown/Page/Index.cshtml index e8624ab0f..d958e82bc 100644 --- a/src/Elastic.Markdown/Page/Index.cshtml +++ b/src/Elastic.Markdown/Page/Index.cshtml @@ -41,7 +41,7 @@ CurrentVersion = Model.CurrentDocument.YamlFrontMatter?.Layout == MarkdownPageLayout.LandingPage ? Model.VersionsConfig.VersioningSystems[0].Current : Model.CurrentVersion, AllVersionsUrl = Model.AllVersionsUrl, VersionDropdownSerializedModel = JsonSerializer.Serialize(Model.VersionDropdownItems, - ViewModelSerializerContext.Default.VersionDrownDownItemViewModelArray), + ViewModelSerializerContext.Default.VersionDropDownItemViewModelArray), }; protected override Task ExecuteSectionAsync(string name) { diff --git a/src/Elastic.Markdown/Page/IndexViewModel.cs b/src/Elastic.Markdown/Page/IndexViewModel.cs index 9977e951f..1c55e12e7 100644 --- a/src/Elastic.Markdown/Page/IndexViewModel.cs +++ b/src/Elastic.Markdown/Page/IndexViewModel.cs @@ -40,7 +40,7 @@ public class IndexViewModel public required string? AllVersionsUrl { get; init; } public required LegacyPageMapping[]? LegacyPages { get; init; } - public required VersionDrownDownItemViewModel[]? VersionDropdownItems { get; init; } + public required VersionDropDownItemViewModel[]? VersionDropdownItems { get; init; } public required string? UrlPathPrefix { get; init; } public required string? GithubEditUrl { get; init; } public required string MarkdownUrl { get; init; } @@ -59,7 +59,7 @@ public class IndexViewModel public required VersionsConfiguration VersionsConfig { get; init; } } -public class VersionDrownDownItemViewModel +public class VersionDropDownItemViewModel { [JsonPropertyName("name")] public required string Name { get; init; } @@ -71,45 +71,49 @@ public class VersionDrownDownItemViewModel public required bool IsDisabled { get; init; } [JsonPropertyName("children")] - public required VersionDrownDownItemViewModel[]? Children { get; init; } + public required VersionDropDownItemViewModel[]? Children { get; init; } // This logic currently only handles one level of children. Although the model supports multiple levels, it is not currently used. - public static VersionDrownDownItemViewModel[]? FromLegacyPageMappings(LegacyPageMapping[]? legacyPageMappings) + public static VersionDropDownItemViewModel[]? FromLegacyPageMappings(LegacyPageMapping[]? legacyPageMappings) { - if (legacyPageMappings is null) + if (legacyPageMappings is null || legacyPageMappings.Length == 0) return null; var groupedVersions = GroupByMajorVersion(legacyPageMappings); - return groupedVersions.Select(m => + + List versions = []; + foreach (var versionGroup in groupedVersions) { - // If there is more than one version, we need to create a dropdown - if (m.Value.Count != 1) + if (versionGroup.Value.Count != 1) { - return new VersionDrownDownItemViewModel + versions.Add(new VersionDropDownItemViewModel { - Name = m.Key, + Name = versionGroup.Key, Href = null, IsDisabled = false, - Children = m.Value.Select(v => new VersionDrownDownItemViewModel + Children = versionGroup.Value.Select(v => new VersionDropDownItemViewModel { Name = v, Href = legacyPageMappings.First(x => x.Version == v).ToString(), IsDisabled = !legacyPageMappings.First(x => x.Version == v).Exists, Children = null }).ToArray() - }; + }); } + else + { + var legacyPageMapping = legacyPageMappings.First(x => x.Version == versionGroup.Value.First()); - var legacyPageMapping = legacyPageMappings.First(x => x.Version == m.Value.First()); + versions.Add(new VersionDropDownItemViewModel + { + Name = legacyPageMapping.Version, + Href = legacyPageMapping.ToString(), + IsDisabled = !legacyPageMapping.Exists, + Children = null + }); + } + } - // If there is only one version, we don't need to create a dropdown - return new VersionDrownDownItemViewModel - { - Name = legacyPageMapping.Version, - Href = legacyPageMapping.ToString(), - IsDisabled = !legacyPageMapping.Exists, - Children = null - }; - }).ToArray(); + return versions.ToArray(); } // The legacy page mappings provide a list of versions. @@ -130,5 +134,5 @@ private static Dictionary> GroupByMajorVersion(LegacyPageMa }); } -[JsonSerializable(typeof(VersionDrownDownItemViewModel[]))] +[JsonSerializable(typeof(VersionDropDownItemViewModel[]))] public partial class ViewModelSerializerContext : JsonSerializerContext; diff --git a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs index 1ac7d4726..1c34287cb 100644 --- a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs +++ b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs @@ -16,6 +16,7 @@ using Elastic.Documentation; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Builder; using Elastic.Documentation.LegacyDocs; using Elastic.Documentation.Tooling.Arguments; using Elastic.Documentation.Tooling.Diagnostics.Console; @@ -180,7 +181,8 @@ public async Task BuildAll( var pathProvider = new GlobalNavigationPathProvider(navigationFile, assembleSources, assembleContext); var htmlWriter = new GlobalNavigationHtmlWriter(logFactory, navigation, collector); var legacyPageChecker = new LegacyPageChecker(); - var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleSources.HistoryMappings); + + var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleContext.VersionsConfiguration, ProductVersionMapper.CreateHistoryVersionMapping(assembleContext.VersionsConfiguration, assembleSources.HistoryMappings)); var builder = new AssemblerBuilder(logFactory, assembleContext, navigation, htmlWriter, pathProvider, historyMapper); await builder.BuildAllAsync(assembleSources.AssembleSets, exporters, ctx); diff --git a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs index 7f2228495..d4570014b 100644 --- a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs +++ b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs @@ -2,6 +2,8 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Legacy; using Elastic.Documentation.LegacyDocs; @@ -9,40 +11,42 @@ namespace Documentation.Assembler.Legacy; public record PageLegacyUrlMapper : ILegacyUrlMapper { - private IReadOnlyDictionary> PreviousUrls { get; } private LegacyPageChecker LegacyPageChecker { get; } - public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, IReadOnlyDictionary> previousUrls) + private string DefaultVersion { get; } + private FrozenDictionary> HistoryMappings { get; } + public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, VersionsConfiguration versions, FrozenDictionary> historyMappings) { - PreviousUrls = previousUrls; LegacyPageChecker = legacyPageChecker; + DefaultVersion = $"{versions.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{versions.VersioningSystems[VersioningSystemId.Stack].Base.Minor}"; + HistoryMappings = historyMappings; } - public IReadOnlyCollection? MapLegacyUrl(IReadOnlyCollection? mappedPages) + + public IReadOnlyCollection? MapLegacyUrl(string productId, IReadOnlyCollection? mappedPages) { if (mappedPages is null) return null; if (mappedPages.Count == 0) - return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, string.Empty, false)]; + return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; var mappedPage = mappedPages.First(); - var versions = PreviousUrls.FirstOrDefault(kv => + if (!HistoryMappings.TryGetValue(productId, out var productInfo)) { - var (key, _) = kv; - return mappedPage.Contains(key, StringComparison.OrdinalIgnoreCase); - }); - - if (versions.Value is null) - return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, string.Empty, false)]; - return versions.Value - .Select(v => - { - var legacyPageMapping = new LegacyPageMapping(mappedPage, v, true); - var path = Uri.TryCreate(legacyPageMapping.ToString(), UriKind.Absolute, out var uri) ? uri : null; - var exists = LegacyPageChecker.PathExists(path?.AbsolutePath!); - return legacyPageMapping with { Exists = exists }; - } - ).ToArray(); + return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; + } + + var allVersions = new List(); + + allVersions.AddRange(productInfo.Select(v => + { + var mapping = new LegacyPageMapping(mappedPage, v, true); + var path = Uri.TryCreate(mapping.ToString(), UriKind.Absolute, out var uri) ? uri : null; + var exists = path is not null && LegacyPageChecker.PathExists(path.AbsolutePath); + return mapping with { Exists = exists }; + })); + + return allVersions; } } From 9521c8aca7573966401ee8e28f446db75ba40389 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 9 Sep 2025 22:03:23 -0300 Subject: [PATCH 03/24] Refactor legacy-url-mappings.yml, update values on versions.yml and products.yml --- config/legacy-url-mappings.yml | 294 ++++++++++++++++++++++++--------- config/products.yml | 66 ++++++-- config/versions.yml | 11 +- 3 files changed, 275 insertions(+), 96 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 041ac7283..15ff7f35c 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -1,82 +1,228 @@ ############################################# # This file defines the legacy URL mappings for the documentation site. # It maps current documentation pages to older versions to ensure users can find the content they need. -# TODO: Refactor the model, for now we just use the first element as current version +# Upon creation of version selection dropdowns, this data is combined with the current version of its product. ############################################# stack: &stack [ '9.0+', '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.17' ] mappings: - en/apm/agent/android/: [ '1.2.0' , '0.x' ] - en/apm/agent/dotnet/: [ '1.33.0' ] - en/apm/agent/go/: [ '2.7.1', '1.x', '0.5' ] - en/apm/agent/java/: ['1.54.0', '0.7', '0.6'] - en/apm/agent/nodejs/: [ '4.x', '3.x', '2.x', '1.x' ] - en/apm/agent/php/: [ '1.15.1', '1.x' ] - en/apm/agent/python/: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] - en/apm/agent/ruby/: [ '4.8.0', '3.x', '2.x', '1.x' ] - en/apm/agent/rum-js/: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] - en/apm/agent/swift/: [ '1.2.1', '0.x' ] - en/apm/attacher/: [ '1.1.3' ] - en/apm/lambda/: [ '1.5.8' ] - en/beats/auditbeat/: *stack - en/beats/devguide/: *stack - en/beats/filebeat/: *stack - en/beats/functionbeat/: *stack - en/beats/heartbeat/: *stack - en/beats/journalbeat/: *stack - en/beats/libbeat/: *stack - en/beats/loggingplugin/: *stack - en/beats/metricbeat/: *stack - en/beats/packetbeat/: *stack - en/beats/topbeat/: *stack - en/beats/winlogbeat/: *stack - en/cloud-heroku/: [] - en/cloud-on-k8s/: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] - en/cloud/: [] - en/cloud-enterprise/: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] - en/ecctl/: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] - en/ecs-logging/: [] - en/ecs-logging/dotnet/: [ '8.18.1' ] - en/ecs-logging/go-logrus/: [ '1.0.0' ] - en/ecs-logging/go-zap/: [ '1.0.3' ] - en/ecs-logging/go-zerolog/: [ '0.2.0' ] - en/ecs-logging/java/: [ '1.7.0', '0.x' ] - en/ecs-logging/nodejs/: [ '1.5.3' ] - en/ecs-logging/overview/: [] - en/ecs-logging/php/: [ '2.0.0' ] - en/ecs-logging/python/: [ '2.2.0' ] - en/ecs-logging/ruby/: ['1.0.0'] - en/ecs/: [ '9.0+', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] - en/elastic-stack-glossary/: [] - en/elastic-stack/: *stack - en/elasticsearch/client/curator/: [ '8.0.21', '7.0', '6.0', '5.8', '5.7', '5.6', '5.5', '5.4', '5.3', '5.2', '5.1', '5.0', '4.3', '4.2', '4.1', '4.0', '3.5', '3.4', '3.3' ] - en/elasticsearch/client/eland/: [ '9.0.1' ] - en/elasticsearch/client/go-api/: *stack - en/elasticsearch/client/java-api-client/: *stack - en/elasticsearch/client/javascript-api/: *stack - en/elasticsearch/client/net-api/: *stack - en/elasticsearch/client/php-api/: *stack - en/elasticsearch/client/python-api/: *stack - en/elasticsearch/client/ruby-api/: *stack - en/elasticsearch/client/rust-api/: *stack - en/elasticsearch/hadoop/: *stack - en/elasticsearch/painless/: *stack - en/elasticsearch/plugins/: *stack - en/elasticsearch/reference/: *stack - en/esf/: ['1.20.1'] - en/fleet/: *stack - en/ingest-overview/: [] - en/ingest/: *stack - en/integrations-developer/: [] - en/integrations/: [] - en/kibana/: *stack - en/logstash-versioned-plugins/: [] - en/logstash/: *stack - en/machine-learning/: *stack - en/observability/: *stack - en/reference-architectures/: [] - en/search-ui/: ['1.24.0'] - en/security/: *stack - en/serverless/: [] - en/starting-with-the-elasticsearch-platform-and-its-solutions/: *stack + en/apm/agent/android/: + product: apm-agent-android + legacyVersions: [ '1.2.0' , '0.x' ] + en/apm/agent/dotnet/: + product: apm-agent-dotnet + legacyVersions: [ '1.33.0' ] + en/apm/agent/go/: + product: apm-agent-go + legacyVersions: [ '2.7.1', '1.x', '0.5' ] + en/apm/agent/java/: + product: apm-agent-java + legacyVersions: ['1.54.0', '0.7', '0.6'] + en/apm/agent/nodejs/: + product: apm-agent-nodejs + legacyVersions: [ '4.x', '3.x', '2.x', '1.x' ] + en/apm/agent/php/: + product: apm-agent-php + legacyVersions: [ '1.15.1', '1.x' ] + en/apm/agent/python/: + product: apm-agent-python + legacyVersions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] + en/apm/agent/ruby/: + product: apm-agent-ruby + legacyVersions: [ '4.8.0', '3.x', '2.x', '1.x' ] + en/apm/agent/rum-js/: + product: apm-agent-rum-js + legacyVersions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] + en/apm/agent/swift/: + product: apm-agent-ios + legacyVersions: [ '1.2.1', '0.x' ] + en/apm/attacher/: + product: apm-k8s-attacher + legacyVersions: [ '1.1.3' ] + en/apm/lambda/: + product: apm-aws-lambda + legacyVersions: [ '1.5.8' ] + en/beats/auditbeat/: + product: auditbeat + legacyVersions: *stack + en/beats/devguide/: + product: beats + legacyVersions: *stack + en/beats/filebeat/: + product: filebeat + legacyVersions: *stack + en/beats/functionbeat/: + product: beats + legacyVersions: *stack + en/beats/heartbeat/: + product: heartbeat + legacyVersions: *stack + en/beats/journalbeat/: + product: beats + legacyVersions: *stack + en/beats/libbeat/: + product: beats + legacyVersions: *stack + en/beats/loggingplugin/: + product: beats + legacyVersions: *stack + en/beats/metricbeat/: + product: metricbeat + legacyVersions: *stack + en/beats/packetbeat/: + product: packetbeat + legacyVersions: *stack + en/beats/topbeat/: + product: beats + legacyVersions: *stack + en/beats/winlogbeat/: + product: winlogbeat + legacyVersions: *stack + en/cloud-heroku/: + product: cloud-hosted + legacyVersions: [] + en/cloud-on-k8s/: + product: cloud-kubernetes + legacyVersions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + en/cloud/: + product: cloud-hosted + legacyVersions: [] + en/cloud-enterprise/: + product: cloud-enterprise + legacyVersions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] + en/ecctl/: + product: cloud-control-ecctl + legacyVersions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + en/ecs-logging/: + product: ecs-logging + legacyVersions: [] + en/ecs-logging/dotnet/: + product: ecs-dotnet + legacyVersions: [] + en/ecs-logging/go-logrus/: + product: ecs-logging-go-logrus + legacyVersions: [] + en/ecs-logging/go-zap/: + product: ecs-logging-go-zap + legacyVersions: [] + en/ecs-logging/go-zerolog/: + product: ecs-logging-go-zerolog + legacyVersions: [] + en/ecs-logging/java/: + product: ecs-logging-java + legacyVersions: ['0.x'] + en/ecs-logging/nodejs/: + product: ecs-logging-nodejs + legacyVersions: [] + en/ecs-logging/overview/: + product: ecs-logging + legacyVersions: [] + en/ecs-logging/php/: + product: ecs-logging-php + legacyVersions: [] + en/ecs-logging/python/: + product: ecs-logging-python + legacyVersions: [ '2.2.0' ] + en/ecs-logging/ruby/: + product: ecs-logging-ruby + legacyVersions: [] + en/ecs/: + product: ecs + legacyVersions: [ '9.0+', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + en/elastic-stack-glossary/: + product: elastic-stack + legacyVersions: [] + en/elastic-stack/: + product: elastic-stack + legacyVersions: *stack + en/elasticsearch/client/curator/: + product: curator + legacyVersions: [ '8.0.21', '7.0', '6.0', '5.8', '5.7', '5.6', '5.5', '5.4', '5.3', '5.2', '5.1', '5.0', '4.3', '4.2', '4.1', '4.0', '3.5', '3.4', '3.3' ] + en/elasticsearch/client/eland/: + product: eland + legacyVersions: [] + en/elasticsearch/client/go-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/java-api-client/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/javascript-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/net-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/php-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/python-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/ruby-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/client/rust-api/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/hadoop/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/painless/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/plugins/: + product: elasticsearch + legacyVersions: *stack + en/elasticsearch/reference/: + product: elasticsearch + legacyVersions: *stack + en/esf/: + product: elastic-serverless-forwarder + legacyVersions: ['1.20.1'] + en/fleet/: + product: elastic-agent + legacyVersions: *stack + en/ingest-overview/: + product: elasticsearch + legacyVersions: [] + en/ingest/: + product: elasticsearch + legacyVersions: *stack + en/integrations-developer/: + product: integrations + legacyVersions: [] + en/integrations/: + product: integrations + legacyVersions: [] + en/kibana/: + product: kibana + legacyVersions: *stack + en/logstash-versioned-plugins/: + product: logstash + legacyVersions: [] + en/logstash/: + product: logstash + legacyVersions: *stack + en/machine-learning/: + product: elasticsearch + legacyVersions: *stack + en/observability/: + product: observability + legacyVersions: *stack + en/reference-architectures/: + product: elasticsearch + legacyVersions: [] + en/search-ui/: + product: search-ui + legacyVersions: ['1.24.1', '1.24.0'] + en/security/: + product: security + legacyVersions: *stack + en/serverless/: + product: serverless + legacyVersions: [] + en/starting-with-the-elasticsearch-platform-and-its-solutions/: + product: elasticsearch + legacyVersions: *stack diff --git a/config/products.yml b/config/products.yml index 6f51d7969..36fb271b7 100644 --- a/config/products.yml +++ b/config/products.yml @@ -14,6 +14,9 @@ products: apm-agent-go: display: 'APM Agent for Go' versioning: 'apm_agent_go' + apm-agent-ios: + display: 'APM Agent for iOS' + versioning: 'apm_agent_ios' apm-agent-java: display: 'APM Agent for Java' versioning: 'apm_agent_java' @@ -32,6 +35,15 @@ products: apm-agent-rum-js: display: 'APM RUM JavaScript agent' versioning: 'apm_agent_rum' + apm-k8s-attacher: + display: 'APM Attacher for Kubernetes' + versioning: 'apm_k8s_attacher' + apm-aws-lambda: + display: 'APM AWS Lambda extension' + versioning: 'apm_aws_lambda' + apm-server: + display: 'APM Server' + versioning: 'stack' auditbeat: display: 'Auditbeat' versioning: 'stack' @@ -40,43 +52,58 @@ products: versioning: 'stack' cloud-control-ecctl: display: 'Elastic Cloud Control ECCTL' - versioning: 'stack' + versioning: 'ecctl' cloud-enterprise: display: 'Elastic Cloud Enterprise' - versioning: 'stack' + versioning: 'ece' cloud-hosted: display: 'Elastic Cloud Hosted' - versioning: 'stack' + versioning: 'ech' cloud-kubernetes: display: 'Elastic Cloud Kubernetes' - versioning: 'stack' + versioning: 'eck' cloud-serverless: display: 'Elastic Cloud Serverless' - versioning: 'stack' + versioning: 'serverless' cloud-terraform: display: 'Elastic Cloud Terraform' versioning: 'stack' curator: display: 'Elasticsearch Curator' versioning: 'curator' - ecctl: - display: 'Elastic Cloud Control ECCTL' - versioning: 'ecctl' - ece: - display: 'Elastic Cloud Enterprise' - versioning: 'ece' - ech: - display: 'Elastic Cloud Hosted' - versioning: 'all' - eck: - display: 'Elastic Cloud on Kubernetes' - versioning: 'eck' ecs: display: 'Elastic Common Schema (ECS)' versioning: 'stack' ecs-logging: display: 'ECS Logging' versioning: 'stack' + ecs-dotnet: + display: 'Elastic Common Schema support for .NET' + versioning: 'ecs_logging_dotnet' + ecs-logging-go-logrus: + display: 'Elastic Common Schema (ECS) support for Logrus' + versioning: 'ecs_logging_go_logrus' + ecs-logging-go-zap: + display: 'Elastic Common Schema (ECS) support for uber-go/zap logger' + versioning: 'ecs_logging_go_zap' + ecs-logging-go-zerolog: + display: 'Elastic Common Schema (ECS) support for zerolog' + versioning: 'ecs-logging-go-zerolog' + ecs-logging-java: + display: 'ECS-based logging for Java applications' + versioning: 'ecs_logging_java' + ecs-logging-nodejs: + display: 'Elastic Common Schema (ECS) support for Node js' + versioning: 'ecs_logging_nodejs' + ecs-logging-php: + display: 'ECS Logging for PHP' + versioning: 'ecs_logging_php' + ecs-logging-python: + display: 'Elastic Common Schema (ECS) support for Python' + versioning: 'ecs_logging_python' + ecs-logging-ruby: + display: 'Elastic Common Schema (ECS) support for Ruby' + versioning: 'ecs_logging_ruby' edot-cf: display: 'EDOT Cloud Forwarder' versioning: 'stack' @@ -110,12 +137,15 @@ products: edot-cf-aws: display: 'EDOT Cloud Forwarder for AWS' versioning: 'edot_cf_aws' + eland: + display: 'Eland' + versioning: 'stack' elastic-agent: display: 'Elastic Agent' versioning: 'stack' elastic-serverless-forwarder: display: 'Elastic Serverless Forwarder' - versioning: 'stack' + versioning: 'esf' elastic-stack: display: 'Elastic Stack' versioning: 'stack' diff --git a/config/versions.yml b/config/versions.yml index 976ad33b1..a88b03486 100644 --- a/config/versions.yml +++ b/config/versions.yml @@ -45,6 +45,9 @@ versioning_systems: apm_agent_go: base: 2.0 current: 2.7.1 + apm_agent_ios: + base: 1.0 + current: 1.3.0 apm_agent_java: base: 1.0 current: 1.55.0 @@ -63,10 +66,10 @@ versioning_systems: apm_agent_rum: base: 5.0 current: 5.17.0 - apm_attacher: + apm_k8s_attacher: base: 1.0 current: 1.1.3 - apm_lambda: + apm_aws_lambda: base: 1.0 current: 1.5.8 @@ -129,7 +132,7 @@ versioning_systems: current: 1.0.0 esf: base: 1.0 - current: 1.20.1 + current: 1.21.0 search_ui: base: 1.0 - current: 1.24.0 \ No newline at end of file + current: 1.24.2 \ No newline at end of file From c9a7ec38b17900c78de9a5caf641b68af896119f Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 9 Sep 2025 22:21:51 -0300 Subject: [PATCH 04/24] Fix merge --- src/tooling/docs-assembler/Cli/RepositoryCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs index e8f984682..8446dd25b 100644 --- a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs +++ b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs @@ -183,7 +183,7 @@ public async Task BuildAll( var htmlWriter = new GlobalNavigationHtmlWriter(logFactory, navigation, collector); var legacyPageChecker = new LegacyPageChecker(); - var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleContext.VersionsConfiguration, ProductVersionMapper.CreateHistoryVersionMapping(assembleContext.VersionsConfiguration, assembleSources.HistoryMappings)); + var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleContext.VersionsConfiguration, ProductVersionMapper.CreateHistoryVersionMapping(assembleContext.VersionsConfiguration, assembleSources.LegacyUrlMappings)); var builder = new AssemblerBuilder(logFactory, assembleContext, navigation, htmlWriter, pathProvider, historyMapper); From d328e327d70fb8135fd55908cc47c9ab91184249 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 9 Sep 2025 22:28:44 -0300 Subject: [PATCH 05/24] Refactor location of Product --- .../Builder/ConfigurationFile.cs | 1 + .../Products/Product.cs | 40 +++++++++++++++++++ .../Versions/VersionConfiguration.cs | 33 +-------------- .../VersionsConfigurationExtensions.cs | 1 + .../Exporters/LlmMarkdownExporter.cs | 1 + src/Elastic.Markdown/HtmlWriter.cs | 1 + .../Myst/FrontMatter/FrontMatterParser.cs | 1 + .../Myst/FrontMatter/Products.cs | 1 + src/Elastic.Markdown/Page/IndexViewModel.cs | 1 + .../Elastic.ApiExplorer.Tests/TestHelpers.cs | 1 + tests/Elastic.Markdown.Tests/TestHelpers.cs | 1 + tests/authoring/Framework/Setup.fs | 1 + .../src/docs-assembler.Tests/TestHelpers.cs | 1 + 13 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 src/Elastic.Documentation.Configuration/Products/Product.cs diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index dd8e21b7f..be20555c7 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using DotNet.Globbing; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Suggestions; using Elastic.Documentation.Configuration.TableOfContents; using Elastic.Documentation.Configuration.Versions; diff --git a/src/Elastic.Documentation.Configuration/Products/Product.cs b/src/Elastic.Documentation.Configuration/Products/Product.cs new file mode 100644 index 000000000..23322661f --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Products/Product.cs @@ -0,0 +1,40 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using Elastic.Documentation.Configuration.Versions; +using YamlDotNet.Serialization; + +namespace Elastic.Documentation.Configuration.Products; + +[YamlSerializable] +public record Product +{ + public required string Id { get; init; } + public required string DisplayName { get; init; } + public VersioningSystemId? VersionSystem { get; init; } + + public static IReadOnlyCollection All(VersionsConfiguration versions) => [.. versions.Products.Values]; + public static IReadOnlyDictionary AllById(VersionsConfiguration versions) => versions.Products; +} + +public sealed class ProductEqualityComparer : IEqualityComparer, IComparer +{ + public bool Equals(Product? x, Product? y) => x?.Id == y?.Id; + public int GetHashCode(Product obj) => obj.Id.GetHashCode(); + + public int Compare(Product? x, Product? y) + { + if (ReferenceEquals(x, y)) + return 0; + if (y is null) + return 1; + if (x is null) + return -1; + var idComparison = string.Compare(x.Id, y.Id, StringComparison.OrdinalIgnoreCase); + if (idComparison != 0) + return idComparison; + var displayNameComparison = string.Compare(x.DisplayName, y.DisplayName, StringComparison.OrdinalIgnoreCase); + return displayNameComparison != 0 ? displayNameComparison : Nullable.Compare(x.VersionSystem, y.VersionSystem); + } +} diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs index fa349b402..38d419d60 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using System.ComponentModel.DataAnnotations; +using Elastic.Documentation.Configuration.Products; using NetEscapades.EnumGenerators; using YamlDotNet.Serialization; @@ -127,35 +128,3 @@ public record VersioningSystem [YamlMember(Alias = "current")] public required SemVersion Current { get; init; } } - -[YamlSerializable] -public record Product -{ - public required string Id { get; init; } - public required string DisplayName { get; init; } - public VersioningSystemId? VersionSystem { get; init; } - - public static IReadOnlyCollection All(VersionsConfiguration versions) => [.. versions.Products.Values]; - public static IReadOnlyDictionary AllById(VersionsConfiguration versions) => versions.Products; -} - -public sealed class ProductEqualityComparer : IEqualityComparer, IComparer -{ - public bool Equals(Product? x, Product? y) => x?.Id == y?.Id; - public int GetHashCode(Product obj) => obj.Id.GetHashCode(); - - public int Compare(Product? x, Product? y) - { - if (ReferenceEquals(x, y)) - return 0; - if (y is null) - return 1; - if (x is null) - return -1; - var idComparison = string.Compare(x.Id, y.Id, StringComparison.OrdinalIgnoreCase); - if (idComparison != 0) - return idComparison; - var displayNameComparison = string.Compare(x.DisplayName, y.DisplayName, StringComparison.OrdinalIgnoreCase); - return displayNameComparison != 0 ? displayNameComparison : Nullable.Compare(x.VersionSystem, y.VersionSystem); - } -} diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs index 59886a8b6..cd5c305b9 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs @@ -2,6 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Serialization; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs index f39adb039..669ff92ad 100644 --- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs @@ -6,6 +6,7 @@ using System.IO.Compression; using System.Text; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.Helpers; using Markdig.Syntax; diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index 558ee3233..b00f57bbb 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using Elastic.Documentation; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; diff --git a/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs b/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs index d1fdd57a2..8692adf92 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.AppliesTo; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index 3057ec9cc..aeee11c92 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -2,6 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Suggestions; using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Core; diff --git a/src/Elastic.Markdown/Page/IndexViewModel.cs b/src/Elastic.Markdown/Page/IndexViewModel.cs index 1c55e12e7..342904ff8 100644 --- a/src/Elastic.Markdown/Page/IndexViewModel.cs +++ b/src/Elastic.Markdown/Page/IndexViewModel.cs @@ -6,6 +6,7 @@ using Elastic.Documentation.AppliesTo; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index 0c389ef61..7f4e931b6 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -5,6 +5,7 @@ using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Elastic.ApiExplorer.Tests; diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index 38674fb42..3fb23164a 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -5,6 +5,7 @@ using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Elastic.Markdown.Tests; diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index 2622a4583..6483b08b1 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -13,6 +13,7 @@ open System.Threading.Tasks open Elastic.Documentation open Elastic.Documentation.Configuration open Elastic.Documentation.Configuration.Versions +open Elastic.Documentation.Configuration.Products open Elastic.Markdown open Elastic.Markdown.IO open JetBrains.Annotations diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs index f90cf622c..ec8125ca3 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs @@ -5,6 +5,7 @@ using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Documentation.Assembler.Tests; From 6361eb1923e2bdf6d5cb329189bb81b47729204e Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 01:20:06 -0300 Subject: [PATCH 06/24] Fix property definition --- config/legacy-url-mappings.yml | 148 ++++++++++++++++----------------- config/products.yml | 6 +- config/versions.yml | 4 +- 3 files changed, 79 insertions(+), 79 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 15ff7f35c..6f6e8c4b5 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -9,220 +9,220 @@ stack: &stack [ '9.0+', '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', mappings: en/apm/agent/android/: product: apm-agent-android - legacyVersions: [ '1.2.0' , '0.x' ] + legacy_versions: [ '1.2.0' , '0.x' ] en/apm/agent/dotnet/: product: apm-agent-dotnet - legacyVersions: [ '1.33.0' ] + legacy_versions: [ '1.33.0' ] en/apm/agent/go/: product: apm-agent-go - legacyVersions: [ '2.7.1', '1.x', '0.5' ] + legacy_versions: [ '2.7.1', '1.x', '0.5' ] en/apm/agent/java/: product: apm-agent-java - legacyVersions: ['1.54.0', '0.7', '0.6'] + legacy_versions: ['1.54.0', '0.7', '0.6'] en/apm/agent/nodejs/: - product: apm-agent-nodejs - legacyVersions: [ '4.x', '3.x', '2.x', '1.x' ] + product: apm-agent-node + legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/php/: product: apm-agent-php - legacyVersions: [ '1.15.1', '1.x' ] + legacy_versions: [ '1.15.1', '1.x' ] en/apm/agent/python/: product: apm-agent-python - legacyVersions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] + legacy_versions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/ruby/: product: apm-agent-ruby - legacyVersions: [ '4.8.0', '3.x', '2.x', '1.x' ] + legacy_versions: [ '4.8.0', '3.x', '2.x', '1.x' ] en/apm/agent/rum-js/: product: apm-agent-rum-js - legacyVersions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] + legacy_versions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] en/apm/agent/swift/: product: apm-agent-ios - legacyVersions: [ '1.2.1', '0.x' ] + legacy_versions: [ '1.2.1', '0.x' ] en/apm/attacher/: product: apm-k8s-attacher - legacyVersions: [ '1.1.3' ] + legacy_versions: [ '1.1.3' ] en/apm/lambda/: product: apm-aws-lambda - legacyVersions: [ '1.5.8' ] + legacy_versions: [ '1.5.8' ] en/beats/auditbeat/: product: auditbeat - legacyVersions: *stack + legacy_versions: *stack en/beats/devguide/: product: beats - legacyVersions: *stack + legacy_versions: *stack en/beats/filebeat/: product: filebeat - legacyVersions: *stack + legacy_versions: *stack en/beats/functionbeat/: product: beats - legacyVersions: *stack + legacy_versions: *stack en/beats/heartbeat/: product: heartbeat - legacyVersions: *stack + legacy_versions: *stack en/beats/journalbeat/: product: beats - legacyVersions: *stack + legacy_versions: *stack en/beats/libbeat/: product: beats - legacyVersions: *stack + legacy_versions: *stack en/beats/loggingplugin/: product: beats - legacyVersions: *stack + legacy_versions: *stack en/beats/metricbeat/: product: metricbeat - legacyVersions: *stack + legacy_versions: *stack en/beats/packetbeat/: product: packetbeat - legacyVersions: *stack + legacy_versions: *stack en/beats/topbeat/: product: beats - legacyVersions: *stack + legacy_versions: *stack en/beats/winlogbeat/: product: winlogbeat - legacyVersions: *stack + legacy_versions: *stack en/cloud-heroku/: product: cloud-hosted - legacyVersions: [] + legacy_versions: [] en/cloud-on-k8s/: product: cloud-kubernetes - legacyVersions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + legacy_versions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/cloud/: product: cloud-hosted - legacyVersions: [] + legacy_versions: [] en/cloud-enterprise/: product: cloud-enterprise - legacyVersions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] + legacy_versions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] en/ecctl/: product: cloud-control-ecctl - legacyVersions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + legacy_versions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/ecs-logging/: product: ecs-logging - legacyVersions: [] + legacy_versions: [] en/ecs-logging/dotnet/: product: ecs-dotnet - legacyVersions: [] + legacy_versions: [] en/ecs-logging/go-logrus/: product: ecs-logging-go-logrus - legacyVersions: [] + legacy_versions: [] en/ecs-logging/go-zap/: product: ecs-logging-go-zap - legacyVersions: [] + legacy_versions: [] en/ecs-logging/go-zerolog/: product: ecs-logging-go-zerolog - legacyVersions: [] + legacy_versions: [] en/ecs-logging/java/: product: ecs-logging-java - legacyVersions: ['0.x'] + legacy_versions: ['0.x'] en/ecs-logging/nodejs/: product: ecs-logging-nodejs - legacyVersions: [] + legacy_versions: [] en/ecs-logging/overview/: product: ecs-logging - legacyVersions: [] + legacy_versions: [] en/ecs-logging/php/: product: ecs-logging-php - legacyVersions: [] + legacy_versions: [] en/ecs-logging/python/: product: ecs-logging-python - legacyVersions: [ '2.2.0' ] + legacy_versions: [ '2.2.0' ] en/ecs-logging/ruby/: product: ecs-logging-ruby - legacyVersions: [] + legacy_versions: [] en/ecs/: product: ecs - legacyVersions: [ '9.0+', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + legacy_versions: [ '9.0+', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/elastic-stack-glossary/: product: elastic-stack - legacyVersions: [] + legacy_versions: [] en/elastic-stack/: product: elastic-stack - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/curator/: product: curator - legacyVersions: [ '8.0.21', '7.0', '6.0', '5.8', '5.7', '5.6', '5.5', '5.4', '5.3', '5.2', '5.1', '5.0', '4.3', '4.2', '4.1', '4.0', '3.5', '3.4', '3.3' ] + legacy_versions: [ '8.0.21', '7.0', '6.0', '5.8', '5.7', '5.6', '5.5', '5.4', '5.3', '5.2', '5.1', '5.0', '4.3', '4.2', '4.1', '4.0', '3.5', '3.4', '3.3' ] en/elasticsearch/client/eland/: product: eland - legacyVersions: [] + legacy_versions: [] en/elasticsearch/client/go-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/java-api-client/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/javascript-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/net-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/php-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/python-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/ruby-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/client/rust-api/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/hadoop/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/painless/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/plugins/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/elasticsearch/reference/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/esf/: product: elastic-serverless-forwarder - legacyVersions: ['1.20.1'] + legacy_versions: ['1.20.1'] en/fleet/: product: elastic-agent - legacyVersions: *stack + legacy_versions: *stack en/ingest-overview/: product: elasticsearch - legacyVersions: [] + legacy_versions: [] en/ingest/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/integrations-developer/: product: integrations - legacyVersions: [] + legacy_versions: [] en/integrations/: product: integrations - legacyVersions: [] + legacy_versions: [] en/kibana/: product: kibana - legacyVersions: *stack + legacy_versions: *stack en/logstash-versioned-plugins/: product: logstash - legacyVersions: [] + legacy_versions: [] en/logstash/: product: logstash - legacyVersions: *stack + legacy_versions: *stack en/machine-learning/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack en/observability/: product: observability - legacyVersions: *stack + legacy_versions: *stack en/reference-architectures/: product: elasticsearch - legacyVersions: [] + legacy_versions: [] en/search-ui/: product: search-ui - legacyVersions: ['1.24.1', '1.24.0'] + legacy_versions: ['1.24.1', '1.24.0'] en/security/: product: security - legacyVersions: *stack + legacy_versions: *stack en/serverless/: product: serverless - legacyVersions: [] + legacy_versions: [] en/starting-with-the-elasticsearch-platform-and-its-solutions/: product: elasticsearch - legacyVersions: *stack + legacy_versions: *stack diff --git a/config/products.yml b/config/products.yml index 36fb271b7..626a62ceb 100644 --- a/config/products.yml +++ b/config/products.yml @@ -37,10 +37,10 @@ products: versioning: 'apm_agent_rum' apm-k8s-attacher: display: 'APM Attacher for Kubernetes' - versioning: 'apm_k8s_attacher' + versioning: 'apm_attacher' apm-aws-lambda: display: 'APM AWS Lambda extension' - versioning: 'apm_aws_lambda' + versioning: 'apm_lambda' apm-server: display: 'APM Server' versioning: 'stack' @@ -88,7 +88,7 @@ products: versioning: 'ecs_logging_go_zap' ecs-logging-go-zerolog: display: 'Elastic Common Schema (ECS) support for zerolog' - versioning: 'ecs-logging-go-zerolog' + versioning: 'ecs_logging_go_zerolog' ecs-logging-java: display: 'ECS-based logging for Java applications' versioning: 'ecs_logging_java' diff --git a/config/versions.yml b/config/versions.yml index a88b03486..61042803f 100644 --- a/config/versions.yml +++ b/config/versions.yml @@ -66,10 +66,10 @@ versioning_systems: apm_agent_rum: base: 5.0 current: 5.17.0 - apm_k8s_attacher: + apm_attacher: base: 1.0 current: 1.1.3 - apm_aws_lambda: + apm_lambda: base: 1.0 current: 1.5.8 From 1ddfa3e08519776327f80b25254570e68cbc3f3c Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 05:34:52 -0300 Subject: [PATCH 07/24] Refactor loading Product and LegacyUrlMapping data --- .../BuildContext.cs | 9 ++- .../Builder/ConfigurationFile.cs | 14 ++--- .../Builder/ProductVersionMapper.cs | 58 ------------------- .../ConfigurationFileProvider.cs | 7 +++ .../IDocumentationConfigurationContext.cs | 11 ++++ .../LegacyUrlMappings/LegacyUrlMapping.cs | 33 +++++++++++ .../LegacyUrlMappingExtensions.cs | 42 ++++++++++++++ .../Products/Product.cs | 13 +++-- .../Products/ProductExtensions.cs | 44 ++++++++++++++ .../Serialization/YamlStaticContext.cs | 4 ++ .../Versions/VersionConfiguration.cs | 2 - .../VersionsConfigurationExtensions.cs | 35 ++--------- .../AppDefaultsExtensions.cs | 8 ++- .../AppliesTo/ApplicableToYamlConverter.cs | 7 +-- .../Legacy/ILegacyUrlMapper.cs | 20 ------- .../DocumentationGenerator.cs | 2 +- .../Exporters/LlmMarkdownExporter.cs | 8 +-- src/Elastic.Markdown/HtmlWriter.cs | 16 +++-- src/Elastic.Markdown/IO/MarkdownFile.cs | 2 +- .../MarkdownLayoutViewModel.cs | 2 +- .../CodeBlocks/EnhancedCodeBlockParser.cs | 2 +- .../Myst/Directives/DirectiveHtmlRenderer.cs | 2 +- .../Myst/FrontMatter/Products.cs | 14 ++--- .../Myst/Roles/AppliesTo/AppliesToRole.cs | 2 +- .../Myst/YamlSerialization.cs | 8 +-- src/Elastic.Markdown/Page/IndexViewModel.cs | 3 +- .../DocumentationTooling.cs | 8 ++- src/tooling/docs-assembler/AssembleContext.cs | 7 +++ src/tooling/docs-assembler/AssembleSources.cs | 46 ++------------- .../Building/AssemblerBuilder.cs | 2 +- .../docs-assembler/Cli/RepositoryCommands.cs | 2 +- .../Legacy/PageLegacyUrlMapper.cs | 24 ++++---- .../Elastic.ApiExplorer.Tests/TestHelpers.cs | 17 ++++-- tests/Elastic.Markdown.Tests/TestHelpers.cs | 19 ++++-- tests/authoring/Framework/Setup.fs | 16 ++--- .../src/docs-assembler.Tests/TestHelpers.cs | 16 +++-- 36 files changed, 283 insertions(+), 242 deletions(-) delete mode 100644 src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs create mode 100644 src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs create mode 100644 src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs create mode 100644 src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs delete mode 100644 src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs diff --git a/src/Elastic.Documentation.Configuration/BuildContext.cs b/src/Elastic.Documentation.Configuration/BuildContext.cs index 9e78846fe..6b3b383c3 100644 --- a/src/Elastic.Documentation.Configuration/BuildContext.cs +++ b/src/Elastic.Documentation.Configuration/BuildContext.cs @@ -6,6 +6,8 @@ using System.Reflection; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; @@ -30,6 +32,9 @@ public record BuildContext : IDocumentationSetContext, IDocumentationConfigurati public ConfigurationFileProvider ConfigurationFileProvider { get; } public DocumentationEndpoints Endpoints { get; } + public ProductsConfiguration ProductsConfiguration { get; } + public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public IFileInfo ConfigurationPath { get; } public GitCheckoutInformation Git { get; } @@ -82,6 +87,8 @@ public BuildContext( AvailableExporters = availableExporters; VersionsConfiguration = configurationContext.VersionsConfiguration; ConfigurationFileProvider = configurationContext.ConfigurationFileProvider; + ProductsConfiguration = configurationContext.ProductsConfiguration; + LegacyUrlMappings = configurationContext.LegacyUrlMappings; Endpoints = configurationContext.Endpoints; var rootFolder = !string.IsNullOrWhiteSpace(source) @@ -100,7 +107,7 @@ public BuildContext( DocumentationSourceDirectory = ConfigurationPath.Directory!; Git = gitCheckoutInformation ?? GitCheckoutInformation.Create(DocumentationCheckoutDirectory, ReadFileSystem); - Configuration = new ConfigurationFile(this, VersionsConfiguration); + Configuration = new ConfigurationFile(this, VersionsConfiguration, ProductsConfiguration); GoogleTagManager = new GoogleTagManagerConfiguration { Enabled = false diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index be20555c7..5b5348bdc 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -63,7 +63,7 @@ public record ConfigurationFile : ITableOfContentsScope Project is not null && Project.Equals("Elastic documentation", StringComparison.OrdinalIgnoreCase); - public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration versionsConfig) + public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration versionsConfig, ProductsConfiguration productsConfig) { _context = context; ScopeDirectory = context.ConfigurationPath.Directory!; @@ -149,10 +149,10 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration break; } - if (!Product.AllById(versionsConfig).ContainsKey(productId.Value)) - reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(Product.All(versionsConfig).Select(p => p.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); + if (!productsConfig.Products.TryGetValue(productId.Value, out var productToAdd)) + reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(productsConfig.Products.Select(p => p.Value.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); else - _ = Products.Add(versionsConfig.Products[productId.Value]); + _ = Products.Add(productToAdd); } break; case "features": @@ -177,10 +177,10 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration _substitutions[key] = system.Base; } - foreach (var (id, product) in versionsConfig.Products) + foreach (var product in productsConfig.Products.Values) { - _substitutions[$"product.{id}"] = product.DisplayName; - _substitutions[$".{id}"] = product.DisplayName; + _substitutions[$"product.{product.Id}"] = product.DisplayName; + _substitutions[$".{product.Id}"] = product.DisplayName; } var toc = new TableOfContentsConfiguration(this, sourceFile, ScopeDirectory, _context, 0, ""); diff --git a/src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs b/src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs deleted file mode 100644 index c29965d91..000000000 --- a/src/Elastic.Documentation.Configuration/Builder/ProductVersionMapper.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System.Collections.Frozen; -using Elastic.Documentation.Configuration.Versions; - -namespace Elastic.Documentation.Configuration.Builder; - -public static class ProductVersionMapper -{ - public static FrozenDictionary> CreateHistoryVersionMapping(VersionsConfiguration versions, FrozenDictionary> historyMappings) - { - var productIdToLegacyKeyMap = new Dictionary(); - foreach (var legacyKey in historyMappings.Keys) - { - _ = productIdToLegacyKeyMap.TryAdd(InferProductId(legacyKey), legacyKey); - } - - var resolvedProducts = new Dictionary> - { - ["docs-content"] = historyMappings.GetValueOrDefault(productIdToLegacyKeyMap[InferProductId("docs-content")]) ?? [] - }; - - foreach (var id in versions.Products.Keys) - { - IReadOnlyCollection legacyVersions = []; - if (productIdToLegacyKeyMap.TryGetValue(id, out var legacyKey)) - { - legacyVersions = historyMappings.GetValueOrDefault(legacyKey) ?? []; - } - resolvedProducts[id] = legacyVersions; - } - - return resolvedProducts.ToFrozenDictionary(); - - static string InferProductId(string url) - { - var parts = url.Trim('/').Split('/'); - if (parts.Length > 1 && parts[0] == "en") - { - var potentialProduct = parts[1]; - return potentialProduct switch - { - "ecs-logging" when parts.Length > 2 => $"ecs_logging_{parts[2].Replace("-", "_")}", - "apm" when parts.Length > 3 && parts[2] == "agent" => $"apm_agent_{parts[3].Replace("-", "_")}", - "apm" when parts.Length > 2 && parts[2] != "agent" => $"apm_{parts[2].Replace("-", "_")}", - "beats" when parts.Length > 2 => parts[2], - "elasticsearch" when parts.Length > 2 && parts[2] == "client" => parts.Length > 3 - ? parts[3] - : "elasticsearch-client", - _ => potentialProduct - }; - } - return url.Equals("docs-content", StringComparison.OrdinalIgnoreCase) ? "elastic-stack" : url; - } - } -} diff --git a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs index 16d0e0caa..762807896 100644 --- a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs +++ b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs @@ -5,8 +5,11 @@ using System.IO.Abstractions; using System.Text.RegularExpressions; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Serialization; using Microsoft.Extensions.DependencyInjection; using NetEscapades.EnumGenerators; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; namespace Elastic.Documentation.Configuration; @@ -23,6 +26,10 @@ public partial class ConfigurationFileProvider private readonly IFileSystem _fileSystem; private readonly string _assemblyName; + internal IDeserializer Deserializer { get; } = new StaticDeserializerBuilder(new YamlStaticContext()) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .Build(); + public ConfigurationSource ConfigurationSource { get; private set; } = ConfigurationSource.Embedded; public string? GitReference { get; } diff --git a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs index da15a7635..709e35885 100644 --- a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs +++ b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs @@ -2,7 +2,9 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; +using Elastic.Documentation.Configuration.LegacyUrlMappings; namespace Elastic.Documentation.Configuration; @@ -11,6 +13,8 @@ public interface IConfigurationContext VersionsConfiguration VersionsConfiguration { get; } ConfigurationFileProvider ConfigurationFileProvider { get; } DocumentationEndpoints Endpoints { get; } + ProductsConfiguration ProductsConfiguration { get; } + LegacyUrlMappingConfiguration LegacyUrlMappings { get; } } /// Used only to seed in DI, you primarily want to depend on @@ -24,6 +28,13 @@ public class ConfigurationContext : IConfigurationContext /// public required DocumentationEndpoints Endpoints { get; init; } + + /// + public required ProductsConfiguration ProductsConfiguration { get; init; } + + /// + public required LegacyUrlMappingConfiguration LegacyUrlMappings { get; init; } + } public interface IDocumentationConfigurationContext : IDocumentationContext, IConfigurationContext; diff --git a/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs new file mode 100644 index 000000000..dd815df6f --- /dev/null +++ b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs @@ -0,0 +1,33 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using Elastic.Documentation.Configuration.Products; + +namespace Elastic.Documentation.Configuration.LegacyUrlMappings; + +public record LegacyUrlMappingConfiguration +{ + public required IReadOnlyCollection Mappings { get; init; } +} +public record LegacyUrlMapping +{ + public required string BaseUrl { get; init; } + public required Product Product { get; init; } + public required IReadOnlyCollection LegacyVersions { get; init; } +} + +public record LegacyPageMapping(Product Product, string RawUrl, string Version, bool Exists) +{ + public override string ToString() => RawUrl.Replace("/current/", $"/{Version}/"); +} + +public interface ILegacyUrlMapper +{ + IReadOnlyCollection? MapLegacyUrl(IReadOnlyCollection? mappedPages); +} + +public record NoopLegacyUrlMapper : ILegacyUrlMapper +{ + public IReadOnlyCollection MapLegacyUrl(IReadOnlyCollection? mappedPages) => []; +} diff --git a/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs new file mode 100644 index 000000000..3a61f43f0 --- /dev/null +++ b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs @@ -0,0 +1,42 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Immutable; +using Elastic.Documentation.Configuration.Products; + +namespace Elastic.Documentation.Configuration.LegacyUrlMappings; + +public static class LegacyUrlMappingExtensions +{ + public static LegacyUrlMappingConfiguration CreateLegacyUrlMappings(this ConfigurationFileProvider provider, ProductsConfiguration products) + { + var legacyUrlMappingsFilePath = provider.LegacyUrlMappingsFile; + + var legacyUrlMappingsDto = provider.Deserializer.Deserialize(legacyUrlMappingsFilePath.OpenText()); + + var legacyUrlMappings = legacyUrlMappingsDto.Mappings.Select(kvp => + new LegacyUrlMapping + { + BaseUrl = kvp.Key, + Product = products.Products[kvp.Value.Product], + LegacyVersions = kvp.Value.LegacyVersions.ToImmutableList() + }); + + return new LegacyUrlMappingConfiguration { Mappings = legacyUrlMappings.ToImmutableList() }; + } +} + +// Private DTOs for deserialization. These match the YAML structure directly. + +internal sealed record LegacyUrlMappingConfigDto +{ + public IEnumerable Stack { get; set; } = []; + public Dictionary Mappings { get; set; } = []; +} + +internal sealed record LegacyUrlMappingDto +{ + public string Product { get; set; } = string.Empty; + public IEnumerable LegacyVersions { get; set; } = []; +} diff --git a/src/Elastic.Documentation.Configuration/Products/Product.cs b/src/Elastic.Documentation.Configuration/Products/Product.cs index 23322661f..7906da969 100644 --- a/src/Elastic.Documentation.Configuration/Products/Product.cs +++ b/src/Elastic.Documentation.Configuration/Products/Product.cs @@ -2,20 +2,23 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; namespace Elastic.Documentation.Configuration.Products; +public record ProductsConfiguration +{ + public required FrozenDictionary Products { get; init; } +} + [YamlSerializable] public record Product { public required string Id { get; init; } public required string DisplayName { get; init; } - public VersioningSystemId? VersionSystem { get; init; } - - public static IReadOnlyCollection All(VersionsConfiguration versions) => [.. versions.Products.Values]; - public static IReadOnlyDictionary AllById(VersionsConfiguration versions) => versions.Products; + public VersioningSystem? VersioningSystem { get; init; } } public sealed class ProductEqualityComparer : IEqualityComparer, IComparer @@ -35,6 +38,6 @@ public int Compare(Product? x, Product? y) if (idComparison != 0) return idComparison; var displayNameComparison = string.Compare(x.DisplayName, y.DisplayName, StringComparison.OrdinalIgnoreCase); - return displayNameComparison != 0 ? displayNameComparison : Nullable.Compare(x.VersionSystem, y.VersionSystem); + return displayNameComparison != 0 ? displayNameComparison : x.VersioningSystem?.Current.CompareTo(y.VersioningSystem?.Current) ?? 0; } } diff --git a/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs new file mode 100644 index 000000000..8f6a8ca55 --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs @@ -0,0 +1,44 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Frozen; +using Elastic.Documentation.Configuration.Versions; + +namespace Elastic.Documentation.Configuration.Products; + +public static class ProductExtensions +{ + public static ProductsConfiguration CreateProducts(this ConfigurationFileProvider provider, VersionsConfiguration versionsConfiguration) + { + var productsFilePath = provider.ProductsFile; + + var productsDto = provider.Deserializer.Deserialize(productsFilePath.OpenText()); + + var products = productsDto.Products.ToDictionary( + kvp => kvp.Key, + kvp => new Product + { + Id = kvp.Key, + DisplayName = kvp.Value.Display, + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersionsConfigurationExtensions.ToVersioningSystemId(kvp.Value.Versioning)) + }); + + return new ProductsConfiguration + { + Products = products.ToFrozenDictionary() + }; + } +} + +// Private DTOs for deserialization. These match the YAML structure directly. + +internal sealed record ProductConfigDto +{ + public Dictionary Products { get; set; } = []; +} +internal sealed record ProductDto +{ + public string Display { get; set; } = string.Empty; + public string Versioning { get; set; } = string.Empty; +} diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index e10cce493..5a9c6ef8a 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; @@ -19,4 +21,6 @@ namespace Elastic.Documentation.Configuration.Serialization; [YamlSerializable(typeof(ProductConfigDto))] [YamlSerializable(typeof(VersioningSystemDto))] [YamlSerializable(typeof(ProductDto))] +[YamlSerializable(typeof(LegacyUrlMappingDto))] +[YamlSerializable(typeof(LegacyUrlMappingConfigDto))] public partial class YamlStaticContext; diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs index 38d419d60..cf48e959e 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information using System.ComponentModel.DataAnnotations; -using Elastic.Documentation.Configuration.Products; using NetEscapades.EnumGenerators; using YamlDotNet.Serialization; @@ -12,7 +11,6 @@ namespace Elastic.Documentation.Configuration.Versions; [YamlSerializable] public record VersionsConfiguration { - public required IReadOnlyDictionary Products { get; init; } public required IReadOnlyDictionary VersioningSystems { get; init; } public VersioningSystem GetVersioningSystem(VersioningSystemId versioningSystem) { diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs index cd5c305b9..430deab43 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs @@ -2,11 +2,6 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using Elastic.Documentation.Configuration.Products; -using Elastic.Documentation.Configuration.Serialization; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - namespace Elastic.Documentation.Configuration.Versions; public static class VersionsConfigurationExtensions @@ -14,14 +9,8 @@ public static class VersionsConfigurationExtensions public static VersionsConfiguration CreateVersionConfiguration(this ConfigurationFileProvider provider) { var versionFilePath = provider.VersionFile; - var productsFilePath = provider.ProductsFile; - - var deserializer = new StaticDeserializerBuilder(new YamlStaticContext()) - .WithNamingConvention(UnderscoredNamingConvention.Instance) - .Build(); - var versionsDto = deserializer.Deserialize(versionFilePath.OpenText()); - var productsDto = deserializer.Deserialize(productsFilePath.OpenText()); + var versionsDto = provider.Deserializer.Deserialize(versionFilePath.OpenText()); var versions = versionsDto.VersioningSystems.ToDictionary( kvp => ToVersioningSystemId(kvp.Key), @@ -31,19 +20,12 @@ public static VersionsConfiguration CreateVersionConfiguration(this Configuratio Base = ToSemVersion(kvp.Value.Base), Current = ToSemVersion(kvp.Value.Current) }); - var products = productsDto.Products.ToDictionary( - kvp => kvp.Key, - kvp => new Product - { - Id = kvp.Key, - DisplayName = kvp.Value.Display, - VersionSystem = ToVersioningSystemId(kvp.Value.Versioning) - }); - var config = new VersionsConfiguration { Products = products, VersioningSystems = versions }; + var config = new VersionsConfiguration { VersioningSystems = versions }; + return config; } - private static VersioningSystemId ToVersioningSystemId(string id) + internal static VersioningSystemId ToVersioningSystemId(string id) { if (!VersioningSystemIdExtensions.TryParse(id, out var versioningSystemId, true, true)) throw new InvalidOperationException($"Could not parse versioning system id {id}"); @@ -78,12 +60,3 @@ internal sealed record VersioningSystemDto public string Current { get; set; } = string.Empty; } -internal sealed record ProductConfigDto -{ - public Dictionary Products { get; set; } = []; -} -internal sealed record ProductDto -{ - public string Display { get; set; } = string.Empty; - public string Versioning { get; set; } = string.Empty; -} diff --git a/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs b/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs index bcfda4fd1..706859967 100644 --- a/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs +++ b/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.ServiceDefaults.Logging; using Microsoft.Extensions.DependencyInjection; @@ -32,7 +34,11 @@ public static TBuilder AddDocumentationServiceDefaults(this TBuilder b _ = services .AddConfigurationFileProvider(skipPrivateRepositories, (s, p) => { - _ = s.AddSingleton(p.CreateVersionConfiguration()); + var versionConfiguration = p.CreateVersionConfiguration(); + var products = p.CreateProducts(versionConfiguration); + _ = s.AddSingleton(p.CreateLegacyUrlMappings(products)); + _ = s.AddSingleton(products); + _ = s.AddSingleton(versionConfiguration); configure?.Invoke(s, p); }); _ = builder.Services.AddElasticDocumentationLogging(logLevel); diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index df1a90afd..23eb895e8 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -14,12 +14,7 @@ public class ApplicableToYamlConverter(IReadOnlyCollection productKeys) { private readonly string[] _knownKeys = [ - "stack", "deployment", "serverless", "product", - "ece", "eck", "ess", "self", - "elasticsearch", "observability", "security", - "ecctl", "curator", - "apm_agent_android","apm_agent_dotnet", "apm_agent_go", "apm_agent_ios", "apm_agent_java", "apm_agent_node", "apm_agent_php", "apm_agent_python", "apm_agent_ruby", "apm_agent_rum", - "edot_ios", "edot_android", "edot_dotnet", "edot_java", "edot_node", "edot_php", "edot_python", "edot_cf_aws", + "stack", "deployment", "product", "ece", "eck", "ess", "ecctl", .. productKeys ]; diff --git a/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs b/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs deleted file mode 100644 index c07dddac6..000000000 --- a/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -namespace Elastic.Documentation.Legacy; - -public record LegacyPageMapping(string RawUrl, string Version, bool Exists) -{ - public override string ToString() => RawUrl.Replace("/current/", $"/{Version}/"); -}; - -public interface ILegacyUrlMapper -{ - IReadOnlyCollection? MapLegacyUrl(string productId, IReadOnlyCollection? mappedPages); -} - -public record NoopLegacyUrlMapper : ILegacyUrlMapper -{ - public IReadOnlyCollection MapLegacyUrl(string productId, IReadOnlyCollection? mappedPages) => []; -} diff --git a/src/Elastic.Markdown/DocumentationGenerator.cs b/src/Elastic.Markdown/DocumentationGenerator.cs index 200d19d78..5929bfd4c 100644 --- a/src/Elastic.Markdown/DocumentationGenerator.cs +++ b/src/Elastic.Markdown/DocumentationGenerator.cs @@ -6,7 +6,7 @@ using System.Text.Json; using Elastic.Documentation; using Elastic.Documentation.Configuration; -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Links; using Elastic.Documentation.Serialization; using Elastic.Documentation.Site.FileProviders; diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs index 669ff92ad..1ed4b7207 100644 --- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs @@ -6,8 +6,6 @@ using System.IO.Compression; using System.Text; using Elastic.Documentation.Configuration; -using Elastic.Documentation.Configuration.Products; -using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.Helpers; using Markdig.Syntax; @@ -115,11 +113,11 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s if (!string.IsNullOrEmpty(sourceFile.Url)) _ = metadata.AppendLine($"url: {context.BuildContext.CanonicalBaseUrl?.Scheme}://{context.BuildContext.CanonicalBaseUrl?.Host}{sourceFile.Url}"); - var configProducts = context.BuildContext.Configuration.Products.Select(p => + var configProducts = context.BuildContext.ProductsConfiguration.Products.Select(p => { - if (Product.AllById(context.BuildContext.VersionsConfiguration).TryGetValue(p.Id, out var product)) + if (context.BuildContext.ProductsConfiguration.Products.TryGetValue(p.Value.Id, out var product)) return product; - throw new ArgumentException($"Invalid product id: {p.Id}"); + throw new ArgumentException($"Invalid product id: {p.Value.Id}"); }); var frontMatterProducts = sourceFile.YamlFrontMatter?.Products ?? []; var allProducts = frontMatterProducts diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index b00f57bbb..0841b4fff 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -4,9 +4,9 @@ using System.IO.Abstractions; using Elastic.Documentation; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; -using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; using Elastic.Markdown.Extensions.DetectionRules; @@ -84,13 +84,13 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc var reportUrl = $"https://github.com/elastic/docs-content/issues/new?template=issue-report.yaml&link={reportLinkParameter}&labels=source:web"; var siteName = DocumentationSet.Tree.Index.Title ?? "Elastic Documentation"; - var legacyPages = LegacyUrlMapper.MapLegacyUrl(DocumentationSet.Name, markdown.YamlFrontMatter?.MappedPages); + var legacyPages = LegacyUrlMapper.MapLegacyUrl(markdown.YamlFrontMatter?.MappedPages); - var configProducts = DocumentationSet.Configuration.Products.Select(p => + var configProducts = DocumentationSet.Context.ProductsConfiguration.Products.Select(p => { - if (Product.AllById(DocumentationSet.Context.VersionsConfiguration).TryGetValue(p.Id, out var product)) + if (DocumentationSet.Context.ProductsConfiguration.Products.TryGetValue(p.Value.Id, out var product)) return product; - throw new ArgumentException($"Invalid product id: {p}"); + throw new ArgumentException($"Invalid product id: {p.Value.Id}"); }); var frontMatterProducts = markdown.YamlFrontMatter?.Products ?? []; @@ -113,6 +113,10 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc fullNavigationRenderResult ); + var currentBaseVersion = legacyPages is { Count: > 0 } + ? $"{legacyPages.ElementAt(0).Product.VersioningSystem?.Base.Major}.{legacyPages.ElementAt(0).Product.VersioningSystem?.Base.Minor}+" + : $"{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Minor}+"; + var slice = Page.Index.Create(new IndexViewModel { IsAssemblerBuild = DocumentationSet.Context.AssemblerBuild, @@ -141,7 +145,7 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc Features = DocumentationSet.Configuration.Features, StaticFileContentHashProvider = StaticFileContentHashProvider, ReportIssueUrl = reportUrl, - CurrentVersion = legacyPages?.Count > 0 ? legacyPages.ElementAt(0).Version : $"{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Minor}+", + CurrentVersion = currentBaseVersion, AllVersionsUrl = allVersionsUrl, LegacyPages = legacyPages?.Skip(1).ToArray(), VersionDropdownItems = VersionDropDownItemViewModel.FromLegacyPageMappings(legacyPages?.Skip(1).ToArray()), diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index f6e6bddcb..a7ae84d99 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -379,7 +379,7 @@ private YamlFrontMatter ReadYamlFrontMatter(string raw) { try { - return YamlSerialization.Deserialize(raw, _set.Context.VersionsConfiguration, [.. _set.Context.VersionsConfiguration.Products.Keys]); + return YamlSerialization.Deserialize(raw, _set.Context.ProductsConfiguration); } catch (InvalidProductException e) { diff --git a/src/Elastic.Markdown/MarkdownLayoutViewModel.cs b/src/Elastic.Markdown/MarkdownLayoutViewModel.cs index b310675b3..7676d747f 100644 --- a/src/Elastic.Markdown/MarkdownLayoutViewModel.cs +++ b/src/Elastic.Markdown/MarkdownLayoutViewModel.cs @@ -2,7 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Site; using Elastic.Documentation.Site.Navigation; diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index c54359156..de2e0c4b6 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -124,7 +124,7 @@ private static void ProcessAppliesToDirective(AppliesToDirective appliesToDirect try { - var applicableTo = YamlSerialization.Deserialize(yaml, appliesToDirective.Build.VersionsConfiguration, [.. appliesToDirective.Build.VersionsConfiguration.Products.Keys]); + var applicableTo = YamlSerialization.Deserialize(yaml, appliesToDirective.Build.ProductsConfiguration); appliesToDirective.AppliesTo = applicableTo; if (appliesToDirective.AppliesTo.Diagnostics is null) return; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 4a05e043f..6beee7e63 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -316,7 +316,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc try { var yaml = file.FileSystem.File.ReadAllText(file.FullName); - settings = YamlSerialization.Deserialize(yaml, block.Context.Build.VersionsConfiguration, [.. block.Context.Build.VersionsConfiguration.Products.Keys]); + settings = YamlSerialization.Deserialize(yaml, block.Context.Build.ProductsConfiguration); } catch (YamlException e) { diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index aeee11c92..74fb99628 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -11,7 +11,7 @@ namespace Elastic.Markdown.Myst.FrontMatter; -public class ProductConverter(VersionsConfiguration versions) : IYamlTypeConverter +public class ProductConverter(ProductsConfiguration products) : IYamlTypeConverter { public bool Accepts(Type type) => type == typeof(Product); @@ -20,7 +20,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria if (parser.Current is Scalar) { var value = parser.Consume().Value; - throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm", versions); + throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm", products); } _ = parser.Consume(); @@ -38,19 +38,19 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria _ = parser.Consume(); if (string.IsNullOrWhiteSpace(productId)) - throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm", versions); + throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm", products); - if (Product.AllById(versions).TryGetValue(productId, out var product)) + if (products.Products.TryGetValue(productId, out var product)) return product; - throw new InvalidProductException(productId, versions); + throw new InvalidProductException(productId, products); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) => serializer.Invoke(value, type); } -public class InvalidProductException(string invalidValue, VersionsConfiguration versions) +public class InvalidProductException(string invalidValue, ProductsConfiguration products) : Exception( $"Invalid products frontmatter value: \"{invalidValue}\"." + - (!string.IsNullOrWhiteSpace(invalidValue) ? " " + new Suggestion(Product.All(versions).Select(p => p.Id).ToHashSet(), invalidValue).GetSuggestionQuestion() : "") + + (!string.IsNullOrWhiteSpace(invalidValue) ? " " + new Suggestion(products.Products.Select(p => p.Value.Id).ToHashSet(), invalidValue).GetSuggestionQuestion() : "") + "\nYou can find the full list at https://docs-v3-preview.elastic.dev/elastic/docs-builder/tree/main/syntax/frontmatter#products."); diff --git a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs index 9e4f0c527..d65d9dd06 100644 --- a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs +++ b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs @@ -32,7 +32,7 @@ public AppliesToRole(string role, string content, InlineProcessor parserContext) { try { - var applicableTo = YamlSerialization.Deserialize(yaml, BuildContext.VersionsConfiguration, [.. BuildContext.VersionsConfiguration.Products.Keys]); + var applicableTo = YamlSerialization.Deserialize(yaml, BuildContext.ProductsConfiguration); if (applicableTo.Diagnostics is null) return applicableTo; foreach (var (severity, message) in applicableTo.Diagnostics) diff --git a/src/Elastic.Markdown/Myst/YamlSerialization.cs b/src/Elastic.Markdown/Myst/YamlSerialization.cs index 9a27941c2..c529932ba 100644 --- a/src/Elastic.Markdown/Myst/YamlSerialization.cs +++ b/src/Elastic.Markdown/Myst/YamlSerialization.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.AppliesTo; -using Elastic.Documentation.Configuration.Versions; +using Elastic.Documentation.Configuration.Products; using Elastic.Markdown.Myst.Directives.Settings; using Elastic.Markdown.Myst.FrontMatter; using YamlDotNet.Serialization; @@ -13,7 +13,7 @@ namespace Elastic.Markdown.Myst; public static class YamlSerialization { - public static T Deserialize(string yaml, VersionsConfiguration versions, IReadOnlyCollection productKeys) + public static T Deserialize(string yaml, ProductsConfiguration products) { var input = new StringReader(yaml); @@ -21,8 +21,8 @@ public static T Deserialize(string yaml, VersionsConfiguration versions, IRea .IgnoreUnmatchedProperties() .WithEnumNamingConvention(HyphenatedNamingConvention.Instance) .WithTypeConverter(new SemVersionConverter()) - .WithTypeConverter(new ProductConverter(versions)) - .WithTypeConverter(new ApplicableToYamlConverter(productKeys)) + .WithTypeConverter(new ProductConverter(products)) + .WithTypeConverter(new ApplicableToYamlConverter(products.Products.Keys)) .Build(); var frontMatter = deserializer.Deserialize(input); diff --git a/src/Elastic.Markdown/Page/IndexViewModel.cs b/src/Elastic.Markdown/Page/IndexViewModel.cs index 342904ff8..2bab8ada4 100644 --- a/src/Elastic.Markdown/Page/IndexViewModel.cs +++ b/src/Elastic.Markdown/Page/IndexViewModel.cs @@ -6,9 +6,9 @@ using Elastic.Documentation.AppliesTo; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; -using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; using Elastic.Markdown.IO; @@ -28,7 +28,6 @@ public class IndexViewModel public required DocumentationGroup Tree { get; init; } public required IReadOnlyCollection PageTocItems { get; init; } public required MarkdownFile CurrentDocument { get; init; } - public required INavigationItem CurrentNavigationItem { get; init; } public required INavigationItem? PreviousDocument { get; init; } public required INavigationItem? NextDocument { get; init; } diff --git a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs index 478a7ced1..58b881234 100644 --- a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs +++ b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs @@ -6,6 +6,8 @@ using System.Net.Sockets; using Actions.Core.Extensions; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; using Microsoft.Extensions.DependencyInjection; @@ -60,11 +62,15 @@ public static TBuilder AddDocumentationToolingDefaults(this TBuilder b var endpoints = sp.GetRequiredService(); var configurationFileProvider = sp.GetRequiredService(); var versionsConfiguration = sp.GetRequiredService(); + var products = sp.GetRequiredService(); + var legacyUrlMappings = sp.GetRequiredService(); return new ConfigurationContext { ConfigurationFileProvider = configurationFileProvider, VersionsConfiguration = versionsConfiguration, - Endpoints = endpoints + Endpoints = endpoints, + ProductsConfiguration = products, + LegacyUrlMappings = legacyUrlMappings }; }); diff --git a/src/tooling/docs-assembler/AssembleContext.cs b/src/tooling/docs-assembler/AssembleContext.cs index 131299530..cdef3e212 100644 --- a/src/tooling/docs-assembler/AssembleContext.cs +++ b/src/tooling/docs-assembler/AssembleContext.cs @@ -5,6 +5,8 @@ using System.IO.Abstractions; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; @@ -27,6 +29,9 @@ public class AssembleContext : IDocumentationConfigurationContext /// public DocumentationEndpoints Endpoints { get; } + public ProductsConfiguration ProductsConfiguration { get; } + public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public IDirectoryInfo CheckoutDirectory { get; } public IDirectoryInfo OutputDirectory { get; } @@ -52,6 +57,8 @@ public AssembleContext( ConfigurationFileProvider = configurationContext.ConfigurationFileProvider; VersionsConfiguration = configurationContext.VersionsConfiguration; Endpoints = configurationContext.Endpoints; + ProductsConfiguration = configurationContext.ProductsConfiguration; + LegacyUrlMappings = configurationContext.LegacyUrlMappings; if (!Configuration.Environments.TryGetValue(environment, out var env)) throw new Exception($"Could not find environment {environment}"); diff --git a/src/tooling/docs-assembler/AssembleSources.cs b/src/tooling/docs-assembler/AssembleSources.cs index 835cc9415..ca8848b6e 100644 --- a/src/tooling/docs-assembler/AssembleSources.cs +++ b/src/tooling/docs-assembler/AssembleSources.cs @@ -11,6 +11,7 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.LinkIndex; using Elastic.Markdown.IO.Navigation; using Elastic.Markdown.Links.CrossLinks; @@ -42,7 +43,7 @@ public class AssembleSources public FrozenDictionary NavigationTocMappings { get; } - public FrozenDictionary> LegacyUrlMappings { get; } + public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } public FrozenDictionary TocConfigurationMapping { get; } @@ -61,7 +62,6 @@ Cancel ctx { var linkIndexProvider = Aws3LinkIndexReader.CreateAnonymous(); var navigationTocMappings = GetTocMappings(context); - var legacyUrlMappings = GetLegacyUrlMappings(context); var uriResolver = new PublishEnvironmentUriResolver(navigationTocMappings, context.Environment); var crossLinkFetcher = new AssemblerCrossLinkFetcher(logFactory, context.Configuration, context.Environment, linkIndexProvider); @@ -74,7 +74,7 @@ Cancel ctx checkouts, configurationContext, navigationTocMappings, - legacyUrlMappings, + configurationContext.LegacyUrlMappings, uriResolver, crossLinkResolver, availableExporters @@ -90,7 +90,7 @@ private AssembleSources( Checkout[] checkouts, IConfigurationContext configurationContext, FrozenDictionary navigationTocMappings, - FrozenDictionary> legacyUrlMappings, + LegacyUrlMappingConfiguration legacyUrlMappings, PublishEnvironmentUriResolver uriResolver, ICrossLinkResolver crossLinkResolver, IReadOnlySet availableExporters @@ -145,44 +145,6 @@ IReadOnlySet availableExporters .ToFrozenDictionary(); } - private static FrozenDictionary> GetLegacyUrlMappings(AssembleContext context) - { - var dictionary = new Dictionary>(); - var reader = new YamlStreamReader(context.ConfigurationFileProvider.LegacyUrlMappingsFile, context.Collector); - foreach (var entry in reader.Read()) - { - switch (entry.Key) - { - case "mappings": - ReadHistoryMappings(dictionary, reader, entry); - break; - } - } - - return dictionary.OrderByDescending(x => x.Key.Length).ToFrozenDictionary(); - - static void ReadHistoryMappings(IDictionary> dictionary, YamlStreamReader reader, YamlToplevelKey entry) - { - if (entry.Entry.Value is not YamlMappingNode mappings) - { - reader.EmitWarning($"It wasn't possible to read the mappings"); - return; - } - - foreach (var mapping in mappings) - { - var mappingKey = $"{((YamlScalarNode)mapping.Key).Value}"; - var mappingValues = ((YamlSequenceNode)mapping.Value).Children.OfType().Where(x => x.Value is not null).Select(x => x.Value!) - .ToList(); - if (dictionary.TryGetValue(mappingKey, out _)) - reader.EmitWarning($"'{mappingKey}' is already mapped to '{mappingValues}'"); - else - dictionary[mappingKey] = mappingValues; - } - } - } - - public static FrozenDictionary GetTocMappings(AssembleContext context) { var dictionary = new Dictionary(); diff --git a/src/tooling/docs-assembler/Building/AssemblerBuilder.cs b/src/tooling/docs-assembler/Building/AssemblerBuilder.cs index deb0d6a06..776a5e8d5 100644 --- a/src/tooling/docs-assembler/Building/AssemblerBuilder.cs +++ b/src/tooling/docs-assembler/Building/AssemblerBuilder.cs @@ -7,7 +7,7 @@ using Documentation.Assembler.Navigation; using Elastic.Documentation; using Elastic.Documentation.Configuration.Assembler; -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Links; using Elastic.Documentation.Serialization; using Elastic.Documentation.Tooling.Arguments; diff --git a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs index 8446dd25b..0d1ddc632 100644 --- a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs +++ b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs @@ -183,7 +183,7 @@ public async Task BuildAll( var htmlWriter = new GlobalNavigationHtmlWriter(logFactory, navigation, collector); var legacyPageChecker = new LegacyPageChecker(); - var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleContext.VersionsConfiguration, ProductVersionMapper.CreateHistoryVersionMapping(assembleContext.VersionsConfiguration, assembleSources.LegacyUrlMappings)); + var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleContext.VersionsConfiguration, configurationContext.LegacyUrlMappings); var builder = new AssemblerBuilder(logFactory, assembleContext, navigation, htmlWriter, pathProvider, historyMapper); diff --git a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs index d4570014b..08b883bc4 100644 --- a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs +++ b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs @@ -2,9 +2,8 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using System.Collections.Frozen; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Versions; -using Elastic.Documentation.Legacy; using Elastic.Documentation.LegacyDocs; namespace Documentation.Assembler.Legacy; @@ -13,35 +12,32 @@ public record PageLegacyUrlMapper : ILegacyUrlMapper { private LegacyPageChecker LegacyPageChecker { get; } private string DefaultVersion { get; } - private FrozenDictionary> HistoryMappings { get; } - public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, VersionsConfiguration versions, FrozenDictionary> historyMappings) + private LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, VersionsConfiguration versions, LegacyUrlMappingConfiguration legacyUrlMappings) { LegacyPageChecker = legacyPageChecker; DefaultVersion = $"{versions.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{versions.VersioningSystems[VersioningSystemId.Stack].Base.Minor}"; - HistoryMappings = historyMappings; + LegacyUrlMappings = legacyUrlMappings; } - public IReadOnlyCollection? MapLegacyUrl(string productId, IReadOnlyCollection? mappedPages) + public IReadOnlyCollection? MapLegacyUrl(IReadOnlyCollection? mappedPages) { - if (mappedPages is null) + if (mappedPages is null || mappedPages.Count == 0) return null; - if (mappedPages.Count == 0) - return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; - var mappedPage = mappedPages.First(); - if (!HistoryMappings.TryGetValue(productId, out var productInfo)) + if (LegacyUrlMappings.Mappings.FirstOrDefault(x => mappedPage.Contains(x.BaseUrl, StringComparison.OrdinalIgnoreCase)) is not { } legacyMappingMatch) { - return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; + return [new LegacyPageMapping(LegacyUrlMappings.Mappings.First(x => x.Product.Id.Equals("elastic-stack", StringComparison.InvariantCultureIgnoreCase)).Product, mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; } var allVersions = new List(); - allVersions.AddRange(productInfo.Select(v => + allVersions.AddRange(legacyMappingMatch.LegacyVersions.Select(x => { - var mapping = new LegacyPageMapping(mappedPage, v, true); + var mapping = new LegacyPageMapping(legacyMappingMatch.Product, mappedPage, x, false); var path = Uri.TryCreate(mapping.ToString(), UriKind.Absolute, out var uri) ? uri : null; var exists = path is not null && LegacyPageChecker.PathExists(path.AbsolutePath); return mapping with { Exists = exists }; diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index 7f4e931b6..6c62afbf9 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -2,9 +2,11 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; @@ -12,7 +14,7 @@ namespace Elastic.ApiExplorer.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -27,17 +29,20 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS } } }, - Products = new Dictionary + }; + productsConfiguration ??= new ProductsConfiguration + { + Products = new Dictionary() { { "elasticsearch", new Product { Id = "elasticsearch", DisplayName = "Elasticsearch", - VersionSystem = VersioningSystemId.Stack + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) } } - } + }.ToFrozenDictionary() }; return new ConfigurationContext { @@ -46,7 +51,9 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Elasticsearch = ElasticsearchEndpoint.Default, }, ConfigurationFileProvider = new ConfigurationFileProvider(fileSystem), - VersionsConfiguration = versionsConfiguration + VersionsConfiguration = versionsConfiguration, + ProductsConfiguration = productsConfiguration, + LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } } diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index 3fb23164a..3b1a95e1e 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -2,9 +2,11 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; @@ -12,7 +14,7 @@ namespace Elastic.Markdown.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -27,6 +29,9 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS } } }, + }; + productsConfiguration ??= new ProductsConfiguration + { Products = new Dictionary { { @@ -34,7 +39,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS { Id = "elasticsearch", DisplayName = "Elasticsearch", - VersionSystem = VersioningSystemId.Stack + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) } }, { @@ -42,7 +47,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS { Id = "apm", DisplayName = "APM", - VersionSystem = VersioningSystemId.Stack + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) } }, { @@ -50,10 +55,10 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS { Id = "apm-agent", DisplayName = "APM Agent", - VersionSystem = VersioningSystemId.Stack + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) } } - } + }.ToFrozenDictionary() }; return new ConfigurationContext { @@ -62,7 +67,9 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Elasticsearch = ElasticsearchEndpoint.Default, }, ConfigurationFileProvider = new ConfigurationFileProvider(fileSystem), - VersionsConfiguration = versionsConfiguration + VersionsConfiguration = versionsConfiguration, + ProductsConfiguration = productsConfiguration, + LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } } diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index 6483b08b1..9037de0b7 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -6,12 +6,14 @@ namespace authoring open System +open System.Collections.Frozen open System.Collections.Generic open System.IO open System.IO.Abstractions.TestingHelpers open System.Threading.Tasks open Elastic.Documentation open Elastic.Documentation.Configuration +open Elastic.Documentation.Configuration.LegacyUrlMappings open Elastic.Documentation.Configuration.Versions open Elastic.Documentation.Configuration.Products open Elastic.Markdown @@ -210,19 +212,19 @@ type Setup = Base = SemVersion(8, 0, 0) ) ) - let products = Dictionary() - products.Add("elasticsearch", Product( - Id = "elasticsearch", + let versionConfig = VersionsConfiguration(VersioningSystems = versioningSystems) + let productDict = Dictionary() + productDict.Add("elasticsearch", Product(Id = "elasticsearch", DisplayName = "Elasticsearch", - VersionSystem = VersioningSystemId.ElasticsearchProject) - ) + VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.ElasticsearchProject])) - let versionConfig = VersionsConfiguration(VersioningSystems = versioningSystems, Products = products) let configurationFileProvider = ConfigurationFileProvider(fileSystem) let configurationContext = ConfigurationContext( VersionsConfiguration = versionConfig, ConfigurationFileProvider = configurationFileProvider, - Endpoints=DocumentationEndpoints(Elasticsearch = ElasticsearchEndpoint.Default) + Endpoints=DocumentationEndpoints(Elasticsearch = ElasticsearchEndpoint.Default), + ProductsConfiguration = ProductsConfiguration(Products = productDict.ToFrozenDictionary()), + LegacyUrlMappings = LegacyUrlMappingConfiguration(Mappings = []) ) let context = BuildContext( collector, diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs index ec8125ca3..ace2d9f5c 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs @@ -2,9 +2,11 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; @@ -15,7 +17,8 @@ public static class TestHelpers public static IConfigurationContext CreateConfigurationContext( IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, - ConfigurationFileProvider? configurationFileProvider = null + ConfigurationFileProvider? configurationFileProvider = null, + ProductsConfiguration? productsConfiguration = null ) { configurationFileProvider ??= new ConfigurationFileProvider(fileSystem); @@ -32,6 +35,9 @@ public static IConfigurationContext CreateConfigurationContext( } } }, + }; + productsConfiguration ??= new ProductsConfiguration + { Products = new Dictionary { { @@ -39,10 +45,10 @@ public static IConfigurationContext CreateConfigurationContext( { Id = "elasticsearch", DisplayName = "Elasticsearch", - VersionSystem = VersioningSystemId.Stack + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) } } - } + }.ToFrozenDictionary() }; return new ConfigurationContext { @@ -51,7 +57,9 @@ public static IConfigurationContext CreateConfigurationContext( Elasticsearch = ElasticsearchEndpoint.Default, }, ConfigurationFileProvider = configurationFileProvider, - VersionsConfiguration = versionsConfiguration + VersionsConfiguration = versionsConfiguration, + ProductsConfiguration = productsConfiguration, + LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } } From ed9aeb1567cdb4ce303e83c50a1a23c886dce08b Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 06:09:12 -0300 Subject: [PATCH 08/24] Add documentation --- docs/_docset.yml | 1 + docs/configure/site/index.md | 1 + docs/configure/site/legacy-url-mappings.md | 14 ++++---- docs/configure/site/products.md | 37 ++++++++++++++++++++++ docs/syntax/version-variables.md | 13 ++++++++ 5 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 docs/configure/site/products.md diff --git a/docs/_docset.yml b/docs/_docset.yml index bfbd5018a..1a942c22d 100644 --- a/docs/_docset.yml +++ b/docs/_docset.yml @@ -76,6 +76,7 @@ toc: - file: navigation.md - file: versions.md - file: legacy-url-mappings.md + - file: products.md - folder: content-set children: - file: index.md diff --git a/docs/configure/site/index.md b/docs/configure/site/index.md index 1728bad3e..e255e46a2 100644 --- a/docs/configure/site/index.md +++ b/docs/configure/site/index.md @@ -10,6 +10,7 @@ Configure the documentation site in these files: - [](./navigation.md) - global navigation index - [](./versions.md) - global versioning schemes - [](./legacy-url-mappings.md) - supported legacy version +- [](./products.md) - company product metadata ## Redirects diff --git a/docs/configure/site/legacy-url-mappings.md b/docs/configure/site/legacy-url-mappings.md index 11ab0a221..0d72bb433 100644 --- a/docs/configure/site/legacy-url-mappings.md +++ b/docs/configure/site/legacy-url-mappings.md @@ -5,7 +5,9 @@ This [`legacy-url-mappings.yml`](https://github.com/elastic/docs-builder/blob/ma This example maps documentation that references `elastic.co/guide/en/elasticsearch/reference/ to Elastic Stack versioned URL paths: ```yml -en/elasticsearch/reference/: *stack +en/elasticsearch/reference/: + product: elastic-stack + legacy_versions: *stack ``` ## Structure @@ -14,11 +16,7 @@ en/elasticsearch/reference/: *stack : Defines a reusable list of version strings for "stack" projects, e.g., [ '9.0+', '8.18', ... ]. `mappings` -: A YAML mapping where each key is a legacy documentation URL path (like `en/apm/agent/java/`) and the value is a list of asciidoc versions that exist for that path. - -:::{important} -The first version in the `mappings` list is treated as the "current" version in documentation version dropdown. -::: - -## Example entry +: A YAML mapping where each key is a legacy documentation URL path (like `en/apm/agent/java/`), and each value is a mapping with: +* `product`: Specifies the product or project type (e.g., `elastic-stack`, `eck`, `ece`, `self-managed`, etc.). Products must be defined in [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/products.yml). See [products.yml](./products.md) for more information. +* `legacy_versions`: A list of version strings that correspond to the available asciidoc pages. diff --git a/docs/configure/site/products.md b/docs/configure/site/products.md new file mode 100644 index 000000000..8d5d9c82e --- /dev/null +++ b/docs/configure/site/products.md @@ -0,0 +1,37 @@ +# `products.yml` + +The [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/products.yml) file specifies metadata regarding the projects in the organization leveraging v3. + +```yml +products: + apm-agent-dotnet: + display: 'APM Agent for .NET' + versioning: 'apm_agent_dotnet' + edot-collector: + display: 'Elastic Distribution of OpenTelemetry Collector' + versioning: 'stack' +#... +``` + +## Structure + +`products` +: A YAML mapping where each key is an Elastic product. +* `display`: A friendly name for the product. +* `versioning`: The versioning system used by the project. The value for this field must match one of the versioning systems defined in [`versions.yml`](https://github.com/elastic/docs-builder/blob/main/config/versions.yml) + + + +## Substitutions + +The following substitutions are available: + +| Substitution | Result | +| --- |---| +| `{{ product.apm-agent-dotnet }}` |{{ product.apm-agent-dotnet }} | +| `{{ .apm-agent-ios }}` | {{ .apm-agent-ios }} | + +## See also + +[](./versions.md) +[](./legacy-url-mappings.md) diff --git a/docs/syntax/version-variables.md b/docs/syntax/version-variables.md index 280c57b18..cb71c0f6d 100644 --- a/docs/syntax/version-variables.md +++ b/docs/syntax/version-variables.md @@ -82,6 +82,8 @@ This is dictated by the [`versions.yml`](https://github.com/elastic/docs-builder * `ech` * `eck` * `ess` +* `esf` +* `search_ui` * `self` * `ecctl` * `curator` @@ -105,6 +107,17 @@ This is dictated by the [`versions.yml`](https://github.com/elastic/docs-builder * `edot_python` * `edot_cf_aws` * `edot_collector` +* `apm_attacher` +* `apm_lambda` +* `ecs_logging_dotnet` +* `ecs_logging_go_logrus` +* `ecs_logging_go_zap` +* `ecs_logging_go_zerolog` +* `ecs_logging_java` +* `ecs_logging_nodejs` +* `ecs_logging_php` +* `ecs_logging_python` +* `ecs_logging_ruby` The following are available but should not be used. These map to serverless projects and have a fixed high version number. From 626c199eb28e0a9398f1dc0c4a4daff87e610c4d Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 06:54:16 -0300 Subject: [PATCH 09/24] Fix legacy version lists with 9.0+ entries --- config/legacy-url-mappings.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 6f6e8c4b5..c6e0ac4b1 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -4,7 +4,7 @@ # Upon creation of version selection dropdowns, this data is combined with the current version of its product. ############################################# -stack: &stack [ '9.0+', '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.17' ] +stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.17' ] mappings: en/apm/agent/android/: @@ -129,7 +129,7 @@ mappings: legacy_versions: [] en/ecs/: product: ecs - legacy_versions: [ '9.0+', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + legacy_versions: [ '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/elastic-stack-glossary/: product: elastic-stack legacy_versions: [] From 806259e45ab14ef7c5f27a277ba0a1eec215b297 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 06:57:02 -0300 Subject: [PATCH 10/24] Standardize with underscores --- config/legacy-url-mappings.yml | 66 ++++++++++---------- config/products.yml | 104 ++++++++++++++++---------------- docs/configure/site/products.md | 12 ++-- 3 files changed, 91 insertions(+), 91 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index c6e0ac4b1..10b75e7ff 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -8,40 +8,40 @@ stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', mappings: en/apm/agent/android/: - product: apm-agent-android + product: apm_agent_android legacy_versions: [ '1.2.0' , '0.x' ] en/apm/agent/dotnet/: - product: apm-agent-dotnet + product: apm_agent_dotnet legacy_versions: [ '1.33.0' ] en/apm/agent/go/: - product: apm-agent-go + product: apm_agent_go legacy_versions: [ '2.7.1', '1.x', '0.5' ] en/apm/agent/java/: - product: apm-agent-java + product: apm_agent_java legacy_versions: ['1.54.0', '0.7', '0.6'] en/apm/agent/nodejs/: - product: apm-agent-node + product: apm_agent_node legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/php/: - product: apm-agent-php + product: apm_agent_php legacy_versions: [ '1.15.1', '1.x' ] en/apm/agent/python/: - product: apm-agent-python + product: apm_agent_python legacy_versions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/ruby/: - product: apm-agent-ruby + product: apm_agent_ruby legacy_versions: [ '4.8.0', '3.x', '2.x', '1.x' ] en/apm/agent/rum-js/: - product: apm-agent-rum-js + product: apm_agent_rum_js legacy_versions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] en/apm/agent/swift/: - product: apm-agent-ios + product: apm_agent_ios legacy_versions: [ '1.2.1', '0.x' ] en/apm/attacher/: - product: apm-k8s-attacher + product: apm_k8s_attacher legacy_versions: [ '1.1.3' ] en/apm/lambda/: - product: apm-aws-lambda + product: apm_aws_lambda legacy_versions: [ '1.5.8' ] en/beats/auditbeat/: product: auditbeat @@ -80,61 +80,61 @@ mappings: product: winlogbeat legacy_versions: *stack en/cloud-heroku/: - product: cloud-hosted + product: cloud_hosted legacy_versions: [] en/cloud-on-k8s/: - product: cloud-kubernetes + product: cloud_kubernetes legacy_versions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/cloud/: - product: cloud-hosted + product: cloud_hosted legacy_versions: [] en/cloud-enterprise/: - product: cloud-enterprise + product: cloud_enterprise legacy_versions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] en/ecctl/: - product: cloud-control-ecctl + product: cloud_control_ecctl legacy_versions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/ecs-logging/: - product: ecs-logging + product: ecs_logging legacy_versions: [] en/ecs-logging/dotnet/: - product: ecs-dotnet + product: ecs_dotnet legacy_versions: [] en/ecs-logging/go-logrus/: - product: ecs-logging-go-logrus + product: ecs_logging_go_logrus legacy_versions: [] en/ecs-logging/go-zap/: - product: ecs-logging-go-zap + product: ecs_logging_go_zap legacy_versions: [] en/ecs-logging/go-zerolog/: - product: ecs-logging-go-zerolog + product: ecs_logging_go_zerolog legacy_versions: [] en/ecs-logging/java/: - product: ecs-logging-java + product: ecs_logging_java legacy_versions: ['0.x'] en/ecs-logging/nodejs/: - product: ecs-logging-nodejs + product: ecs_logging_nodejs legacy_versions: [] en/ecs-logging/overview/: - product: ecs-logging + product: ecs_logging legacy_versions: [] en/ecs-logging/php/: - product: ecs-logging-php + product: ecs_logging_php legacy_versions: [] en/ecs-logging/python/: - product: ecs-logging-python + product: ecs_logging_python legacy_versions: [ '2.2.0' ] en/ecs-logging/ruby/: - product: ecs-logging-ruby + product: ecs_logging_ruby legacy_versions: [] en/ecs/: product: ecs legacy_versions: [ '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/elastic-stack-glossary/: - product: elastic-stack + product: elastic_stack legacy_versions: [] en/elastic-stack/: - product: elastic-stack + product: elastic_stack legacy_versions: *stack en/elasticsearch/client/curator/: product: curator @@ -179,10 +179,10 @@ mappings: product: elasticsearch legacy_versions: *stack en/esf/: - product: elastic-serverless-forwarder + product: elastic_serverless_forwarder legacy_versions: ['1.20.1'] en/fleet/: - product: elastic-agent + product: elastic_agent legacy_versions: *stack en/ingest-overview/: product: elasticsearch @@ -215,7 +215,7 @@ mappings: product: elasticsearch legacy_versions: [] en/search-ui/: - product: search-ui + product: search_ui legacy_versions: ['1.24.1', '1.24.0'] en/security/: product: security diff --git a/config/products.yml b/config/products.yml index 626a62ceb..9c9cf5cb6 100644 --- a/config/products.yml +++ b/config/products.yml @@ -2,46 +2,46 @@ products: apm: display: 'APM' versioning: 'stack' - apm-agent: + apm_agent: display: 'APM Agent' versioning: 'stack' - apm-agent-android: + apm_agent_android: display: 'APM Agent for Android' versioning: 'apm_agent_android' - apm-agent-dotnet: + apm_agent_dotnet: display: 'APM Agent for .NET' versioning: 'apm_agent_dotnet' - apm-agent-go: + apm_agent_go: display: 'APM Agent for Go' versioning: 'apm_agent_go' - apm-agent-ios: + apm_agent_ios: display: 'APM Agent for iOS' versioning: 'apm_agent_ios' - apm-agent-java: + apm_agent_java: display: 'APM Agent for Java' versioning: 'apm_agent_java' - apm-agent-node: + apm_agent_node: display: 'APM Agent for Node' versioning: 'apm_agent_node' - apm-agent-php: + apm_agent_php: display: 'APM Agent for PHP' versioning: 'apm_agent_php' - apm-agent-python: + apm_agent_python: display: 'APM Agent for Python' versioning: 'apm_agent_python' - apm-agent-ruby: + apm_agent_ruby: display: 'APM Agent for Ruby' versioning: 'apm_agent_ruby' - apm-agent-rum-js: + apm_agent_rum_js: display: 'APM RUM JavaScript agent' versioning: 'apm_agent_rum' - apm-k8s-attacher: + apm_k8s_attacher: display: 'APM Attacher for Kubernetes' versioning: 'apm_attacher' - apm-aws-lambda: + apm_aws_lambda: display: 'APM AWS Lambda extension' versioning: 'apm_lambda' - apm-server: + apm_server: display: 'APM Server' versioning: 'stack' auditbeat: @@ -50,22 +50,22 @@ products: beats: display: 'Beats' versioning: 'stack' - cloud-control-ecctl: + cloud_control_ecctl: display: 'Elastic Cloud Control ECCTL' versioning: 'ecctl' - cloud-enterprise: + cloud_enterprise: display: 'Elastic Cloud Enterprise' versioning: 'ece' - cloud-hosted: + cloud_hosted: display: 'Elastic Cloud Hosted' versioning: 'ech' - cloud-kubernetes: + cloud_kubernetes: display: 'Elastic Cloud Kubernetes' versioning: 'eck' - cloud-serverless: + cloud_serverless: display: 'Elastic Cloud Serverless' versioning: 'serverless' - cloud-terraform: + cloud_terraform: display: 'Elastic Cloud Terraform' versioning: 'stack' curator: @@ -74,85 +74,85 @@ products: ecs: display: 'Elastic Common Schema (ECS)' versioning: 'stack' - ecs-logging: + ecs_logging: display: 'ECS Logging' versioning: 'stack' - ecs-dotnet: + ecs_dotnet: display: 'Elastic Common Schema support for .NET' versioning: 'ecs_logging_dotnet' - ecs-logging-go-logrus: + ecs_logging_go_logrus: display: 'Elastic Common Schema (ECS) support for Logrus' versioning: 'ecs_logging_go_logrus' - ecs-logging-go-zap: - display: 'Elastic Common Schema (ECS) support for uber-go/zap logger' + ecs_logging_go_zap: + display: 'Elastic Common Schema (ECS) support for uber_go/zap logger' versioning: 'ecs_logging_go_zap' - ecs-logging-go-zerolog: + ecs_logging_go_zerolog: display: 'Elastic Common Schema (ECS) support for zerolog' versioning: 'ecs_logging_go_zerolog' - ecs-logging-java: - display: 'ECS-based logging for Java applications' + ecs_logging_java: + display: 'ECS_based logging for Java applications' versioning: 'ecs_logging_java' - ecs-logging-nodejs: + ecs_logging_nodejs: display: 'Elastic Common Schema (ECS) support for Node js' versioning: 'ecs_logging_nodejs' - ecs-logging-php: + ecs_logging_php: display: 'ECS Logging for PHP' versioning: 'ecs_logging_php' - ecs-logging-python: + ecs_logging_python: display: 'Elastic Common Schema (ECS) support for Python' versioning: 'ecs_logging_python' - ecs-logging-ruby: + ecs_logging_ruby: display: 'Elastic Common Schema (ECS) support for Ruby' versioning: 'ecs_logging_ruby' - edot-cf: + edot_cf: display: 'EDOT Cloud Forwarder' versioning: 'stack' - edot-sdk: + edot_sdk: display: 'Elastic Distribution of OpenTelemetry SDK' versioning: 'stack' - edot-collector: + edot_collector: display: 'Elastic Distribution of OpenTelemetry Collector' versioning: 'stack' - edot-ios: + edot_ios: display: 'Elastic Distribution of OpenTelemetry iOS' versioning: 'edot_ios' - edot-android: + edot_android: display: 'Elastic Distribution of OpenTelemetry Android' versioning: 'edot_android' - edot-dotnet: + edot_dotnet: display: 'Elastic Distribution of OpenTelemetry .NET' versioning: 'edot_dotnet' - edot-java: + edot_java: display: 'Elastic Distribution of OpenTelemetry Java' versioning: 'edot_java' - edot-node: + edot_node: display: 'Elastic Distribution of OpenTelemetry Node' versioning: 'edot_node' - edot-php: + edot_php: display: 'Elastic Distribution of OpenTelemetry PHP' versioning: 'edot_php' - edot-python: + edot_python: display: 'Elastic Distribution of OpenTelemetry Python' versioning: 'edot_python' - edot-cf-aws: + edot_cf_aws: display: 'EDOT Cloud Forwarder for AWS' versioning: 'edot_cf_aws' eland: display: 'Eland' versioning: 'stack' - elastic-agent: + elastic_agent: display: 'Elastic Agent' versioning: 'stack' - elastic-serverless-forwarder: + elastic_serverless_forwarder: display: 'Elastic Serverless Forwarder' versioning: 'esf' - elastic-stack: + elastic_stack: display: 'Elastic Stack' versioning: 'stack' elasticsearch: display: 'Elasticsearch' versioning: 'stack' - elasticsearch-client: + elasticsearch_client: display: 'Elasticsearch Client' versioning: 'stack' ess: @@ -176,7 +176,7 @@ products: logstash: display: 'Logstash' versioning: 'stack' - machine-learning: + machine_learning: display: 'Machine Learning' versioning: 'stack' metricbeat: @@ -191,7 +191,7 @@ products: painless: display: 'Elasticsearch Painless scripting language' versioning: 'stack' - search-ui: + search_ui: display: 'Search UI' versioning: 'stack' security: @@ -203,13 +203,13 @@ products: serverless: display: 'Elastic Serverless' versioning: 'all' - serverless-elasticsearch: + serverless_elasticsearch: display: 'Elasticsearch' versioning: 'all' - serverless-observability: + serverless_observability: display: 'Elastic Observability' versioning: 'all' - serverless-security: + serverless_security: display: 'Elastic Security' versioning: 'all' winlogbeat: diff --git a/docs/configure/site/products.md b/docs/configure/site/products.md index 8d5d9c82e..759afb151 100644 --- a/docs/configure/site/products.md +++ b/docs/configure/site/products.md @@ -4,10 +4,10 @@ The [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/pr ```yml products: - apm-agent-dotnet: + apm_agent_dotnet: display: 'APM Agent for .NET' versioning: 'apm_agent_dotnet' - edot-collector: + edot_collector: display: 'Elastic Distribution of OpenTelemetry Collector' versioning: 'stack' #... @@ -26,10 +26,10 @@ products: The following substitutions are available: -| Substitution | Result | -| --- |---| -| `{{ product.apm-agent-dotnet }}` |{{ product.apm-agent-dotnet }} | -| `{{ .apm-agent-ios }}` | {{ .apm-agent-ios }} | +| Substitution | Result | +|---------------------------------|---| +| `{{ product.apm_agent_dotnet }}` |{{ product.apm_agent_dotnet }} | +| `{{ .apm_agent_ios }}` | {{ .apm_agent_ios }} | ## See also From 0434e008f73acbfd50756a9f1d153b6b998f9d05 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 06:59:11 -0300 Subject: [PATCH 11/24] Update docs/configure/site/products.md Co-authored-by: Fabrizio Ferri-Benedetti --- docs/configure/site/products.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configure/site/products.md b/docs/configure/site/products.md index 759afb151..8c0be84b8 100644 --- a/docs/configure/site/products.md +++ b/docs/configure/site/products.md @@ -1,6 +1,6 @@ # `products.yml` -The [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/products.yml) file specifies metadata regarding the projects in the organization leveraging v3. +The [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/products.yml) file specifies metadata regarding the projects in the organization that use the V3 docs system. ```yml products: From d4a2167d7d9d37b1059f7551ecd6a68f68baf75a Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 07:10:25 -0300 Subject: [PATCH 12/24] Update docs/configure/site/products.md Fabrizio with the save. Co-authored-by: Fabrizio Ferri-Benedetti --- docs/configure/site/products.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/configure/site/products.md b/docs/configure/site/products.md index 8c0be84b8..132be8480 100644 --- a/docs/configure/site/products.md +++ b/docs/configure/site/products.md @@ -24,12 +24,20 @@ products: ## Substitutions -The following substitutions are available: +Writing `{{ product. }}` renders the friendly name of the product in the documentation. For example: | Substitution | Result | |---------------------------------|---| | `{{ product.apm_agent_dotnet }}` |{{ product.apm_agent_dotnet }} | -| `{{ .apm_agent_ios }}` | {{ .apm_agent_ios }} | +| `{{ product.edot_collector }}` | {{ product.edot_collector }} | + +You can also use the shorthand notation `{{ . }}`. For example: + +| Substitution | Result | +|---------------------------------|---| +| `{{ .apm_agent_dotnet }}` |{{ .apm_agent_dotnet }} | +| `{{ .edot_collector }}` | {{ .edot_collector }} | + ## See also From 03f3ccfeecf960e87edf27db66ecea4d695ee09a Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 12:16:10 -0300 Subject: [PATCH 13/24] Revert to kebab-case product definition and allow compatibility with frontmatter definitions --- config/legacy-url-mappings.yml | 66 ++++++------ config/products.yml | 100 +++++++++--------- .../Builder/ConfigurationFile.cs | 2 +- .../AppliesTo/ApplicableToYamlConverter.cs | 2 +- 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 10b75e7ff..c6e0ac4b1 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -8,40 +8,40 @@ stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', mappings: en/apm/agent/android/: - product: apm_agent_android + product: apm-agent-android legacy_versions: [ '1.2.0' , '0.x' ] en/apm/agent/dotnet/: - product: apm_agent_dotnet + product: apm-agent-dotnet legacy_versions: [ '1.33.0' ] en/apm/agent/go/: - product: apm_agent_go + product: apm-agent-go legacy_versions: [ '2.7.1', '1.x', '0.5' ] en/apm/agent/java/: - product: apm_agent_java + product: apm-agent-java legacy_versions: ['1.54.0', '0.7', '0.6'] en/apm/agent/nodejs/: - product: apm_agent_node + product: apm-agent-node legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/php/: - product: apm_agent_php + product: apm-agent-php legacy_versions: [ '1.15.1', '1.x' ] en/apm/agent/python/: - product: apm_agent_python + product: apm-agent-python legacy_versions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/ruby/: - product: apm_agent_ruby + product: apm-agent-ruby legacy_versions: [ '4.8.0', '3.x', '2.x', '1.x' ] en/apm/agent/rum-js/: - product: apm_agent_rum_js + product: apm-agent-rum-js legacy_versions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] en/apm/agent/swift/: - product: apm_agent_ios + product: apm-agent-ios legacy_versions: [ '1.2.1', '0.x' ] en/apm/attacher/: - product: apm_k8s_attacher + product: apm-k8s-attacher legacy_versions: [ '1.1.3' ] en/apm/lambda/: - product: apm_aws_lambda + product: apm-aws-lambda legacy_versions: [ '1.5.8' ] en/beats/auditbeat/: product: auditbeat @@ -80,61 +80,61 @@ mappings: product: winlogbeat legacy_versions: *stack en/cloud-heroku/: - product: cloud_hosted + product: cloud-hosted legacy_versions: [] en/cloud-on-k8s/: - product: cloud_kubernetes + product: cloud-kubernetes legacy_versions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/cloud/: - product: cloud_hosted + product: cloud-hosted legacy_versions: [] en/cloud-enterprise/: - product: cloud_enterprise + product: cloud-enterprise legacy_versions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] en/ecctl/: - product: cloud_control_ecctl + product: cloud-control-ecctl legacy_versions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/ecs-logging/: - product: ecs_logging + product: ecs-logging legacy_versions: [] en/ecs-logging/dotnet/: - product: ecs_dotnet + product: ecs-dotnet legacy_versions: [] en/ecs-logging/go-logrus/: - product: ecs_logging_go_logrus + product: ecs-logging-go-logrus legacy_versions: [] en/ecs-logging/go-zap/: - product: ecs_logging_go_zap + product: ecs-logging-go-zap legacy_versions: [] en/ecs-logging/go-zerolog/: - product: ecs_logging_go_zerolog + product: ecs-logging-go-zerolog legacy_versions: [] en/ecs-logging/java/: - product: ecs_logging_java + product: ecs-logging-java legacy_versions: ['0.x'] en/ecs-logging/nodejs/: - product: ecs_logging_nodejs + product: ecs-logging-nodejs legacy_versions: [] en/ecs-logging/overview/: - product: ecs_logging + product: ecs-logging legacy_versions: [] en/ecs-logging/php/: - product: ecs_logging_php + product: ecs-logging-php legacy_versions: [] en/ecs-logging/python/: - product: ecs_logging_python + product: ecs-logging-python legacy_versions: [ '2.2.0' ] en/ecs-logging/ruby/: - product: ecs_logging_ruby + product: ecs-logging-ruby legacy_versions: [] en/ecs/: product: ecs legacy_versions: [ '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/elastic-stack-glossary/: - product: elastic_stack + product: elastic-stack legacy_versions: [] en/elastic-stack/: - product: elastic_stack + product: elastic-stack legacy_versions: *stack en/elasticsearch/client/curator/: product: curator @@ -179,10 +179,10 @@ mappings: product: elasticsearch legacy_versions: *stack en/esf/: - product: elastic_serverless_forwarder + product: elastic-serverless-forwarder legacy_versions: ['1.20.1'] en/fleet/: - product: elastic_agent + product: elastic-agent legacy_versions: *stack en/ingest-overview/: product: elasticsearch @@ -215,7 +215,7 @@ mappings: product: elasticsearch legacy_versions: [] en/search-ui/: - product: search_ui + product: search-ui legacy_versions: ['1.24.1', '1.24.0'] en/security/: product: security diff --git a/config/products.yml b/config/products.yml index 9c9cf5cb6..4877d3cd0 100644 --- a/config/products.yml +++ b/config/products.yml @@ -2,46 +2,46 @@ products: apm: display: 'APM' versioning: 'stack' - apm_agent: + apm-agent: display: 'APM Agent' versioning: 'stack' - apm_agent_android: + apm-agent-android: display: 'APM Agent for Android' versioning: 'apm_agent_android' - apm_agent_dotnet: + apm-agent-dotnet: display: 'APM Agent for .NET' versioning: 'apm_agent_dotnet' - apm_agent_go: + apm-agent-go: display: 'APM Agent for Go' versioning: 'apm_agent_go' - apm_agent_ios: + apm-agent-ios: display: 'APM Agent for iOS' versioning: 'apm_agent_ios' - apm_agent_java: + apm-agent-java: display: 'APM Agent for Java' versioning: 'apm_agent_java' - apm_agent_node: + apm-agent-node: display: 'APM Agent for Node' versioning: 'apm_agent_node' - apm_agent_php: + apm-agent-php: display: 'APM Agent for PHP' versioning: 'apm_agent_php' - apm_agent_python: + apm-agent-python: display: 'APM Agent for Python' versioning: 'apm_agent_python' - apm_agent_ruby: + apm-agent-ruby: display: 'APM Agent for Ruby' versioning: 'apm_agent_ruby' - apm_agent_rum_js: + apm-agent-rum-js: display: 'APM RUM JavaScript agent' versioning: 'apm_agent_rum' - apm_k8s_attacher: + apm-k8s-attacher: display: 'APM Attacher for Kubernetes' versioning: 'apm_attacher' - apm_aws_lambda: + apm-aws-lambda: display: 'APM AWS Lambda extension' versioning: 'apm_lambda' - apm_server: + apm-server: display: 'APM Server' versioning: 'stack' auditbeat: @@ -50,22 +50,22 @@ products: beats: display: 'Beats' versioning: 'stack' - cloud_control_ecctl: + cloud-control-ecctl: display: 'Elastic Cloud Control ECCTL' versioning: 'ecctl' - cloud_enterprise: + cloud-enterprise: display: 'Elastic Cloud Enterprise' versioning: 'ece' - cloud_hosted: + cloud-hosted: display: 'Elastic Cloud Hosted' versioning: 'ech' - cloud_kubernetes: + cloud-kubernetes: display: 'Elastic Cloud Kubernetes' versioning: 'eck' - cloud_serverless: + cloud-serverless: display: 'Elastic Cloud Serverless' versioning: 'serverless' - cloud_terraform: + cloud-terraform: display: 'Elastic Cloud Terraform' versioning: 'stack' curator: @@ -74,85 +74,85 @@ products: ecs: display: 'Elastic Common Schema (ECS)' versioning: 'stack' - ecs_logging: + ecs-logging: display: 'ECS Logging' versioning: 'stack' - ecs_dotnet: + ecs-dotnet: display: 'Elastic Common Schema support for .NET' versioning: 'ecs_logging_dotnet' - ecs_logging_go_logrus: + ecs-logging-go-logrus: display: 'Elastic Common Schema (ECS) support for Logrus' versioning: 'ecs_logging_go_logrus' - ecs_logging_go_zap: + ecs-logging-go-zap: display: 'Elastic Common Schema (ECS) support for uber_go/zap logger' versioning: 'ecs_logging_go_zap' - ecs_logging_go_zerolog: + ecs-logging-go-zerolog: display: 'Elastic Common Schema (ECS) support for zerolog' versioning: 'ecs_logging_go_zerolog' - ecs_logging_java: + ecs-logging-java: display: 'ECS_based logging for Java applications' versioning: 'ecs_logging_java' - ecs_logging_nodejs: + ecs-logging-nodejs: display: 'Elastic Common Schema (ECS) support for Node js' versioning: 'ecs_logging_nodejs' - ecs_logging_php: + ecs-logging-php: display: 'ECS Logging for PHP' versioning: 'ecs_logging_php' - ecs_logging_python: + ecs-logging-python: display: 'Elastic Common Schema (ECS) support for Python' versioning: 'ecs_logging_python' - ecs_logging_ruby: + ecs-logging-ruby: display: 'Elastic Common Schema (ECS) support for Ruby' versioning: 'ecs_logging_ruby' - edot_cf: + edot-cf: display: 'EDOT Cloud Forwarder' versioning: 'stack' - edot_sdk: + edot-sdk: display: 'Elastic Distribution of OpenTelemetry SDK' versioning: 'stack' - edot_collector: + edot-collector: display: 'Elastic Distribution of OpenTelemetry Collector' versioning: 'stack' - edot_ios: + edot-ios: display: 'Elastic Distribution of OpenTelemetry iOS' versioning: 'edot_ios' - edot_android: + edot-android: display: 'Elastic Distribution of OpenTelemetry Android' versioning: 'edot_android' - edot_dotnet: + edot-dotnet: display: 'Elastic Distribution of OpenTelemetry .NET' versioning: 'edot_dotnet' - edot_java: + edot-java: display: 'Elastic Distribution of OpenTelemetry Java' versioning: 'edot_java' - edot_node: + edot-node: display: 'Elastic Distribution of OpenTelemetry Node' versioning: 'edot_node' - edot_php: + edot-php: display: 'Elastic Distribution of OpenTelemetry PHP' versioning: 'edot_php' - edot_python: + edot-python: display: 'Elastic Distribution of OpenTelemetry Python' versioning: 'edot_python' - edot_cf_aws: + edot-cf-aws: display: 'EDOT Cloud Forwarder for AWS' versioning: 'edot_cf_aws' eland: display: 'Eland' versioning: 'stack' - elastic_agent: + elastic-agent: display: 'Elastic Agent' versioning: 'stack' - elastic_serverless_forwarder: + elastic-serverless-forwarder: display: 'Elastic Serverless Forwarder' versioning: 'esf' - elastic_stack: + elastic-stack: display: 'Elastic Stack' versioning: 'stack' elasticsearch: display: 'Elasticsearch' versioning: 'stack' - elasticsearch_client: + elasticsearch-client: display: 'Elasticsearch Client' versioning: 'stack' ess: @@ -176,7 +176,7 @@ products: logstash: display: 'Logstash' versioning: 'stack' - machine_learning: + machine-learning: display: 'Machine Learning' versioning: 'stack' metricbeat: @@ -191,7 +191,7 @@ products: painless: display: 'Elasticsearch Painless scripting language' versioning: 'stack' - search_ui: + search-ui: display: 'Search UI' versioning: 'stack' security: @@ -203,13 +203,13 @@ products: serverless: display: 'Elastic Serverless' versioning: 'all' - serverless_elasticsearch: + serverless-elasticsearch: display: 'Elasticsearch' versioning: 'all' - serverless_observability: + serverless-observability: display: 'Elastic Observability' versioning: 'all' - serverless_security: + serverless-security: display: 'Elastic Security' versioning: 'all' winlogbeat: diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index 5b5348bdc..50614df53 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -149,7 +149,7 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration break; } - if (!productsConfig.Products.TryGetValue(productId.Value, out var productToAdd)) + if (!productsConfig.Products.TryGetValue(productId.Value.Replace('_', '-'), out var productToAdd)) reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(productsConfig.Products.Select(p => p.Value.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); else _ = Products.Add(productToAdd); diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index 23eb895e8..7d3aac229 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -41,7 +41,7 @@ .. productKeys if (deserialized is not Dictionary { Count: > 0 } dictionary) return null; - var keys = dictionary.Keys.OfType().ToArray(); + var keys = dictionary.Keys.OfType().Select(x => x.Replace('_', '-')).ToArray(); var oldStyleKeys = keys.Where(k => k.StartsWith(':')).ToList(); if (oldStyleKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not use valid yaml keys: {string.Join(", ", oldStyleKeys)}")); From c0130e447f0662e1e3e6505bc20f4546ccba0bdf Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 12:23:27 -0300 Subject: [PATCH 14/24] Fix lint --- .../IDocumentationConfigurationContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs index 709e35885..4a44881cc 100644 --- a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs +++ b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs @@ -2,9 +2,9 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; -using Elastic.Documentation.Configuration.LegacyUrlMappings; namespace Elastic.Documentation.Configuration; From 7ee832ea2a8f267e4219e308c8db75c4d3e3e680 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 10 Sep 2025 12:44:08 -0300 Subject: [PATCH 15/24] Standardize on underscores as it is more widely used by writers --- config/legacy-url-mappings.yml | 66 ++++++------ config/products.yml | 100 +++++++++--------- .../Builder/ConfigurationFile.cs | 2 +- .../AppliesTo/ApplicableToYamlConverter.cs | 2 +- .../Myst/FrontMatter/Products.cs | 2 +- .../Legacy/PageLegacyUrlMapper.cs | 2 +- 6 files changed, 87 insertions(+), 87 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index c6e0ac4b1..10b75e7ff 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -8,40 +8,40 @@ stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', mappings: en/apm/agent/android/: - product: apm-agent-android + product: apm_agent_android legacy_versions: [ '1.2.0' , '0.x' ] en/apm/agent/dotnet/: - product: apm-agent-dotnet + product: apm_agent_dotnet legacy_versions: [ '1.33.0' ] en/apm/agent/go/: - product: apm-agent-go + product: apm_agent_go legacy_versions: [ '2.7.1', '1.x', '0.5' ] en/apm/agent/java/: - product: apm-agent-java + product: apm_agent_java legacy_versions: ['1.54.0', '0.7', '0.6'] en/apm/agent/nodejs/: - product: apm-agent-node + product: apm_agent_node legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/php/: - product: apm-agent-php + product: apm_agent_php legacy_versions: [ '1.15.1', '1.x' ] en/apm/agent/python/: - product: apm-agent-python + product: apm_agent_python legacy_versions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/ruby/: - product: apm-agent-ruby + product: apm_agent_ruby legacy_versions: [ '4.8.0', '3.x', '2.x', '1.x' ] en/apm/agent/rum-js/: - product: apm-agent-rum-js + product: apm_agent_rum_js legacy_versions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] en/apm/agent/swift/: - product: apm-agent-ios + product: apm_agent_ios legacy_versions: [ '1.2.1', '0.x' ] en/apm/attacher/: - product: apm-k8s-attacher + product: apm_k8s_attacher legacy_versions: [ '1.1.3' ] en/apm/lambda/: - product: apm-aws-lambda + product: apm_aws_lambda legacy_versions: [ '1.5.8' ] en/beats/auditbeat/: product: auditbeat @@ -80,61 +80,61 @@ mappings: product: winlogbeat legacy_versions: *stack en/cloud-heroku/: - product: cloud-hosted + product: cloud_hosted legacy_versions: [] en/cloud-on-k8s/: - product: cloud-kubernetes + product: cloud_kubernetes legacy_versions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/cloud/: - product: cloud-hosted + product: cloud_hosted legacy_versions: [] en/cloud-enterprise/: - product: cloud-enterprise + product: cloud_enterprise legacy_versions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] en/ecctl/: - product: cloud-control-ecctl + product: cloud_control_ecctl legacy_versions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/ecs-logging/: - product: ecs-logging + product: ecs_logging legacy_versions: [] en/ecs-logging/dotnet/: - product: ecs-dotnet + product: ecs_dotnet legacy_versions: [] en/ecs-logging/go-logrus/: - product: ecs-logging-go-logrus + product: ecs_logging_go_logrus legacy_versions: [] en/ecs-logging/go-zap/: - product: ecs-logging-go-zap + product: ecs_logging_go_zap legacy_versions: [] en/ecs-logging/go-zerolog/: - product: ecs-logging-go-zerolog + product: ecs_logging_go_zerolog legacy_versions: [] en/ecs-logging/java/: - product: ecs-logging-java + product: ecs_logging_java legacy_versions: ['0.x'] en/ecs-logging/nodejs/: - product: ecs-logging-nodejs + product: ecs_logging_nodejs legacy_versions: [] en/ecs-logging/overview/: - product: ecs-logging + product: ecs_logging legacy_versions: [] en/ecs-logging/php/: - product: ecs-logging-php + product: ecs_logging_php legacy_versions: [] en/ecs-logging/python/: - product: ecs-logging-python + product: ecs_logging_python legacy_versions: [ '2.2.0' ] en/ecs-logging/ruby/: - product: ecs-logging-ruby + product: ecs_logging_ruby legacy_versions: [] en/ecs/: product: ecs legacy_versions: [ '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/elastic-stack-glossary/: - product: elastic-stack + product: elastic_stack legacy_versions: [] en/elastic-stack/: - product: elastic-stack + product: elastic_stack legacy_versions: *stack en/elasticsearch/client/curator/: product: curator @@ -179,10 +179,10 @@ mappings: product: elasticsearch legacy_versions: *stack en/esf/: - product: elastic-serverless-forwarder + product: elastic_serverless_forwarder legacy_versions: ['1.20.1'] en/fleet/: - product: elastic-agent + product: elastic_agent legacy_versions: *stack en/ingest-overview/: product: elasticsearch @@ -215,7 +215,7 @@ mappings: product: elasticsearch legacy_versions: [] en/search-ui/: - product: search-ui + product: search_ui legacy_versions: ['1.24.1', '1.24.0'] en/security/: product: security diff --git a/config/products.yml b/config/products.yml index 4877d3cd0..9c9cf5cb6 100644 --- a/config/products.yml +++ b/config/products.yml @@ -2,46 +2,46 @@ products: apm: display: 'APM' versioning: 'stack' - apm-agent: + apm_agent: display: 'APM Agent' versioning: 'stack' - apm-agent-android: + apm_agent_android: display: 'APM Agent for Android' versioning: 'apm_agent_android' - apm-agent-dotnet: + apm_agent_dotnet: display: 'APM Agent for .NET' versioning: 'apm_agent_dotnet' - apm-agent-go: + apm_agent_go: display: 'APM Agent for Go' versioning: 'apm_agent_go' - apm-agent-ios: + apm_agent_ios: display: 'APM Agent for iOS' versioning: 'apm_agent_ios' - apm-agent-java: + apm_agent_java: display: 'APM Agent for Java' versioning: 'apm_agent_java' - apm-agent-node: + apm_agent_node: display: 'APM Agent for Node' versioning: 'apm_agent_node' - apm-agent-php: + apm_agent_php: display: 'APM Agent for PHP' versioning: 'apm_agent_php' - apm-agent-python: + apm_agent_python: display: 'APM Agent for Python' versioning: 'apm_agent_python' - apm-agent-ruby: + apm_agent_ruby: display: 'APM Agent for Ruby' versioning: 'apm_agent_ruby' - apm-agent-rum-js: + apm_agent_rum_js: display: 'APM RUM JavaScript agent' versioning: 'apm_agent_rum' - apm-k8s-attacher: + apm_k8s_attacher: display: 'APM Attacher for Kubernetes' versioning: 'apm_attacher' - apm-aws-lambda: + apm_aws_lambda: display: 'APM AWS Lambda extension' versioning: 'apm_lambda' - apm-server: + apm_server: display: 'APM Server' versioning: 'stack' auditbeat: @@ -50,22 +50,22 @@ products: beats: display: 'Beats' versioning: 'stack' - cloud-control-ecctl: + cloud_control_ecctl: display: 'Elastic Cloud Control ECCTL' versioning: 'ecctl' - cloud-enterprise: + cloud_enterprise: display: 'Elastic Cloud Enterprise' versioning: 'ece' - cloud-hosted: + cloud_hosted: display: 'Elastic Cloud Hosted' versioning: 'ech' - cloud-kubernetes: + cloud_kubernetes: display: 'Elastic Cloud Kubernetes' versioning: 'eck' - cloud-serverless: + cloud_serverless: display: 'Elastic Cloud Serverless' versioning: 'serverless' - cloud-terraform: + cloud_terraform: display: 'Elastic Cloud Terraform' versioning: 'stack' curator: @@ -74,85 +74,85 @@ products: ecs: display: 'Elastic Common Schema (ECS)' versioning: 'stack' - ecs-logging: + ecs_logging: display: 'ECS Logging' versioning: 'stack' - ecs-dotnet: + ecs_dotnet: display: 'Elastic Common Schema support for .NET' versioning: 'ecs_logging_dotnet' - ecs-logging-go-logrus: + ecs_logging_go_logrus: display: 'Elastic Common Schema (ECS) support for Logrus' versioning: 'ecs_logging_go_logrus' - ecs-logging-go-zap: + ecs_logging_go_zap: display: 'Elastic Common Schema (ECS) support for uber_go/zap logger' versioning: 'ecs_logging_go_zap' - ecs-logging-go-zerolog: + ecs_logging_go_zerolog: display: 'Elastic Common Schema (ECS) support for zerolog' versioning: 'ecs_logging_go_zerolog' - ecs-logging-java: + ecs_logging_java: display: 'ECS_based logging for Java applications' versioning: 'ecs_logging_java' - ecs-logging-nodejs: + ecs_logging_nodejs: display: 'Elastic Common Schema (ECS) support for Node js' versioning: 'ecs_logging_nodejs' - ecs-logging-php: + ecs_logging_php: display: 'ECS Logging for PHP' versioning: 'ecs_logging_php' - ecs-logging-python: + ecs_logging_python: display: 'Elastic Common Schema (ECS) support for Python' versioning: 'ecs_logging_python' - ecs-logging-ruby: + ecs_logging_ruby: display: 'Elastic Common Schema (ECS) support for Ruby' versioning: 'ecs_logging_ruby' - edot-cf: + edot_cf: display: 'EDOT Cloud Forwarder' versioning: 'stack' - edot-sdk: + edot_sdk: display: 'Elastic Distribution of OpenTelemetry SDK' versioning: 'stack' - edot-collector: + edot_collector: display: 'Elastic Distribution of OpenTelemetry Collector' versioning: 'stack' - edot-ios: + edot_ios: display: 'Elastic Distribution of OpenTelemetry iOS' versioning: 'edot_ios' - edot-android: + edot_android: display: 'Elastic Distribution of OpenTelemetry Android' versioning: 'edot_android' - edot-dotnet: + edot_dotnet: display: 'Elastic Distribution of OpenTelemetry .NET' versioning: 'edot_dotnet' - edot-java: + edot_java: display: 'Elastic Distribution of OpenTelemetry Java' versioning: 'edot_java' - edot-node: + edot_node: display: 'Elastic Distribution of OpenTelemetry Node' versioning: 'edot_node' - edot-php: + edot_php: display: 'Elastic Distribution of OpenTelemetry PHP' versioning: 'edot_php' - edot-python: + edot_python: display: 'Elastic Distribution of OpenTelemetry Python' versioning: 'edot_python' - edot-cf-aws: + edot_cf_aws: display: 'EDOT Cloud Forwarder for AWS' versioning: 'edot_cf_aws' eland: display: 'Eland' versioning: 'stack' - elastic-agent: + elastic_agent: display: 'Elastic Agent' versioning: 'stack' - elastic-serverless-forwarder: + elastic_serverless_forwarder: display: 'Elastic Serverless Forwarder' versioning: 'esf' - elastic-stack: + elastic_stack: display: 'Elastic Stack' versioning: 'stack' elasticsearch: display: 'Elasticsearch' versioning: 'stack' - elasticsearch-client: + elasticsearch_client: display: 'Elasticsearch Client' versioning: 'stack' ess: @@ -176,7 +176,7 @@ products: logstash: display: 'Logstash' versioning: 'stack' - machine-learning: + machine_learning: display: 'Machine Learning' versioning: 'stack' metricbeat: @@ -191,7 +191,7 @@ products: painless: display: 'Elasticsearch Painless scripting language' versioning: 'stack' - search-ui: + search_ui: display: 'Search UI' versioning: 'stack' security: @@ -203,13 +203,13 @@ products: serverless: display: 'Elastic Serverless' versioning: 'all' - serverless-elasticsearch: + serverless_elasticsearch: display: 'Elasticsearch' versioning: 'all' - serverless-observability: + serverless_observability: display: 'Elastic Observability' versioning: 'all' - serverless-security: + serverless_security: display: 'Elastic Security' versioning: 'all' winlogbeat: diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index 50614df53..9b9c1ef80 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -149,7 +149,7 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration break; } - if (!productsConfig.Products.TryGetValue(productId.Value.Replace('_', '-'), out var productToAdd)) + if (!productsConfig.Products.TryGetValue(productId.Value.Replace('-', '_'), out var productToAdd)) reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(productsConfig.Products.Select(p => p.Value.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); else _ = Products.Add(productToAdd); diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index 7d3aac229..d4a2309bc 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -41,7 +41,7 @@ .. productKeys if (deserialized is not Dictionary { Count: > 0 } dictionary) return null; - var keys = dictionary.Keys.OfType().Select(x => x.Replace('_', '-')).ToArray(); + var keys = dictionary.Keys.OfType().Select(x => x.Replace('-', '_')).ToArray(); var oldStyleKeys = keys.Where(k => k.StartsWith(':')).ToList(); if (oldStyleKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not use valid yaml keys: {string.Join(", ", oldStyleKeys)}")); diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index 74fb99628..70a4bd62a 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -40,7 +40,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria if (string.IsNullOrWhiteSpace(productId)) throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm", products); - if (products.Products.TryGetValue(productId, out var product)) + if (products.Products.TryGetValue(productId.Replace('-', '_'), out var product)) return product; throw new InvalidProductException(productId, products); diff --git a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs index 08b883bc4..9caa85a81 100644 --- a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs +++ b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs @@ -30,7 +30,7 @@ public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, VersionsConfigur if (LegacyUrlMappings.Mappings.FirstOrDefault(x => mappedPage.Contains(x.BaseUrl, StringComparison.OrdinalIgnoreCase)) is not { } legacyMappingMatch) { - return [new LegacyPageMapping(LegacyUrlMappings.Mappings.First(x => x.Product.Id.Equals("elastic-stack", StringComparison.InvariantCultureIgnoreCase)).Product, mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; + return [new LegacyPageMapping(LegacyUrlMappings.Mappings.First(x => x.Product.Id.Equals("elastic_stack", StringComparison.InvariantCultureIgnoreCase)).Product, mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; } var allVersions = new List(); From 1234a84adc2b3df1fa171b162a06ac9c26ffd84e Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 11:38:58 -0300 Subject: [PATCH 16/24] Update config/products.yml Co-authored-by: florent-leborgne --- config/products.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/products.yml b/config/products.yml index 9c9cf5cb6..692e98a48 100644 --- a/config/products.yml +++ b/config/products.yml @@ -198,7 +198,7 @@ products: display: 'Elastic Security' versioning: 'stack' self: - display: 'Elastic Stack' + display: 'Self-managed Elastic' versioning: 'stack' serverless: display: 'Elastic Serverless' From 8f7e77d9f1540ebfe00a2cca8b921ed7cef333c9 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 11:54:25 -0300 Subject: [PATCH 17/24] Remove obsolete EqualityComparer --- .../Builder/ConfigurationFile.cs | 2 +- .../Products/Product.cs | 20 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index 9b9c1ef80..4e9f92eb8 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -37,7 +37,7 @@ public record ConfigurationFile : ITableOfContentsScope public Dictionary? Redirects { get; } - public HashSet Products { get; } = new(new ProductEqualityComparer()); + public HashSet Products { get; } = []; public HashSet ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase); diff --git a/src/Elastic.Documentation.Configuration/Products/Product.cs b/src/Elastic.Documentation.Configuration/Products/Product.cs index 7906da969..9dabeb1eb 100644 --- a/src/Elastic.Documentation.Configuration/Products/Product.cs +++ b/src/Elastic.Documentation.Configuration/Products/Product.cs @@ -21,23 +21,3 @@ public record Product public VersioningSystem? VersioningSystem { get; init; } } -public sealed class ProductEqualityComparer : IEqualityComparer, IComparer -{ - public bool Equals(Product? x, Product? y) => x?.Id == y?.Id; - public int GetHashCode(Product obj) => obj.Id.GetHashCode(); - - public int Compare(Product? x, Product? y) - { - if (ReferenceEquals(x, y)) - return 0; - if (y is null) - return 1; - if (x is null) - return -1; - var idComparison = string.Compare(x.Id, y.Id, StringComparison.OrdinalIgnoreCase); - if (idComparison != 0) - return idComparison; - var displayNameComparison = string.Compare(x.DisplayName, y.DisplayName, StringComparison.OrdinalIgnoreCase); - return displayNameComparison != 0 ? displayNameComparison : x.VersioningSystem?.Current.CompareTo(y.VersioningSystem?.Current) ?? 0; - } -} From 017525351f3b6929b755021828ad3b708fde3f78 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 11:55:10 -0300 Subject: [PATCH 18/24] Adjust product listing --- config/legacy-url-mappings.yml | 2 +- config/products.yml | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 10b75e7ff..d244aaa81 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -221,7 +221,7 @@ mappings: product: security legacy_versions: *stack en/serverless/: - product: serverless + product: cloud_serverless legacy_versions: [] en/starting-with-the-elasticsearch-platform-and-its-solutions/: product: elasticsearch diff --git a/config/products.yml b/config/products.yml index 692e98a48..4de211473 100644 --- a/config/products.yml +++ b/config/products.yml @@ -200,17 +200,14 @@ products: self: display: 'Self-managed Elastic' versioning: 'stack' - serverless: - display: 'Elastic Serverless' - versioning: 'all' serverless_elasticsearch: - display: 'Elasticsearch' + display: 'Elasticsearch Serverless' versioning: 'all' serverless_observability: - display: 'Elastic Observability' + display: 'Elastic Observability Serverless' versioning: 'all' serverless_security: - display: 'Elastic Security' + display: 'Elastic Security Serverless' versioning: 'all' winlogbeat: display: 'Winlogbeat' From 4627aecc4a7553d9f8d71f8392c76b8773fe44de Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 11:57:01 -0300 Subject: [PATCH 19/24] Minor clean-ups --- src/Elastic.Markdown/HtmlWriter.cs | 1 - src/Elastic.Markdown/Myst/FrontMatter/Products.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index 0841b4fff..75c41f943 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -5,7 +5,6 @@ using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration.LegacyUrlMappings; -using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index 70a4bd62a..735eee59e 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -4,7 +4,6 @@ using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Suggestions; -using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; From a69fa1e6824acee33171b77a62230f30eb67b50e Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 12:13:17 -0300 Subject: [PATCH 20/24] Adding non-product entries back --- .../AppliesTo/ApplicableToYamlConverter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index d4a2309bc..a0cbda648 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -14,7 +14,9 @@ public class ApplicableToYamlConverter(IReadOnlyCollection productKeys) { private readonly string[] _knownKeys = [ - "stack", "deployment", "product", "ece", "eck", "ess", "ecctl", + "stack", "deployment", "serverless", "product", // Applicability categories + "ece", "eck", "ess", "self", // Deployment options + "elasticsearch", "observability", "security", // Serverless flavors .. productKeys ]; From a0c2bc173792f6221de506857ac39fdf12a8d21c Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 12:19:16 -0300 Subject: [PATCH 21/24] Remove duplicate version entries between versions.yml and the legacy url mappings --- config/legacy-url-mappings.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index d244aaa81..477cbcc58 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -9,40 +9,40 @@ stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', mappings: en/apm/agent/android/: product: apm_agent_android - legacy_versions: [ '1.2.0' , '0.x' ] + legacy_versions: [ '0.x' ] en/apm/agent/dotnet/: product: apm_agent_dotnet legacy_versions: [ '1.33.0' ] en/apm/agent/go/: product: apm_agent_go - legacy_versions: [ '2.7.1', '1.x', '0.5' ] + legacy_versions: [ '1.x', '0.5' ] en/apm/agent/java/: product: apm_agent_java - legacy_versions: ['1.54.0', '0.7', '0.6'] + legacy_versions: [ '1.54.0', '0.7', '0.6'] en/apm/agent/nodejs/: product: apm_agent_node legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/php/: product: apm_agent_php - legacy_versions: [ '1.15.1', '1.x' ] + legacy_versions: [ '1.x' ] en/apm/agent/python/: product: apm_agent_python - legacy_versions: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] + legacy_versions: [ '5.x', '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/ruby/: product: apm_agent_ruby - legacy_versions: [ '4.8.0', '3.x', '2.x', '1.x' ] + legacy_versions: [ '3.x', '2.x', '1.x' ] en/apm/agent/rum-js/: product: apm_agent_rum_js - legacy_versions: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] + legacy_versions: [ '4.x', '3.x', '2.x', '1.x', '0.x' ] en/apm/agent/swift/: product: apm_agent_ios legacy_versions: [ '1.2.1', '0.x' ] en/apm/attacher/: product: apm_k8s_attacher - legacy_versions: [ '1.1.3' ] + legacy_versions: [] en/apm/lambda/: product: apm_aws_lambda - legacy_versions: [ '1.5.8' ] + legacy_versions: [] en/beats/auditbeat/: product: auditbeat legacy_versions: *stack @@ -180,7 +180,7 @@ mappings: legacy_versions: *stack en/esf/: product: elastic_serverless_forwarder - legacy_versions: ['1.20.1'] + legacy_versions: [] en/fleet/: product: elastic_agent legacy_versions: *stack From f06444316810619d95c2fccc36a661924fa9b008 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 11 Sep 2025 23:17:45 -0300 Subject: [PATCH 22/24] FIx tests and ecctl definition --- config/legacy-url-mappings.yml | 2 +- config/products.yml | 2 +- tests/authoring/Framework/Setup.fs | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 477cbcc58..eb4a1d35c 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -92,7 +92,7 @@ mappings: product: cloud_enterprise legacy_versions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] en/ecctl/: - product: cloud_control_ecctl + product: ecctl legacy_versions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/ecs-logging/: product: ecs_logging diff --git a/config/products.yml b/config/products.yml index 4de211473..8c75b62a7 100644 --- a/config/products.yml +++ b/config/products.yml @@ -50,7 +50,7 @@ products: beats: display: 'Beats' versioning: 'stack' - cloud_control_ecctl: + ecctl: display: 'Elastic Cloud Control ECCTL' versioning: 'ecctl' cloud_enterprise: diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index 9037de0b7..0a7450da9 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -217,6 +217,12 @@ type Setup = productDict.Add("elasticsearch", Product(Id = "elasticsearch", DisplayName = "Elasticsearch", VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.ElasticsearchProject])) + productDict.Add("apm_agent_dotnet", Product(Id = "apm_agent_dotnet", + DisplayName = "APM Agent for .NET", + VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.ApmAgentDotnet])) + productDict.Add("ecctl", Product(Id = "ecctl", + DisplayName = "Elastic Cloud Control ECCTL", + VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.Ecctl])) let configurationFileProvider = ConfigurationFileProvider(fileSystem) let configurationContext = ConfigurationContext( From daa393c49ecf5c9cd725db5ba43847a976c4ec98 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Fri, 12 Sep 2025 12:14:54 -0300 Subject: [PATCH 23/24] Use a static deserializer through the same assembly --- .../Assembler/AssemblyConfiguration.cs | 6 +----- .../ConfigurationFileProvider.cs | 2 +- .../LegacyUrlMappings/LegacyUrlMappingExtensions.cs | 2 +- .../Products/ProductExtensions.cs | 2 +- .../Versions/VersionsConfigurationExtensions.cs | 2 +- src/Elastic.Markdown/Myst/YamlSerialization.cs | 2 -- 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs b/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs index 240aa9ed3..a98685e16 100644 --- a/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs @@ -18,13 +18,9 @@ public static AssemblyConfiguration Deserialize(string yaml, bool skipPrivateRep { var input = new StringReader(yaml); - var deserializer = new StaticDeserializerBuilder(new YamlStaticContext()) - .IgnoreUnmatchedProperties() - .Build(); - try { - var config = deserializer.Deserialize(input); + var config = ConfigurationFileProvider.Deserializer.Deserialize(input); foreach (var (name, r) in config.ReferenceRepositories) { var repository = RepositoryDefaults(r, name); diff --git a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs index 762807896..656682e5b 100644 --- a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs +++ b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs @@ -26,7 +26,7 @@ public partial class ConfigurationFileProvider private readonly IFileSystem _fileSystem; private readonly string _assemblyName; - internal IDeserializer Deserializer { get; } = new StaticDeserializerBuilder(new YamlStaticContext()) + internal static IDeserializer Deserializer { get; } = new StaticDeserializerBuilder(new YamlStaticContext()) .WithNamingConvention(UnderscoredNamingConvention.Instance) .Build(); diff --git a/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs index 3a61f43f0..07760d8ed 100644 --- a/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs +++ b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs @@ -13,7 +13,7 @@ public static LegacyUrlMappingConfiguration CreateLegacyUrlMappings(this Configu { var legacyUrlMappingsFilePath = provider.LegacyUrlMappingsFile; - var legacyUrlMappingsDto = provider.Deserializer.Deserialize(legacyUrlMappingsFilePath.OpenText()); + var legacyUrlMappingsDto = ConfigurationFileProvider.Deserializer.Deserialize(legacyUrlMappingsFilePath.OpenText()); var legacyUrlMappings = legacyUrlMappingsDto.Mappings.Select(kvp => new LegacyUrlMapping diff --git a/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs index 8f6a8ca55..96cd8d5ae 100644 --- a/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs @@ -13,7 +13,7 @@ public static ProductsConfiguration CreateProducts(this ConfigurationFileProvide { var productsFilePath = provider.ProductsFile; - var productsDto = provider.Deserializer.Deserialize(productsFilePath.OpenText()); + var productsDto = ConfigurationFileProvider.Deserializer.Deserialize(productsFilePath.OpenText()); var products = productsDto.Products.ToDictionary( kvp => kvp.Key, diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs index 430deab43..00ae91b88 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs @@ -10,7 +10,7 @@ public static VersionsConfiguration CreateVersionConfiguration(this Configuratio { var versionFilePath = provider.VersionFile; - var versionsDto = provider.Deserializer.Deserialize(versionFilePath.OpenText()); + var versionsDto = ConfigurationFileProvider.Deserializer.Deserialize(versionFilePath.OpenText()); var versions = versionsDto.VersioningSystems.ToDictionary( kvp => ToVersioningSystemId(kvp.Key), diff --git a/src/Elastic.Markdown/Myst/YamlSerialization.cs b/src/Elastic.Markdown/Myst/YamlSerialization.cs index c529932ba..669833fa3 100644 --- a/src/Elastic.Markdown/Myst/YamlSerialization.cs +++ b/src/Elastic.Markdown/Myst/YamlSerialization.cs @@ -33,8 +33,6 @@ public static T Deserialize(string yaml, ProductsConfiguration products) [YamlStaticContext] [YamlSerializable(typeof(YamlSettings))] [YamlSerializable(typeof(SettingsGrouping))] -[YamlSerializable(typeof(YamlSettings))] -[YamlSerializable(typeof(SettingsGrouping))] [YamlSerializable(typeof(Setting))] [YamlSerializable(typeof(AllowedValue))] [YamlSerializable(typeof(SettingMutability))] From 741c1ec9f39c7427758689b3a4877537fbd9c1f1 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Fri, 12 Sep 2025 12:16:28 -0300 Subject: [PATCH 24/24] Adjust product id for ecctl and fix flexibility rules --- config/legacy-url-mappings.yml | 68 ++++++------ config/products.yml | 100 +++++++++--------- .../Builder/ConfigurationFile.cs | 2 +- .../AppliesTo/ApplicableToYamlConverter.cs | 2 +- .../Myst/FrontMatter/Products.cs | 2 +- .../Legacy/PageLegacyUrlMapper.cs | 2 +- 6 files changed, 88 insertions(+), 88 deletions(-) diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index eb4a1d35c..577c2388d 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -8,40 +8,40 @@ stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', mappings: en/apm/agent/android/: - product: apm_agent_android + product: apm-agent-android legacy_versions: [ '0.x' ] en/apm/agent/dotnet/: - product: apm_agent_dotnet + product: apm-agent-dotnet legacy_versions: [ '1.33.0' ] en/apm/agent/go/: - product: apm_agent_go + product: apm-agent-go legacy_versions: [ '1.x', '0.5' ] en/apm/agent/java/: - product: apm_agent_java + product: apm-agent-java legacy_versions: [ '1.54.0', '0.7', '0.6'] en/apm/agent/nodejs/: - product: apm_agent_node + product: apm-agent-node legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/php/: - product: apm_agent_php + product: apm-agent-php legacy_versions: [ '1.x' ] en/apm/agent/python/: - product: apm_agent_python + product: apm-agent-python legacy_versions: [ '5.x', '4.x', '3.x', '2.x', '1.x' ] en/apm/agent/ruby/: - product: apm_agent_ruby + product: apm-agent-ruby legacy_versions: [ '3.x', '2.x', '1.x' ] en/apm/agent/rum-js/: - product: apm_agent_rum_js + product: apm-agent-rum-js legacy_versions: [ '4.x', '3.x', '2.x', '1.x', '0.x' ] en/apm/agent/swift/: - product: apm_agent_ios + product: apm-agent-ios legacy_versions: [ '1.2.1', '0.x' ] en/apm/attacher/: - product: apm_k8s_attacher + product: apm-k8s-attacher legacy_versions: [] en/apm/lambda/: - product: apm_aws_lambda + product: apm-aws-lambda legacy_versions: [] en/beats/auditbeat/: product: auditbeat @@ -80,61 +80,61 @@ mappings: product: winlogbeat legacy_versions: *stack en/cloud-heroku/: - product: cloud_hosted + product: cloud-hosted legacy_versions: [] en/cloud-on-k8s/: - product: cloud_kubernetes + product: cloud-kubernetes legacy_versions: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/cloud/: - product: cloud_hosted + product: cloud-hosted legacy_versions: [] en/cloud-enterprise/: - product: cloud_enterprise + product: cloud-enterprise legacy_versions: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] en/ecctl/: - product: ecctl + product: cloud-control-ecctl legacy_versions: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/ecs-logging/: - product: ecs_logging + product: ecs-logging legacy_versions: [] en/ecs-logging/dotnet/: - product: ecs_dotnet + product: ecs-dotnet legacy_versions: [] en/ecs-logging/go-logrus/: - product: ecs_logging_go_logrus + product: ecs-logging-go-logrus legacy_versions: [] en/ecs-logging/go-zap/: - product: ecs_logging_go_zap + product: ecs-logging-go-zap legacy_versions: [] en/ecs-logging/go-zerolog/: - product: ecs_logging_go_zerolog + product: ecs-logging-go-zerolog legacy_versions: [] en/ecs-logging/java/: - product: ecs_logging_java + product: ecs-logging-java legacy_versions: ['0.x'] en/ecs-logging/nodejs/: - product: ecs_logging_nodejs + product: ecs-logging-nodejs legacy_versions: [] en/ecs-logging/overview/: - product: ecs_logging + product: ecs-logging legacy_versions: [] en/ecs-logging/php/: - product: ecs_logging_php + product: ecs-logging-php legacy_versions: [] en/ecs-logging/python/: - product: ecs_logging_python + product: ecs-logging-python legacy_versions: [ '2.2.0' ] en/ecs-logging/ruby/: - product: ecs_logging_ruby + product: ecs-logging-ruby legacy_versions: [] en/ecs/: product: ecs legacy_versions: [ '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] en/elastic-stack-glossary/: - product: elastic_stack + product: elastic-stack legacy_versions: [] en/elastic-stack/: - product: elastic_stack + product: elastic-stack legacy_versions: *stack en/elasticsearch/client/curator/: product: curator @@ -179,10 +179,10 @@ mappings: product: elasticsearch legacy_versions: *stack en/esf/: - product: elastic_serverless_forwarder + product: elastic-serverless-forwarder legacy_versions: [] en/fleet/: - product: elastic_agent + product: elastic-agent legacy_versions: *stack en/ingest-overview/: product: elasticsearch @@ -215,13 +215,13 @@ mappings: product: elasticsearch legacy_versions: [] en/search-ui/: - product: search_ui + product: search-ui legacy_versions: ['1.24.1', '1.24.0'] en/security/: product: security legacy_versions: *stack en/serverless/: - product: cloud_serverless + product: cloud-serverless legacy_versions: [] en/starting-with-the-elasticsearch-platform-and-its-solutions/: product: elasticsearch diff --git a/config/products.yml b/config/products.yml index 8c75b62a7..f56b7644c 100644 --- a/config/products.yml +++ b/config/products.yml @@ -2,46 +2,46 @@ products: apm: display: 'APM' versioning: 'stack' - apm_agent: + apm-agent: display: 'APM Agent' versioning: 'stack' - apm_agent_android: + apm-agent-android: display: 'APM Agent for Android' versioning: 'apm_agent_android' - apm_agent_dotnet: + apm-agent-dotnet: display: 'APM Agent for .NET' versioning: 'apm_agent_dotnet' - apm_agent_go: + apm-agent-go: display: 'APM Agent for Go' versioning: 'apm_agent_go' - apm_agent_ios: + apm-agent-ios: display: 'APM Agent for iOS' versioning: 'apm_agent_ios' - apm_agent_java: + apm-agent-java: display: 'APM Agent for Java' versioning: 'apm_agent_java' - apm_agent_node: + apm-agent-node: display: 'APM Agent for Node' versioning: 'apm_agent_node' - apm_agent_php: + apm-agent-php: display: 'APM Agent for PHP' versioning: 'apm_agent_php' - apm_agent_python: + apm-agent-python: display: 'APM Agent for Python' versioning: 'apm_agent_python' - apm_agent_ruby: + apm-agent-ruby: display: 'APM Agent for Ruby' versioning: 'apm_agent_ruby' - apm_agent_rum_js: + apm-agent-rum-js: display: 'APM RUM JavaScript agent' versioning: 'apm_agent_rum' - apm_k8s_attacher: + apm-k8s-attacher: display: 'APM Attacher for Kubernetes' versioning: 'apm_attacher' - apm_aws_lambda: + apm-aws-lambda: display: 'APM AWS Lambda extension' versioning: 'apm_lambda' - apm_server: + apm-server: display: 'APM Server' versioning: 'stack' auditbeat: @@ -50,22 +50,22 @@ products: beats: display: 'Beats' versioning: 'stack' - ecctl: + cloud-control-ecctl: display: 'Elastic Cloud Control ECCTL' versioning: 'ecctl' - cloud_enterprise: + cloud-enterprise: display: 'Elastic Cloud Enterprise' versioning: 'ece' - cloud_hosted: + cloud-hosted: display: 'Elastic Cloud Hosted' versioning: 'ech' - cloud_kubernetes: + cloud-kubernetes: display: 'Elastic Cloud Kubernetes' versioning: 'eck' - cloud_serverless: + cloud-serverless: display: 'Elastic Cloud Serverless' versioning: 'serverless' - cloud_terraform: + cloud-terraform: display: 'Elastic Cloud Terraform' versioning: 'stack' curator: @@ -74,85 +74,85 @@ products: ecs: display: 'Elastic Common Schema (ECS)' versioning: 'stack' - ecs_logging: + ecs-logging: display: 'ECS Logging' versioning: 'stack' - ecs_dotnet: + ecs-dotnet: display: 'Elastic Common Schema support for .NET' versioning: 'ecs_logging_dotnet' - ecs_logging_go_logrus: + ecs-logging-go-logrus: display: 'Elastic Common Schema (ECS) support for Logrus' versioning: 'ecs_logging_go_logrus' - ecs_logging_go_zap: + ecs-logging-go-zap: display: 'Elastic Common Schema (ECS) support for uber_go/zap logger' versioning: 'ecs_logging_go_zap' - ecs_logging_go_zerolog: + ecs-logging-go-zerolog: display: 'Elastic Common Schema (ECS) support for zerolog' versioning: 'ecs_logging_go_zerolog' - ecs_logging_java: + ecs-logging-java: display: 'ECS_based logging for Java applications' versioning: 'ecs_logging_java' - ecs_logging_nodejs: + ecs-logging-nodejs: display: 'Elastic Common Schema (ECS) support for Node js' versioning: 'ecs_logging_nodejs' - ecs_logging_php: + ecs-logging-php: display: 'ECS Logging for PHP' versioning: 'ecs_logging_php' - ecs_logging_python: + ecs-logging-python: display: 'Elastic Common Schema (ECS) support for Python' versioning: 'ecs_logging_python' - ecs_logging_ruby: + ecs-logging-ruby: display: 'Elastic Common Schema (ECS) support for Ruby' versioning: 'ecs_logging_ruby' - edot_cf: + edot-cf: display: 'EDOT Cloud Forwarder' versioning: 'stack' - edot_sdk: + edot-sdk: display: 'Elastic Distribution of OpenTelemetry SDK' versioning: 'stack' - edot_collector: + edot-collector: display: 'Elastic Distribution of OpenTelemetry Collector' versioning: 'stack' - edot_ios: + edot-ios: display: 'Elastic Distribution of OpenTelemetry iOS' versioning: 'edot_ios' - edot_android: + edot-android: display: 'Elastic Distribution of OpenTelemetry Android' versioning: 'edot_android' - edot_dotnet: + edot-dotnet: display: 'Elastic Distribution of OpenTelemetry .NET' versioning: 'edot_dotnet' - edot_java: + edot-java: display: 'Elastic Distribution of OpenTelemetry Java' versioning: 'edot_java' - edot_node: + edot-node: display: 'Elastic Distribution of OpenTelemetry Node' versioning: 'edot_node' - edot_php: + edot-php: display: 'Elastic Distribution of OpenTelemetry PHP' versioning: 'edot_php' - edot_python: + edot-python: display: 'Elastic Distribution of OpenTelemetry Python' versioning: 'edot_python' - edot_cf_aws: + edot-cf-aws: display: 'EDOT Cloud Forwarder for AWS' versioning: 'edot_cf_aws' eland: display: 'Eland' versioning: 'stack' - elastic_agent: + elastic-agent: display: 'Elastic Agent' versioning: 'stack' - elastic_serverless_forwarder: + elastic-serverless-forwarder: display: 'Elastic Serverless Forwarder' versioning: 'esf' - elastic_stack: + elastic-stack: display: 'Elastic Stack' versioning: 'stack' elasticsearch: display: 'Elasticsearch' versioning: 'stack' - elasticsearch_client: + elasticsearch-client: display: 'Elasticsearch Client' versioning: 'stack' ess: @@ -176,7 +176,7 @@ products: logstash: display: 'Logstash' versioning: 'stack' - machine_learning: + machine-learning: display: 'Machine Learning' versioning: 'stack' metricbeat: @@ -191,7 +191,7 @@ products: painless: display: 'Elasticsearch Painless scripting language' versioning: 'stack' - search_ui: + search-ui: display: 'Search UI' versioning: 'stack' security: @@ -200,13 +200,13 @@ products: self: display: 'Self-managed Elastic' versioning: 'stack' - serverless_elasticsearch: + serverless-elasticsearch: display: 'Elasticsearch Serverless' versioning: 'all' - serverless_observability: + serverless-observability: display: 'Elastic Observability Serverless' versioning: 'all' - serverless_security: + serverless-security: display: 'Elastic Security Serverless' versioning: 'all' winlogbeat: diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index 4e9f92eb8..4898b3885 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -149,7 +149,7 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration break; } - if (!productsConfig.Products.TryGetValue(productId.Value.Replace('-', '_'), out var productToAdd)) + if (!productsConfig.Products.TryGetValue(productId.Value.Replace('_', '-'), out var productToAdd)) reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(productsConfig.Products.Select(p => p.Value.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); else _ = Products.Add(productToAdd); diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index a0cbda648..0cfb1a505 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -43,7 +43,7 @@ .. productKeys if (deserialized is not Dictionary { Count: > 0 } dictionary) return null; - var keys = dictionary.Keys.OfType().Select(x => x.Replace('-', '_')).ToArray(); + var keys = dictionary.Keys.OfType().Select(x => x.Replace('_', '-')).ToArray(); var oldStyleKeys = keys.Where(k => k.StartsWith(':')).ToList(); if (oldStyleKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not use valid yaml keys: {string.Join(", ", oldStyleKeys)}")); diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index 735eee59e..cb2cf064b 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -39,7 +39,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria if (string.IsNullOrWhiteSpace(productId)) throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm", products); - if (products.Products.TryGetValue(productId.Replace('-', '_'), out var product)) + if (products.Products.TryGetValue(productId.Replace('_', '-'), out var product)) return product; throw new InvalidProductException(productId, products); diff --git a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs index 9caa85a81..08b883bc4 100644 --- a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs +++ b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs @@ -30,7 +30,7 @@ public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, VersionsConfigur if (LegacyUrlMappings.Mappings.FirstOrDefault(x => mappedPage.Contains(x.BaseUrl, StringComparison.OrdinalIgnoreCase)) is not { } legacyMappingMatch) { - return [new LegacyPageMapping(LegacyUrlMappings.Mappings.First(x => x.Product.Id.Equals("elastic_stack", StringComparison.InvariantCultureIgnoreCase)).Product, mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; + return [new LegacyPageMapping(LegacyUrlMappings.Mappings.First(x => x.Product.Id.Equals("elastic-stack", StringComparison.InvariantCultureIgnoreCase)).Product, mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; } var allVersions = new List();