From 3c1f7ad289a37e80fe9e881def5cb84fb5e48b74 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 12 Sep 2025 11:51:18 +0200 Subject: [PATCH 1/7] Refactor applies_to --- .../Myst/Components/ApplicabilityItem.cs | 13 + .../Myst/Components/ApplicabilityMappings.cs | 64 ++++ .../Myst/Components/ApplicabilityRenderer.cs | 147 +++++++ .../Components/ApplicableToComponent.cshtml | 359 +----------------- .../Myst/Components/ApplicableToViewModel.cs | 169 +++++++++ .../Applicability/ApplicableToComponent.fs | 26 ++ 6 files changed, 436 insertions(+), 342 deletions(-) create mode 100644 src/Elastic.Markdown/Myst/Components/ApplicabilityItem.cs create mode 100644 src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs create mode 100644 src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityItem.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityItem.cs new file mode 100644 index 000000000..d5a71f78b --- /dev/null +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityItem.cs @@ -0,0 +1,13 @@ +// 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.AppliesTo; + +namespace Elastic.Markdown.Myst.Components; + +public record ApplicabilityItem( + string Key, + Applicability Applicability, + ApplicabilityRenderer.ApplicabilityRenderData RenderData +); diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs new file mode 100644 index 000000000..826d939e4 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs @@ -0,0 +1,64 @@ +// 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; + +namespace Elastic.Markdown.Myst.Components; + +public static class ApplicabilityMappings +{ + public record ApplicabilityDefinition(string Key, string DisplayName, VersioningSystemId VersioningSystemId); + + private static readonly Dictionary Mappings = new() + { + // Stack + ["stack"] = new ApplicabilityDefinition("Stack", "Elastic Stack", VersioningSystemId.Stack), + + // Serverless + ["serverless"] = new ApplicabilityDefinition("Serverless", "Elastic Cloud Serverless", VersioningSystemId.Serverless), + ["serverless-elasticsearch"] = new ApplicabilityDefinition("Serverless Elasticsearch", "Serverless Elasticsearch projects", VersioningSystemId.ElasticsearchProject), + ["serverless-observability"] = new ApplicabilityDefinition("Serverless Observability", "Serverless Observability projects", VersioningSystemId.ObservabilityProject), + ["serverless-security"] = new ApplicabilityDefinition("Serverless Security", "Serverless Security projects", VersioningSystemId.SecurityProject), + + // Deployment + ["ech"] = new ApplicabilityDefinition("ECH", "Elastic Cloud Hosted", VersioningSystemId.Ess), + ["eck"] = new ApplicabilityDefinition("ECK", "Elastic Cloud on Kubernetes", VersioningSystemId.Eck), + ["ece"] = new ApplicabilityDefinition("ECE", "Elastic Cloud Enterprise", VersioningSystemId.Ece), + ["self"] = new ApplicabilityDefinition("Self-Managed", "Self-managed Elastic deployments", VersioningSystemId.Self), + + // Product Applicability + ["ecctl"] = new ApplicabilityDefinition("ECCTL", "Elastic Cloud Control", VersioningSystemId.Ecctl), + ["curator"] = new ApplicabilityDefinition("Curator", "Curator", VersioningSystemId.Curator), + + // EDOT Products + ["edot-android"] = new ApplicabilityDefinition("EDOT Android", "Elastic Distribution of OpenTelemetry Android", VersioningSystemId.EdotAndroid), + ["edot-cf-aws"] = new ApplicabilityDefinition("EDOT CF AWS", "Elastic Distribution of OpenTelemetry Cloud Forwarder for AWS", VersioningSystemId.EdotCfAws), + ["edot-collector"] = new ApplicabilityDefinition("EDOT Collector", "Elastic Distribution of OpenTelemetry Collector", VersioningSystemId.EdotCollector), + ["edot-dotnet"] = new ApplicabilityDefinition("EDOT .NET", "Elastic Distribution of OpenTelemetry .NET", VersioningSystemId.EdotDotnet), + ["edot-ios"] = new ApplicabilityDefinition("EDOT iOS", "Elastic Distribution of OpenTelemetry iOS", VersioningSystemId.EdotIos), + ["edot-java"] = new ApplicabilityDefinition("EDOT Java", "Elastic Distribution of OpenTelemetry Java", VersioningSystemId.EdotJava), + ["edot-node"] = new ApplicabilityDefinition("EDOT Node.js", "Elastic Distribution of OpenTelemetry Node.js", VersioningSystemId.EdotNode), + ["edot-php"] = new ApplicabilityDefinition("EDOT PHP", "Elastic Distribution of OpenTelemetry PHP", VersioningSystemId.ApmAgentPhp), + ["edot-python"] = new ApplicabilityDefinition("EDOT Python", "Elastic Distribution of OpenTelemetry Python", VersioningSystemId.EdotPython), + + // APM Agents + ["apm-agent-android"] = new ApplicabilityDefinition("APM Agent Android", "Application Performance Monitoring Agent for Android", VersioningSystemId.ApmAgentAndroid), + ["apm-agent-dotnet"] = new ApplicabilityDefinition("APM Agent .NET", "Application Performance Monitoring Agent for .NET", VersioningSystemId.ApmAgentDotnet), + ["apm-agent-go"] = new ApplicabilityDefinition("APM Agent Go", "Application Performance Monitoring Agent for Go", VersioningSystemId.ApmAgentGo), + ["apm-agent-ios"] = new ApplicabilityDefinition("APM Agent iOS", "Application Performance Monitoring Agent for iOS", VersioningSystemId.ApmAgentIos), + ["apm-agent-java"] = new ApplicabilityDefinition("APM Agent Java", "Application Performance Monitoring Agent for Java", VersioningSystemId.ApmAgentJava), + ["apm-agent-node"] = new ApplicabilityDefinition("APM Agent Node.js", "Application Performance Monitoring Agent for Node.js", VersioningSystemId.ApmAgentNode), + ["apm-agent-php"] = new ApplicabilityDefinition("APM Agent PHP", "Application Performance Monitoring Agent for PHP", VersioningSystemId.ApmAgentPhp), + ["apm-agent-python"] = new ApplicabilityDefinition("APM Agent Python", "Application Performance Monitoring Agent for Python", VersioningSystemId.ApmAgentPython), + ["apm-agent-ruby"] = new ApplicabilityDefinition("APM Agent Ruby", "Application Performance Monitoring Agent for Ruby", VersioningSystemId.ApmAgentRuby), + ["apm-agent-rum"] = new ApplicabilityDefinition("APM Agent RUM", "Application Performance Monitoring Agent for Real User Monitoring", VersioningSystemId.ApmAgentRum), + + // Generic product + ["product"] = new ApplicabilityDefinition("", "", VersioningSystemId.All) + }; + + public static ApplicabilityDefinition GetProductDefinition(string productKey) => Mappings.TryGetValue(productKey, out var definition) + ? definition + : throw new ArgumentException($"Unknown product key: {productKey}"); +} diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs new file mode 100644 index 000000000..11e22e297 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs @@ -0,0 +1,147 @@ +// 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.Diagnostics.CodeAnalysis; +using Elastic.Documentation; +using Elastic.Documentation.AppliesTo; +using Elastic.Documentation.Configuration.Versions; + +namespace Elastic.Markdown.Myst.Components; + +public class ApplicabilityRenderer +{ + public record ApplicabilityRenderData( + string BadgeText, + string Version, + string TooltipText, + string LifecycleClass, + bool ShowLifecycle, + bool ShowVersion + ); + + public ApplicabilityRenderData RenderApplicability( + Applicability applicability, + ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, + VersioningSystem versioningSystem, + AppliesCollection allApplications) + { + var lifecycleClass = applicability.GetLifeCycleName().ToLowerInvariant().Replace(" ", "-"); + var lifecycleFull = GetLifecycleFullText(applicability.Lifecycle); + var realVersion = TryGetRealVersion(applicability, out var v) ? v : null; + + var tooltipText = BuildTooltipText(applicability, applicabilityDefinition, versioningSystem, realVersion, lifecycleFull); + var badgeText = BuildBadgeText(applicability, applicabilityDefinition, versioningSystem, realVersion, allApplications); + var badgeTextChanged = badgeText != applicabilityDefinition.Key; + + var showLifecycle = applicability.Lifecycle != ProductLifecycle.GenerallyAvailable && !badgeTextChanged; + var showVersion = applicability.Version is not null and not AllVersions; + + var version = showVersion && versioningSystem.Current >= applicability.Version! + ? applicability.Version!.ToString() + : badgeText; + + return new ApplicabilityRenderData( + BadgeText: badgeText, + Version: version, + TooltipText: tooltipText, + LifecycleClass: lifecycleClass, + ShowLifecycle: showLifecycle, + ShowVersion: showVersion + ); + } + + private static string GetLifecycleFullText(ProductLifecycle lifecycle) => lifecycle switch + { + ProductLifecycle.GenerallyAvailable => "Available", + ProductLifecycle.Beta => "Available in beta", + ProductLifecycle.TechnicalPreview => "Available in technical preview", + ProductLifecycle.Deprecated => "Deprecated", + ProductLifecycle.Removed => "Removed", + ProductLifecycle.Unavailable => "Not available", + _ => "" + }; + + private static string BuildTooltipText( + Applicability applicability, + ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, + VersioningSystem versioningSystem, + SemVersion? realVersion, + string lifecycleFull) + { + var tooltipText = ""; + + tooltipText = realVersion is not null + ? realVersion <= versioningSystem.Current + ? $"{lifecycleFull} on {applicabilityDefinition.DisplayName} version {realVersion} and later unless otherwise specified." + : applicability.Lifecycle switch + { + ProductLifecycle.GenerallyAvailable + or ProductLifecycle.Beta + or ProductLifecycle.TechnicalPreview + or ProductLifecycle.Planned => + $"We plan to add this functionality in a future {applicabilityDefinition.DisplayName} update. Subject to change.", + ProductLifecycle.Deprecated => $"We plan to deprecate this functionality in a future {applicabilityDefinition.DisplayName} update. Subject to change.", + ProductLifecycle.Removed => $"We plan to remove this functionality in a future {applicabilityDefinition.DisplayName} update. Subject to change.", + _ => tooltipText + } + : $"{lifecycleFull} on {applicabilityDefinition.DisplayName} unless otherwise specified."; + + var disclaimer = GetDisclaimer(applicability.Lifecycle, versioningSystem.Id); + if (disclaimer is not null) + tooltipText = $"{tooltipText}\n\n{disclaimer}"; + + return tooltipText; + } + + private static string? GetDisclaimer(ProductLifecycle lifecycle, VersioningSystemId versioningSystemId) => lifecycle switch + { + ProductLifecycle.Beta => "Beta features are subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.", + ProductLifecycle.TechnicalPreview => "This functionality may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.", + ProductLifecycle.GenerallyAvailable => versioningSystemId is VersioningSystemId.Stack + ? "If this functionality is unavailable or behaves differently when deployed on ECH, ECE, ECK, or a self-managed installation, it will be indicated on the page." + : null, + _ => null + }; + + private static string BuildBadgeText( + Applicability applicability, + ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, + VersioningSystem versioningSystem, + SemVersion? realVersion, + AppliesCollection allApplications) + { + var badgeText = applicabilityDefinition.Key; + + if (realVersion is not null && realVersion > versioningSystem.Current) + { + badgeText = applicability.Lifecycle switch + { + ProductLifecycle.TechnicalPreview => "Planned", + ProductLifecycle.Beta => "Planned", + ProductLifecycle.GenerallyAvailable => + allApplications.Any(a => a.Lifecycle is ProductLifecycle.TechnicalPreview or ProductLifecycle.Beta) + ? "GA planned" + : "Planned", + ProductLifecycle.Deprecated => "Deprecation planned", + ProductLifecycle.Removed => "Removal planned", + ProductLifecycle.Planned => "Planned", + _ => badgeText + }; + } + + return badgeText; + } + + private static bool TryGetRealVersion(Applicability applicability, [NotNullWhen(true)] out SemVersion? version) + { + version = null; + if (applicability.Version is not null && applicability.Version != AllVersions.Instance) + { + version = applicability.Version; + return true; + } + + return false; + } +} diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml index a8960aad9..1a0ad590d 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml @@ -1,357 +1,32 @@ -@using System.Diagnostics.CodeAnalysis -@using Elastic.Documentation -@using Elastic.Documentation.AppliesTo -@using Elastic.Documentation.Configuration.Versions @inherits RazorSlice -@{ - var appliesTo = Model.AppliesTo; -} - -@if (appliesTo.Stack is not null) -{ - @RenderProduct( - "Stack", - "Elastic Stack", - VersioningSystemId.Stack, - appliesTo.Stack - ) -} -@if (appliesTo.Serverless is not null) -{ - if (appliesTo.Serverless.AllProjects is not null) - { - @RenderProduct( - "Serverless", - "Elastic Cloud Serverless", - VersioningSystemId.Serverless, - appliesTo.Serverless.AllProjects - ) - } - else - { - if (appliesTo.Serverless.Elasticsearch is not null) - { - @RenderProduct( - "Serverless Elasticsearch", - "Serverless Elasticsearch projects", - VersioningSystemId.ElasticsearchProject, - appliesTo.Serverless.Elasticsearch - ) - } - - if (appliesTo.Serverless.Observability is not null) - { - @RenderProduct( - "Serverless Observability", - "Serverless Observability projects", - VersioningSystemId.ObservabilityProject, - appliesTo.Serverless.Observability - ) - } - - if (appliesTo.Serverless.Security is not null) - { - @RenderProduct( - "Serverless Security", - "Serverless Security projects", - VersioningSystemId.SecurityProject, - appliesTo.Serverless.Security - ) - } - } -} -@if (appliesTo.Deployment is not null) -{ - - if (appliesTo.Deployment.Ess is not null) - { - @RenderProduct( - "ECH", - "Elastic Cloud Hosted", - VersioningSystemId.Ess, - appliesTo.Deployment.Ess - ) - } - - if (appliesTo.Deployment.Eck is not null) - { - @RenderProduct( - "ECK", - "Elastic Cloud on Kubernetes", - VersioningSystemId.Eck, - appliesTo.Deployment.Eck - ) - } - - if (appliesTo.Deployment.Ece is not null) - { - @RenderProduct("ECE", - "Elastic Cloud Enterprise", - VersioningSystemId.Ece, - appliesTo.Deployment.Ece - ) - } - - if (appliesTo.Deployment.Self is not null) - { - @RenderProduct( - "Self-Managed", - "Self-managed Elastic deployments", - VersioningSystemId.Self, - appliesTo.Deployment.Self - ) - } -} -@if (appliesTo.ProductApplicability is not null) -{ - var pa = appliesTo.ProductApplicability; - if (pa.Ecctl is not null) - { - @RenderProduct("ECCTL", "Elastic Cloud Control", VersioningSystemId.Ecctl, pa.Ecctl) - } - - if (pa.Curator is not null) - { - @RenderProduct("Curator", "Curator", VersioningSystemId.Curator, pa.Curator) - } - - if (pa.EdotAndroid is not null) - { - @RenderProduct("EDOT Android", "Elastic Distribution of OpenTelemetry Android", VersioningSystemId.EdotAndroid, pa.EdotAndroid) - } - - if (pa.EdotCfAws is not null) - { - @RenderProduct("EDOT CF AWS", "Elastic Distribution of OpenTelemetry Cloud Forwarder for AWS", VersioningSystemId.EdotCfAws, pa.EdotCfAws) - } - - if (pa.EdotCollector is not null) - { - @RenderProduct("EDOT Collector", "Elastic Distribution of OpenTelemetry Collector", VersioningSystemId.EdotCollector, pa.EdotCollector) - } - - if (pa.EdotDotnet is not null) - { - @RenderProduct("EDOT .NET", "Elastic Distribution of OpenTelemetry .NET", VersioningSystemId.EdotDotnet, pa.EdotDotnet) - } - - if (pa.EdotIos is not null) - { - @RenderProduct("EDOT iOS", "Elastic Distribution of OpenTelemetry iOS", VersioningSystemId.EdotIos, pa.EdotIos) - } - - if (pa.EdotJava is not null) - { - @RenderProduct("EDOT Java", "Elastic Distribution of OpenTelemetry Java", VersioningSystemId.EdotJava, pa.EdotJava) - } - - if (pa.EdotNode is not null) - { - @RenderProduct("EDOT Node.js", "Elastic Distribution of OpenTelemetry Node.js", VersioningSystemId.EdotNode, pa.EdotNode) - } - - if (pa.EdotPhp is not null) - { - @RenderProduct("EDOT PHP", "Elastic Distribution of OpenTelemetry PHP", VersioningSystemId.ApmAgentPhp, pa.EdotPhp) - } - - if (pa.EdotPython is not null) - { - @RenderProduct("EDOT Python", "Elastic Distribution of OpenTelemetry Python", VersioningSystemId.EdotPython, pa.EdotPython) - } - - if (pa.ApmAgentAndroid is not null) - { - @RenderProduct("APM Agent Android", "Application Performance Monitoring Agent for Android", VersioningSystemId.ApmAgentAndroid, pa.ApmAgentAndroid) - } - - if (pa.ApmAgentDotnet is not null) - { - @RenderProduct("APM Agent .NET", "Application Performance Monitoring Agent for .NET", VersioningSystemId.ApmAgentDotnet, pa.ApmAgentDotnet) - } - - if (pa.ApmAgentGo is not null) - { - @RenderProduct("APM Agent Go", "Application Performance Monitoring Agent for Go", VersioningSystemId.ApmAgentGo, pa.ApmAgentGo) - } - - if (pa.ApmAgentIos is not null) - { - @RenderProduct("APM Agent iOS", "Application Performance Monitoring Agent for iOS", VersioningSystemId.ApmAgentIos, pa.ApmAgentIos) - } - - if (pa.ApmAgentJava is not null) - { - @RenderProduct("APM Agent Java", "Application Performance Monitoring Agent for Java", VersioningSystemId.ApmAgentJava, pa.ApmAgentJava) - } - - if (pa.ApmAgentNode is not null) - { - @RenderProduct("APM Agent Node.js", "Application Performance Monitoring Agent for Node.js", VersioningSystemId.ApmAgentNode, pa.ApmAgentNode) - } - - if (pa.ApmAgentPhp is not null) - { - @RenderProduct("APM Agent PHP", "Application Performance Monitoring Agent for PHP", VersioningSystemId.ApmAgentPhp, pa.ApmAgentPhp) - } - - if (pa.ApmAgentPython is not null) - { - @RenderProduct("APM Agent Python", "Application Performance Monitoring Agent for Python", VersioningSystemId.ApmAgentPython, pa.ApmAgentPython) - } - - if (pa.ApmAgentRuby is not null) - { - @RenderProduct("APM Agent Ruby", "Application Performance Monitoring Agent for Ruby", VersioningSystemId.ApmAgentRuby, pa.ApmAgentRuby) - } - - if (pa.ApmAgentRum is not null) - { - @RenderProduct("APM Agent RUM", "Application Performance Monitoring Agent for Real User Monitoring", VersioningSystemId.ApmAgentRum, pa.ApmAgentRum) - } -} -@if (appliesTo.Product is not null) +@foreach (var item in Model.GetApplicabilityItems()) { - @RenderProduct("", "", VersioningSystemId.All, appliesTo.Product) -} - -@functions { - - private IHtmlContent RenderProduct(string key, string keyFull, VersioningSystemId versioningSystemName, AppliesCollection applications) - { - var versioningSystem = Model.VersionsConfig.GetVersioningSystem(versioningSystemName); - - foreach (var applicability in applications) - { - var badgeText = key; - var tooltipText = ""; - var lifecycleClass = applicability.GetLifeCycleName().ToLowerInvariant().Replace(" ", "-"); + + @item.Key - var lifecycleFull = applicability.Lifecycle switch - { - ProductLifecycle.GenerallyAvailable => "Available", - ProductLifecycle.Beta => "Available in beta", - ProductLifecycle.TechnicalPreview => "Available in technical preview", - ProductLifecycle.Deprecated => "Deprecated", - ProductLifecycle.Removed => "Removed", - ProductLifecycle.Unavailable => "Not available", - _ => "" - }; - - var realVersion = TryGetRealVersion(applicability, out var v) ? v : null; - - if (realVersion is not null) - { - - if (realVersion <= versioningSystem.Current) - { - tooltipText = $"{lifecycleFull} on {keyFull} version {realVersion} and later unless otherwise specified."; - } - else - { - tooltipText = applicability.Lifecycle switch - { - ProductLifecycle.GenerallyAvailable - or ProductLifecycle.Beta - or ProductLifecycle.TechnicalPreview - or ProductLifecycle.Planned => - $"We plan to add this functionality in a future {keyFull} update. Subject to change.", - ProductLifecycle.Deprecated => $"We plan to deprecate this functionality in a future {keyFull} update. Subject to change.", - ProductLifecycle.Removed => $"We plan to remove this functionality in a future {keyFull} update. Subject to change.", - _ => tooltipText - }; - } - - } - else + @if (!string.IsNullOrEmpty(item.Key) && (item.RenderData.ShowLifecycle || item.RenderData.ShowVersion)) { - tooltipText = $"{lifecycleFull} on {keyFull} unless otherwise specified."; + } - - var disclaimer = applicability.Lifecycle switch - { - ProductLifecycle.Beta => "Beta features are subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.", - ProductLifecycle.TechnicalPreview => "This functionality may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.", - ProductLifecycle.GenerallyAvailable => versioningSystem.Id is VersioningSystemId.Stack ? "If this functionality is unavailable or behaves differently when deployed on ECH, ECE, ECK, or a self-managed installation, it will be indicated on the page." : null, - _ => null - }; - - if (disclaimer is not null) - { - tooltipText = $"{tooltipText}\n\n{disclaimer}"; - } - - if (realVersion is not null && realVersion > versioningSystem.Current) - { - badgeText = applicability.Lifecycle switch + + @if (item.RenderData.ShowLifecycle) { - ProductLifecycle.TechnicalPreview => "Planned", - ProductLifecycle.Beta => "Planned", - ProductLifecycle.GenerallyAvailable => - applications.Any(a => a.Lifecycle is ProductLifecycle.TechnicalPreview or ProductLifecycle.Beta) - ? "GA planned" - : "Planned", - ProductLifecycle.Deprecated => "Deprecation planned", - ProductLifecycle.Removed => "Removal planned", - ProductLifecycle.Planned => "Planned", - _ => badgeText - }; - } - - var badgeTextChanged = badgeText != key; - - @key - - @{ - var showLifeCycle = applicability.Lifecycle != ProductLifecycle.GenerallyAvailable && !badgeTextChanged; - var showVersion = applicability.Version is not null and not AllVersions; + @item.Applicability.GetLifeCycleName() } - - @if (!string.IsNullOrEmpty(key) && (showLifeCycle || showVersion)) + @if (item.RenderData.ShowVersion) { - - } - - @if (showLifeCycle) + @if (item.RenderData.ShowVersion && item.RenderData.Version != item.RenderData.BadgeText) { - @applicability.GetLifeCycleName() + + @item.Applicability.Version + } - @if (showVersion) + else { - @if (versioningSystem.Current >= applicability.Version!) - { - - @applicability.Version - - } - else - { - @badgeText - } + @item.RenderData.BadgeText } - + } - } - - return HtmlString.Empty; - } - -} - -@functions { - - private static bool TryGetRealVersion(Applicability applicability, [NotNullWhen(true)] out SemVersion? version) - { - version = null; - if (applicability.Version is not null && applicability.Version != AllVersions.Instance) - { - version = applicability.Version; - return true; - } - - return false; - } - + } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index 103226fb7..dab0318c0 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -9,7 +9,176 @@ namespace Elastic.Markdown.Myst.Components; public class ApplicableToViewModel { + private readonly ApplicabilityRenderer _applicabilityRenderer = new(); + public required bool Inline { get; init; } public required ApplicableTo AppliesTo { get; init; } public required VersionsConfiguration VersionsConfig { get; init; } + + public IEnumerable GetApplicabilityItems() + { + var items = new List(); + + // Process Stack + if (AppliesTo.Stack is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("stack"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(AppliesTo.Stack, productDef, versioningSystem)); + } + + // Process Serverless + if (AppliesTo.Serverless is not null) + { + if (AppliesTo.Serverless.AllProjects is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("serverless"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(AppliesTo.Serverless.AllProjects, productDef, versioningSystem)); + } + else + items.AddRange(ProcessServerlessProjects(AppliesTo.Serverless)); + } + + // Process Deployment + if (AppliesTo.Deployment is not null) + items.AddRange(ProcessDeploymentTypes(AppliesTo.Deployment)); + + // Process Product Applicability + if (AppliesTo.ProductApplicability is not null) + items.AddRange(ProcessProductApplicability(AppliesTo.ProductApplicability)); + + // Process Generic Product + if (AppliesTo.Product is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("product"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(AppliesTo.Product, productDef, versioningSystem)); + } + + return items; + } + + private IEnumerable ProcessServerlessProjects(ServerlessProjectApplicability serverless) + { + var items = new List(); + + if (serverless.Elasticsearch is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("serverless-elasticsearch"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(serverless.Elasticsearch, productDef, versioningSystem)); + } + + if (serverless.Observability is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("serverless-observability"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(serverless.Observability, productDef, versioningSystem)); + } + + if (serverless.Security is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("serverless-security"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(serverless.Security, productDef, versioningSystem)); + } + + return items; + } + + private IEnumerable ProcessDeploymentTypes(DeploymentApplicability deployment) + { + var items = new List(); + + if (deployment.Ess is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("ech"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Ess, productDef, versioningSystem)); + } + + if (deployment.Eck is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("eck"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Eck, productDef, versioningSystem)); + } + + if (deployment.Ece is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("ece"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Ece, productDef, versioningSystem)); + } + + if (deployment.Self is not null) + { + var productDef = ApplicabilityMappings.GetProductDefinition("self"); + var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Self, productDef, versioningSystem)); + } + + return items; + } + + private IEnumerable ProcessProductApplicability(ProductApplicability productApplicability) + { + var items = new List(); + + // Process each product applicability property explicitly (AOT-compatible) + ProcessProductIfNotNull(productApplicability.Ecctl, "ecctl", items); + ProcessProductIfNotNull(productApplicability.Curator, "curator", items); + ProcessProductIfNotNull(productApplicability.EdotAndroid, "edot-android", items); + ProcessProductIfNotNull(productApplicability.EdotCfAws, "edot-cf-aws", items); + ProcessProductIfNotNull(productApplicability.EdotCollector, "edot-collector", items); + ProcessProductIfNotNull(productApplicability.EdotDotnet, "edot-dotnet", items); + ProcessProductIfNotNull(productApplicability.EdotIos, "edot-ios", items); + ProcessProductIfNotNull(productApplicability.EdotJava, "edot-java", items); + ProcessProductIfNotNull(productApplicability.EdotNode, "edot-node", items); + ProcessProductIfNotNull(productApplicability.EdotPhp, "edot-php", items); + ProcessProductIfNotNull(productApplicability.EdotPython, "edot-python", items); + ProcessProductIfNotNull(productApplicability.ApmAgentAndroid, "apm-agent-android", items); + ProcessProductIfNotNull(productApplicability.ApmAgentDotnet, "apm-agent-dotnet", items); + ProcessProductIfNotNull(productApplicability.ApmAgentGo, "apm-agent-go", items); + ProcessProductIfNotNull(productApplicability.ApmAgentIos, "apm-agent-ios", items); + ProcessProductIfNotNull(productApplicability.ApmAgentJava, "apm-agent-java", items); + ProcessProductIfNotNull(productApplicability.ApmAgentNode, "apm-agent-node", items); + ProcessProductIfNotNull(productApplicability.ApmAgentPhp, "apm-agent-php", items); + ProcessProductIfNotNull(productApplicability.ApmAgentPython, "apm-agent-python", items); + ProcessProductIfNotNull(productApplicability.ApmAgentRuby, "apm-agent-ruby", items); + ProcessProductIfNotNull(productApplicability.ApmAgentRum, "apm-agent-rum", items); + + return items; + } + + private void ProcessProductIfNotNull(AppliesCollection? collection, string productKey, List items) + { + if (collection is null) + return; + + var applicabilityDefinition = ApplicabilityMappings.GetProductDefinition(productKey); + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDefinition.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(collection, applicabilityDefinition, versioningSystem)); + } + + private IEnumerable ProcessApplicabilityCollection( + AppliesCollection applications, + ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, + VersioningSystem versioningSystem) => + applications.Select(applicability => + { + var renderData = _applicabilityRenderer.RenderApplicability( + applicability, + applicabilityDefinition, + versioningSystem, + applications); + + return new ApplicabilityItem( + Key: applicabilityDefinition.Key, + Applicability: applicability, + RenderData: renderData + ); + }); + } diff --git a/tests/authoring/Applicability/ApplicableToComponent.fs b/tests/authoring/Applicability/ApplicableToComponent.fs index 7a8b50357..fc6c07223 100644 --- a/tests/authoring/Applicability/ApplicableToComponent.fs +++ b/tests/authoring/Applicability/ApplicableToComponent.fs @@ -507,6 +507,32 @@ product: ga

""" + +type ``product preview`` () = + static let markdown = Setup.Markdown """ +```{applies_to} +product: preview 1.3.0 +``` +""" + + [] + let ``renders product preview`` () = + markdown |> convertsToHtml """ +

+ + + + Preview + + 1.3.0 + + + +

+""" + // Test complex mixed scenarios type ``complex mixed scenario`` () = From e0ac169e06dad7d8f6ef54f84087b1ae4102a6ae Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 12 Sep 2025 12:11:53 +0200 Subject: [PATCH 2/7] Update src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs index 826d939e4..ad19e0ce5 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs @@ -39,7 +39,7 @@ public record ApplicabilityDefinition(string Key, string DisplayName, Versioning ["edot-ios"] = new ApplicabilityDefinition("EDOT iOS", "Elastic Distribution of OpenTelemetry iOS", VersioningSystemId.EdotIos), ["edot-java"] = new ApplicabilityDefinition("EDOT Java", "Elastic Distribution of OpenTelemetry Java", VersioningSystemId.EdotJava), ["edot-node"] = new ApplicabilityDefinition("EDOT Node.js", "Elastic Distribution of OpenTelemetry Node.js", VersioningSystemId.EdotNode), - ["edot-php"] = new ApplicabilityDefinition("EDOT PHP", "Elastic Distribution of OpenTelemetry PHP", VersioningSystemId.ApmAgentPhp), + ["edot-php"] = new ApplicabilityDefinition("EDOT PHP", "Elastic Distribution of OpenTelemetry PHP", VersioningSystemId.EdotPhp), ["edot-python"] = new ApplicabilityDefinition("EDOT Python", "Elastic Distribution of OpenTelemetry Python", VersioningSystemId.EdotPython), // APM Agents From 3339962920ca1ebb8a05afef59c73f601ebd4126 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 12 Sep 2025 12:12:03 +0200 Subject: [PATCH 3/7] Update src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Myst/Components/ApplicableToComponent.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml index 1a0ad590d..334fd8654 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml @@ -16,7 +16,7 @@ } @if (item.RenderData.ShowVersion) { - @if (item.RenderData.ShowVersion && item.RenderData.Version != item.RenderData.BadgeText) + @if (item.RenderData.Version != item.RenderData.BadgeText) { @item.Applicability.Version From 327a2fa8cd578f715f442966a25b4c201bade466 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 12 Sep 2025 23:56:18 +0200 Subject: [PATCH 4/7] Fix naming --- .../Myst/Components/ApplicabilityMappings.cs | 4 ++-- .../Myst/Components/ApplicableToViewModel.cs | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs index ad19e0ce5..1dd50883c 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs @@ -58,7 +58,7 @@ public record ApplicabilityDefinition(string Key, string DisplayName, Versioning ["product"] = new ApplicabilityDefinition("", "", VersioningSystemId.All) }; - public static ApplicabilityDefinition GetProductDefinition(string productKey) => Mappings.TryGetValue(productKey, out var definition) + public static ApplicabilityDefinition GetApplicabilityDefinition(string key) => Mappings.TryGetValue(key, out var definition) ? definition - : throw new ArgumentException($"Unknown product key: {productKey}"); + : throw new ArgumentException($"Unknown product key: {key}"); } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index dab0318c0..3dde4e0c8 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -22,7 +22,7 @@ public IEnumerable GetApplicabilityItems() // Process Stack if (AppliesTo.Stack is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("stack"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("stack"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(AppliesTo.Stack, productDef, versioningSystem)); } @@ -32,7 +32,7 @@ public IEnumerable GetApplicabilityItems() { if (AppliesTo.Serverless.AllProjects is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("serverless"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(AppliesTo.Serverless.AllProjects, productDef, versioningSystem)); } @@ -51,7 +51,7 @@ public IEnumerable GetApplicabilityItems() // Process Generic Product if (AppliesTo.Product is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("product"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("product"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(AppliesTo.Product, productDef, versioningSystem)); } @@ -65,21 +65,21 @@ private IEnumerable ProcessServerlessProjects(ServerlessProje if (serverless.Elasticsearch is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("serverless-elasticsearch"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless-elasticsearch"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(serverless.Elasticsearch, productDef, versioningSystem)); } if (serverless.Observability is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("serverless-observability"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless-observability"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(serverless.Observability, productDef, versioningSystem)); } if (serverless.Security is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("serverless-security"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless-security"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(serverless.Security, productDef, versioningSystem)); } @@ -93,28 +93,28 @@ private IEnumerable ProcessDeploymentTypes(DeploymentApplicab if (deployment.Ess is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("ech"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("ech"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(deployment.Ess, productDef, versioningSystem)); } if (deployment.Eck is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("eck"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("eck"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(deployment.Eck, productDef, versioningSystem)); } if (deployment.Ece is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("ece"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("ece"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(deployment.Ece, productDef, versioningSystem)); } if (deployment.Self is not null) { - var productDef = ApplicabilityMappings.GetProductDefinition("self"); + var productDef = ApplicabilityMappings.GetApplicabilityDefinition("self"); var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(deployment.Self, productDef, versioningSystem)); } @@ -157,7 +157,7 @@ private void ProcessProductIfNotNull(AppliesCollection? collection, string produ if (collection is null) return; - var applicabilityDefinition = ApplicabilityMappings.GetProductDefinition(productKey); + var applicabilityDefinition = ApplicabilityMappings.GetApplicabilityDefinition(productKey); var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDefinition.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(collection, applicabilityDefinition, versioningSystem)); } From 19d04d5679fdc3f6daaab2a2a43e99774d5ca374 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Sun, 14 Sep 2025 00:25:18 +0200 Subject: [PATCH 5/7] Simplify a bit --- .../Myst/Components/ApplicabilityRenderer.cs | 28 ++++++++----------- .../Components/ApplicableToComponent.cshtml | 21 ++++++-------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs index 11e22e297..87a683c89 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs @@ -12,11 +12,11 @@ namespace Elastic.Markdown.Myst.Components; public class ApplicabilityRenderer { public record ApplicabilityRenderData( - string BadgeText, + string BadgeLifecycleText, string Version, string TooltipText, string LifecycleClass, - bool ShowLifecycle, + bool ShowLifecycleName, bool ShowVersion ); @@ -31,22 +31,17 @@ public ApplicabilityRenderData RenderApplicability( var realVersion = TryGetRealVersion(applicability, out var v) ? v : null; var tooltipText = BuildTooltipText(applicability, applicabilityDefinition, versioningSystem, realVersion, lifecycleFull); - var badgeText = BuildBadgeText(applicability, applicabilityDefinition, versioningSystem, realVersion, allApplications); - var badgeTextChanged = badgeText != applicabilityDefinition.Key; - - var showLifecycle = applicability.Lifecycle != ProductLifecycle.GenerallyAvailable && !badgeTextChanged; - var showVersion = applicability.Version is not null and not AllVersions; - - var version = showVersion && versioningSystem.Current >= applicability.Version! - ? applicability.Version!.ToString() - : badgeText; + var badgeLifecycleText = BuildBadgeLifecycleText(applicability, versioningSystem, realVersion, allApplications); + var showLifecycle = applicability.Lifecycle != ProductLifecycle.GenerallyAvailable && string.IsNullOrEmpty(badgeLifecycleText); + var showVersion = applicability.Version is not null and not AllVersions && versioningSystem.Current >= applicability.Version; + var version = applicability.Version?.ToString() ?? ""; return new ApplicabilityRenderData( - BadgeText: badgeText, + BadgeLifecycleText: badgeLifecycleText, Version: version, TooltipText: tooltipText, LifecycleClass: lifecycleClass, - ShowLifecycle: showLifecycle, + ShowLifecycleName: showLifecycle, ShowVersion: showVersion ); } @@ -104,15 +99,13 @@ or ProductLifecycle.TechnicalPreview _ => null }; - private static string BuildBadgeText( + private static string BuildBadgeLifecycleText( Applicability applicability, - ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, VersioningSystem versioningSystem, SemVersion? realVersion, AppliesCollection allApplications) { - var badgeText = applicabilityDefinition.Key; - + var badgeText = ""; if (realVersion is not null && realVersion > versioningSystem.Current) { badgeText = applicability.Lifecycle switch @@ -126,6 +119,7 @@ private static string BuildBadgeText( ProductLifecycle.Deprecated => "Deprecation planned", ProductLifecycle.Removed => "Removal planned", ProductLifecycle.Planned => "Planned", + ProductLifecycle.Unavailable => "Unavailable", _ => badgeText }; } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml index 334fd8654..f35d221e9 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml @@ -5,27 +5,24 @@ @item.Key - @if (!string.IsNullOrEmpty(item.Key) && (item.RenderData.ShowLifecycle || item.RenderData.ShowVersion)) + @if (!string.IsNullOrEmpty(item.Key) && (item.RenderData.ShowLifecycleName || item.RenderData.ShowVersion || !string.IsNullOrEmpty(item.RenderData.BadgeLifecycleText))) { } - @if (item.RenderData.ShowLifecycle) + @if (item.RenderData.ShowLifecycleName) { @item.Applicability.GetLifeCycleName() } @if (item.RenderData.ShowVersion) { - @if (item.RenderData.Version != item.RenderData.BadgeText) - { - - @item.Applicability.Version - - } - else - { - @item.RenderData.BadgeText - } + + @item.RenderData.Version + + } + else + { + @item.RenderData.BadgeLifecycleText } From 324d1e1050713e8693f768c0c107845a009ae32b Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Sun, 14 Sep 2025 21:04:55 +0200 Subject: [PATCH 6/7] Use properties instead of dictionary --- .../Myst/Components/ApplicabilityMappings.cs | 95 +++++++--------- .../Myst/Components/ApplicableToViewModel.cs | 107 +++++++++--------- 2 files changed, 97 insertions(+), 105 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs index 1dd50883c..138b04a0f 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs @@ -10,55 +10,48 @@ public static class ApplicabilityMappings { public record ApplicabilityDefinition(string Key, string DisplayName, VersioningSystemId VersioningSystemId); - private static readonly Dictionary Mappings = new() - { - // Stack - ["stack"] = new ApplicabilityDefinition("Stack", "Elastic Stack", VersioningSystemId.Stack), - - // Serverless - ["serverless"] = new ApplicabilityDefinition("Serverless", "Elastic Cloud Serverless", VersioningSystemId.Serverless), - ["serverless-elasticsearch"] = new ApplicabilityDefinition("Serverless Elasticsearch", "Serverless Elasticsearch projects", VersioningSystemId.ElasticsearchProject), - ["serverless-observability"] = new ApplicabilityDefinition("Serverless Observability", "Serverless Observability projects", VersioningSystemId.ObservabilityProject), - ["serverless-security"] = new ApplicabilityDefinition("Serverless Security", "Serverless Security projects", VersioningSystemId.SecurityProject), - - // Deployment - ["ech"] = new ApplicabilityDefinition("ECH", "Elastic Cloud Hosted", VersioningSystemId.Ess), - ["eck"] = new ApplicabilityDefinition("ECK", "Elastic Cloud on Kubernetes", VersioningSystemId.Eck), - ["ece"] = new ApplicabilityDefinition("ECE", "Elastic Cloud Enterprise", VersioningSystemId.Ece), - ["self"] = new ApplicabilityDefinition("Self-Managed", "Self-managed Elastic deployments", VersioningSystemId.Self), - - // Product Applicability - ["ecctl"] = new ApplicabilityDefinition("ECCTL", "Elastic Cloud Control", VersioningSystemId.Ecctl), - ["curator"] = new ApplicabilityDefinition("Curator", "Curator", VersioningSystemId.Curator), - - // EDOT Products - ["edot-android"] = new ApplicabilityDefinition("EDOT Android", "Elastic Distribution of OpenTelemetry Android", VersioningSystemId.EdotAndroid), - ["edot-cf-aws"] = new ApplicabilityDefinition("EDOT CF AWS", "Elastic Distribution of OpenTelemetry Cloud Forwarder for AWS", VersioningSystemId.EdotCfAws), - ["edot-collector"] = new ApplicabilityDefinition("EDOT Collector", "Elastic Distribution of OpenTelemetry Collector", VersioningSystemId.EdotCollector), - ["edot-dotnet"] = new ApplicabilityDefinition("EDOT .NET", "Elastic Distribution of OpenTelemetry .NET", VersioningSystemId.EdotDotnet), - ["edot-ios"] = new ApplicabilityDefinition("EDOT iOS", "Elastic Distribution of OpenTelemetry iOS", VersioningSystemId.EdotIos), - ["edot-java"] = new ApplicabilityDefinition("EDOT Java", "Elastic Distribution of OpenTelemetry Java", VersioningSystemId.EdotJava), - ["edot-node"] = new ApplicabilityDefinition("EDOT Node.js", "Elastic Distribution of OpenTelemetry Node.js", VersioningSystemId.EdotNode), - ["edot-php"] = new ApplicabilityDefinition("EDOT PHP", "Elastic Distribution of OpenTelemetry PHP", VersioningSystemId.EdotPhp), - ["edot-python"] = new ApplicabilityDefinition("EDOT Python", "Elastic Distribution of OpenTelemetry Python", VersioningSystemId.EdotPython), - - // APM Agents - ["apm-agent-android"] = new ApplicabilityDefinition("APM Agent Android", "Application Performance Monitoring Agent for Android", VersioningSystemId.ApmAgentAndroid), - ["apm-agent-dotnet"] = new ApplicabilityDefinition("APM Agent .NET", "Application Performance Monitoring Agent for .NET", VersioningSystemId.ApmAgentDotnet), - ["apm-agent-go"] = new ApplicabilityDefinition("APM Agent Go", "Application Performance Monitoring Agent for Go", VersioningSystemId.ApmAgentGo), - ["apm-agent-ios"] = new ApplicabilityDefinition("APM Agent iOS", "Application Performance Monitoring Agent for iOS", VersioningSystemId.ApmAgentIos), - ["apm-agent-java"] = new ApplicabilityDefinition("APM Agent Java", "Application Performance Monitoring Agent for Java", VersioningSystemId.ApmAgentJava), - ["apm-agent-node"] = new ApplicabilityDefinition("APM Agent Node.js", "Application Performance Monitoring Agent for Node.js", VersioningSystemId.ApmAgentNode), - ["apm-agent-php"] = new ApplicabilityDefinition("APM Agent PHP", "Application Performance Monitoring Agent for PHP", VersioningSystemId.ApmAgentPhp), - ["apm-agent-python"] = new ApplicabilityDefinition("APM Agent Python", "Application Performance Monitoring Agent for Python", VersioningSystemId.ApmAgentPython), - ["apm-agent-ruby"] = new ApplicabilityDefinition("APM Agent Ruby", "Application Performance Monitoring Agent for Ruby", VersioningSystemId.ApmAgentRuby), - ["apm-agent-rum"] = new ApplicabilityDefinition("APM Agent RUM", "Application Performance Monitoring Agent for Real User Monitoring", VersioningSystemId.ApmAgentRum), - - // Generic product - ["product"] = new ApplicabilityDefinition("", "", VersioningSystemId.All) - }; - - public static ApplicabilityDefinition GetApplicabilityDefinition(string key) => Mappings.TryGetValue(key, out var definition) - ? definition - : throw new ArgumentException($"Unknown product key: {key}"); + // Stack + public static readonly ApplicabilityDefinition Stack = new("Stack", "Elastic Stack", VersioningSystemId.Stack); + + // Serverless + public static readonly ApplicabilityDefinition Serverless = new("Serverless", "Elastic Cloud Serverless", VersioningSystemId.Serverless); + public static readonly ApplicabilityDefinition ServerlessElasticsearch = new("Serverless Elasticsearch", "Serverless Elasticsearch projects", VersioningSystemId.ElasticsearchProject); + public static readonly ApplicabilityDefinition ServerlessObservability = new("Serverless Observability", "Serverless Observability projects", VersioningSystemId.ObservabilityProject); + public static readonly ApplicabilityDefinition ServerlessSecurity = new("Serverless Security", "Serverless Security projects", VersioningSystemId.SecurityProject); + + // Deployment + public static readonly ApplicabilityDefinition Ech = new("ECH", "Elastic Cloud Hosted", VersioningSystemId.Ess); + public static readonly ApplicabilityDefinition Eck = new("ECK", "Elastic Cloud on Kubernetes", VersioningSystemId.Eck); + public static readonly ApplicabilityDefinition Ece = new("ECE", "Elastic Cloud Enterprise", VersioningSystemId.Ece); + public static readonly ApplicabilityDefinition Self = new("Self-Managed", "Self-managed Elastic deployments", VersioningSystemId.Self); + + // Product Applicability + public static readonly ApplicabilityDefinition Ecctl = new("ECCTL", "Elastic Cloud Control", VersioningSystemId.Ecctl); + public static readonly ApplicabilityDefinition Curator = new("Curator", "Curator", VersioningSystemId.Curator); + + // EDOT Products + public static readonly ApplicabilityDefinition EdotAndroid = new("EDOT Android", "Elastic Distribution of OpenTelemetry Android", VersioningSystemId.EdotAndroid); + public static readonly ApplicabilityDefinition EdotCfAws = new("EDOT CF AWS", "Elastic Distribution of OpenTelemetry Cloud Forwarder for AWS", VersioningSystemId.EdotCfAws); + public static readonly ApplicabilityDefinition EdotCollector = new("EDOT Collector", "Elastic Distribution of OpenTelemetry Collector", VersioningSystemId.EdotCollector); + public static readonly ApplicabilityDefinition EdotDotnet = new("EDOT .NET", "Elastic Distribution of OpenTelemetry .NET", VersioningSystemId.EdotDotnet); + public static readonly ApplicabilityDefinition EdotIos = new("EDOT iOS", "Elastic Distribution of OpenTelemetry iOS", VersioningSystemId.EdotIos); + public static readonly ApplicabilityDefinition EdotJava = new("EDOT Java", "Elastic Distribution of OpenTelemetry Java", VersioningSystemId.EdotJava); + public static readonly ApplicabilityDefinition EdotNode = new("EDOT Node.js", "Elastic Distribution of OpenTelemetry Node.js", VersioningSystemId.EdotNode); + public static readonly ApplicabilityDefinition EdotPhp = new("EDOT PHP", "Elastic Distribution of OpenTelemetry PHP", VersioningSystemId.EdotPhp); + public static readonly ApplicabilityDefinition EdotPython = new("EDOT Python", "Elastic Distribution of OpenTelemetry Python", VersioningSystemId.EdotPython); + + // APM Agents + public static readonly ApplicabilityDefinition ApmAgentAndroid = new("APM Agent Android", "Application Performance Monitoring Agent for Android", VersioningSystemId.ApmAgentAndroid); + public static readonly ApplicabilityDefinition ApmAgentDotnet = new("APM Agent .NET", "Application Performance Monitoring Agent for .NET", VersioningSystemId.ApmAgentDotnet); + public static readonly ApplicabilityDefinition ApmAgentGo = new("APM Agent Go", "Application Performance Monitoring Agent for Go", VersioningSystemId.ApmAgentGo); + public static readonly ApplicabilityDefinition ApmAgentIos = new("APM Agent iOS", "Application Performance Monitoring Agent for iOS", VersioningSystemId.ApmAgentIos); + public static readonly ApplicabilityDefinition ApmAgentJava = new("APM Agent Java", "Application Performance Monitoring Agent for Java", VersioningSystemId.ApmAgentJava); + public static readonly ApplicabilityDefinition ApmAgentNode = new("APM Agent Node.js", "Application Performance Monitoring Agent for Node.js", VersioningSystemId.ApmAgentNode); + public static readonly ApplicabilityDefinition ApmAgentPhp = new("APM Agent PHP", "Application Performance Monitoring Agent for PHP", VersioningSystemId.ApmAgentPhp); + public static readonly ApplicabilityDefinition ApmAgentPython = new("APM Agent Python", "Application Performance Monitoring Agent for Python", VersioningSystemId.ApmAgentPython); + public static readonly ApplicabilityDefinition ApmAgentRuby = new("APM Agent Ruby", "Application Performance Monitoring Agent for Ruby", VersioningSystemId.ApmAgentRuby); + public static readonly ApplicabilityDefinition ApmAgentRum = new("APM Agent RUM", "Application Performance Monitoring Agent for Real User Monitoring", VersioningSystemId.ApmAgentRum); + + // Generic product + public static readonly ApplicabilityDefinition Product = new("", "", VersioningSystemId.All); } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index 3dde4e0c8..b2b231345 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -22,9 +22,9 @@ public IEnumerable GetApplicabilityItems() // Process Stack if (AppliesTo.Stack is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("stack"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(AppliesTo.Stack, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Stack; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(AppliesTo.Stack, applicabilityDef, versioningSystem)); } // Process Serverless @@ -32,9 +32,9 @@ public IEnumerable GetApplicabilityItems() { if (AppliesTo.Serverless.AllProjects is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(AppliesTo.Serverless.AllProjects, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Serverless; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(AppliesTo.Serverless.AllProjects, applicabilityDef, versioningSystem)); } else items.AddRange(ProcessServerlessProjects(AppliesTo.Serverless)); @@ -51,9 +51,9 @@ public IEnumerable GetApplicabilityItems() // Process Generic Product if (AppliesTo.Product is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("product"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(AppliesTo.Product, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Product; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(AppliesTo.Product, applicabilityDef, versioningSystem)); } return items; @@ -65,23 +65,23 @@ private IEnumerable ProcessServerlessProjects(ServerlessProje if (serverless.Elasticsearch is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless-elasticsearch"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(serverless.Elasticsearch, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.ServerlessElasticsearch; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(serverless.Elasticsearch, applicabilityDef, versioningSystem)); } if (serverless.Observability is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless-observability"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(serverless.Observability, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.ServerlessObservability; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(serverless.Observability, applicabilityDef, versioningSystem)); } if (serverless.Security is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("serverless-security"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(serverless.Security, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.ServerlessSecurity; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(serverless.Security, applicabilityDef, versioningSystem)); } return items; @@ -93,30 +93,30 @@ private IEnumerable ProcessDeploymentTypes(DeploymentApplicab if (deployment.Ess is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("ech"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Ess, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Ech; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Ess, applicabilityDef, versioningSystem)); } if (deployment.Eck is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("eck"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Eck, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Eck; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Eck, applicabilityDef, versioningSystem)); } if (deployment.Ece is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("ece"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Ece, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Ece; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Ece, applicabilityDef, versioningSystem)); } if (deployment.Self is not null) { - var productDef = ApplicabilityMappings.GetApplicabilityDefinition("self"); - var versioningSystem = VersionsConfig.GetVersioningSystem(productDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Self, productDef, versioningSystem)); + var applicabilityDef = ApplicabilityMappings.Self; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); + items.AddRange(ProcessApplicabilityCollection(deployment.Self, applicabilityDef, versioningSystem)); } return items; @@ -126,38 +126,37 @@ private IEnumerable ProcessProductApplicability(ProductApplic { var items = new List(); - // Process each product applicability property explicitly (AOT-compatible) - ProcessProductIfNotNull(productApplicability.Ecctl, "ecctl", items); - ProcessProductIfNotNull(productApplicability.Curator, "curator", items); - ProcessProductIfNotNull(productApplicability.EdotAndroid, "edot-android", items); - ProcessProductIfNotNull(productApplicability.EdotCfAws, "edot-cf-aws", items); - ProcessProductIfNotNull(productApplicability.EdotCollector, "edot-collector", items); - ProcessProductIfNotNull(productApplicability.EdotDotnet, "edot-dotnet", items); - ProcessProductIfNotNull(productApplicability.EdotIos, "edot-ios", items); - ProcessProductIfNotNull(productApplicability.EdotJava, "edot-java", items); - ProcessProductIfNotNull(productApplicability.EdotNode, "edot-node", items); - ProcessProductIfNotNull(productApplicability.EdotPhp, "edot-php", items); - ProcessProductIfNotNull(productApplicability.EdotPython, "edot-python", items); - ProcessProductIfNotNull(productApplicability.ApmAgentAndroid, "apm-agent-android", items); - ProcessProductIfNotNull(productApplicability.ApmAgentDotnet, "apm-agent-dotnet", items); - ProcessProductIfNotNull(productApplicability.ApmAgentGo, "apm-agent-go", items); - ProcessProductIfNotNull(productApplicability.ApmAgentIos, "apm-agent-ios", items); - ProcessProductIfNotNull(productApplicability.ApmAgentJava, "apm-agent-java", items); - ProcessProductIfNotNull(productApplicability.ApmAgentNode, "apm-agent-node", items); - ProcessProductIfNotNull(productApplicability.ApmAgentPhp, "apm-agent-php", items); - ProcessProductIfNotNull(productApplicability.ApmAgentPython, "apm-agent-python", items); - ProcessProductIfNotNull(productApplicability.ApmAgentRuby, "apm-agent-ruby", items); - ProcessProductIfNotNull(productApplicability.ApmAgentRum, "apm-agent-rum", items); + // Process each product applicability property explicitly (AOT-compatible) using strongly-typed definitions + ProcessProductIfNotNull(productApplicability.Ecctl, ApplicabilityMappings.Ecctl, items); + ProcessProductIfNotNull(productApplicability.Curator, ApplicabilityMappings.Curator, items); + ProcessProductIfNotNull(productApplicability.EdotAndroid, ApplicabilityMappings.EdotAndroid, items); + ProcessProductIfNotNull(productApplicability.EdotCfAws, ApplicabilityMappings.EdotCfAws, items); + ProcessProductIfNotNull(productApplicability.EdotCollector, ApplicabilityMappings.EdotCollector, items); + ProcessProductIfNotNull(productApplicability.EdotDotnet, ApplicabilityMappings.EdotDotnet, items); + ProcessProductIfNotNull(productApplicability.EdotIos, ApplicabilityMappings.EdotIos, items); + ProcessProductIfNotNull(productApplicability.EdotJava, ApplicabilityMappings.EdotJava, items); + ProcessProductIfNotNull(productApplicability.EdotNode, ApplicabilityMappings.EdotNode, items); + ProcessProductIfNotNull(productApplicability.EdotPhp, ApplicabilityMappings.EdotPhp, items); + ProcessProductIfNotNull(productApplicability.EdotPython, ApplicabilityMappings.EdotPython, items); + ProcessProductIfNotNull(productApplicability.ApmAgentAndroid, ApplicabilityMappings.ApmAgentAndroid, items); + ProcessProductIfNotNull(productApplicability.ApmAgentDotnet, ApplicabilityMappings.ApmAgentDotnet, items); + ProcessProductIfNotNull(productApplicability.ApmAgentGo, ApplicabilityMappings.ApmAgentGo, items); + ProcessProductIfNotNull(productApplicability.ApmAgentIos, ApplicabilityMappings.ApmAgentIos, items); + ProcessProductIfNotNull(productApplicability.ApmAgentJava, ApplicabilityMappings.ApmAgentJava, items); + ProcessProductIfNotNull(productApplicability.ApmAgentNode, ApplicabilityMappings.ApmAgentNode, items); + ProcessProductIfNotNull(productApplicability.ApmAgentPhp, ApplicabilityMappings.ApmAgentPhp, items); + ProcessProductIfNotNull(productApplicability.ApmAgentPython, ApplicabilityMappings.ApmAgentPython, items); + ProcessProductIfNotNull(productApplicability.ApmAgentRuby, ApplicabilityMappings.ApmAgentRuby, items); + ProcessProductIfNotNull(productApplicability.ApmAgentRum, ApplicabilityMappings.ApmAgentRum, items); return items; } - private void ProcessProductIfNotNull(AppliesCollection? collection, string productKey, List items) + private void ProcessProductIfNotNull(AppliesCollection? collection, ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, List items) { if (collection is null) return; - var applicabilityDefinition = ApplicabilityMappings.GetApplicabilityDefinition(productKey); var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDefinition.VersioningSystemId); items.AddRange(ProcessApplicabilityCollection(collection, applicabilityDefinition, versioningSystem)); } From 6c2ffb841fc43976082e9c4711a7b02676eadf19 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Sun, 14 Sep 2025 21:46:50 +0200 Subject: [PATCH 7/7] Refactor repeating logic --- .../Myst/Components/ApplicableToViewModel.cs | 172 +++++++----------- 1 file changed, 62 insertions(+), 110 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index b2b231345..ef50735ab 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -15,152 +15,104 @@ public class ApplicableToViewModel public required ApplicableTo AppliesTo { get; init; } public required VersionsConfiguration VersionsConfig { get; init; } + // Dictionary mapping property selectors to their applicability definitions + private static readonly Dictionary, ApplicabilityMappings.ApplicabilityDefinition> DeploymentMappings = new() + { + [d => d.Ess] = ApplicabilityMappings.Ech, + [d => d.Eck] = ApplicabilityMappings.Eck, + [d => d.Ece] = ApplicabilityMappings.Ece, + [d => d.Self] = ApplicabilityMappings.Self + }; + + private static readonly Dictionary, ApplicabilityMappings.ApplicabilityDefinition> ServerlessMappings = new() + { + [s => s.Elasticsearch] = ApplicabilityMappings.ServerlessElasticsearch, + [s => s.Observability] = ApplicabilityMappings.ServerlessObservability, + [s => s.Security] = ApplicabilityMappings.ServerlessSecurity + }; + + private static readonly Dictionary, ApplicabilityMappings.ApplicabilityDefinition> ProductMappings = new() + { + [p => p.Ecctl] = ApplicabilityMappings.Ecctl, + [p => p.Curator] = ApplicabilityMappings.Curator, + [p => p.EdotAndroid] = ApplicabilityMappings.EdotAndroid, + [p => p.EdotCfAws] = ApplicabilityMappings.EdotCfAws, + [p => p.EdotCollector] = ApplicabilityMappings.EdotCollector, + [p => p.EdotDotnet] = ApplicabilityMappings.EdotDotnet, + [p => p.EdotIos] = ApplicabilityMappings.EdotIos, + [p => p.EdotJava] = ApplicabilityMappings.EdotJava, + [p => p.EdotNode] = ApplicabilityMappings.EdotNode, + [p => p.EdotPhp] = ApplicabilityMappings.EdotPhp, + [p => p.EdotPython] = ApplicabilityMappings.EdotPython, + [p => p.ApmAgentAndroid] = ApplicabilityMappings.ApmAgentAndroid, + [p => p.ApmAgentDotnet] = ApplicabilityMappings.ApmAgentDotnet, + [p => p.ApmAgentGo] = ApplicabilityMappings.ApmAgentGo, + [p => p.ApmAgentIos] = ApplicabilityMappings.ApmAgentIos, + [p => p.ApmAgentJava] = ApplicabilityMappings.ApmAgentJava, + [p => p.ApmAgentNode] = ApplicabilityMappings.ApmAgentNode, + [p => p.ApmAgentPhp] = ApplicabilityMappings.ApmAgentPhp, + [p => p.ApmAgentPython] = ApplicabilityMappings.ApmAgentPython, + [p => p.ApmAgentRuby] = ApplicabilityMappings.ApmAgentRuby, + [p => p.ApmAgentRum] = ApplicabilityMappings.ApmAgentRum + }; + public IEnumerable GetApplicabilityItems() { var items = new List(); // Process Stack if (AppliesTo.Stack is not null) - { - var applicabilityDef = ApplicabilityMappings.Stack; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(AppliesTo.Stack, applicabilityDef, versioningSystem)); - } + items.AddRange(ProcessSingleCollection(AppliesTo.Stack, ApplicabilityMappings.Stack)); // Process Serverless if (AppliesTo.Serverless is not null) { - if (AppliesTo.Serverless.AllProjects is not null) - { - var applicabilityDef = ApplicabilityMappings.Serverless; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(AppliesTo.Serverless.AllProjects, applicabilityDef, versioningSystem)); - } - else - items.AddRange(ProcessServerlessProjects(AppliesTo.Serverless)); + items.AddRange(AppliesTo.Serverless.AllProjects is not null + ? ProcessSingleCollection(AppliesTo.Serverless.AllProjects, ApplicabilityMappings.Serverless) + : ProcessMappedCollections(AppliesTo.Serverless, ServerlessMappings)); } // Process Deployment if (AppliesTo.Deployment is not null) - items.AddRange(ProcessDeploymentTypes(AppliesTo.Deployment)); + items.AddRange(ProcessMappedCollections(AppliesTo.Deployment, DeploymentMappings)); // Process Product Applicability if (AppliesTo.ProductApplicability is not null) - items.AddRange(ProcessProductApplicability(AppliesTo.ProductApplicability)); + items.AddRange(ProcessMappedCollections(AppliesTo.ProductApplicability, ProductMappings)); // Process Generic Product if (AppliesTo.Product is not null) - { - var applicabilityDef = ApplicabilityMappings.Product; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(AppliesTo.Product, applicabilityDef, versioningSystem)); - } + items.AddRange(ProcessSingleCollection(AppliesTo.Product, ApplicabilityMappings.Product)); return items; } - private IEnumerable ProcessServerlessProjects(ServerlessProjectApplicability serverless) + /// + /// Processes a single collection with its corresponding applicability definition + /// + private IEnumerable ProcessSingleCollection(AppliesCollection collection, ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition) { - var items = new List(); - - if (serverless.Elasticsearch is not null) - { - var applicabilityDef = ApplicabilityMappings.ServerlessElasticsearch; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(serverless.Elasticsearch, applicabilityDef, versioningSystem)); - } - - if (serverless.Observability is not null) - { - var applicabilityDef = ApplicabilityMappings.ServerlessObservability; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(serverless.Observability, applicabilityDef, versioningSystem)); - } - - if (serverless.Security is not null) - { - var applicabilityDef = ApplicabilityMappings.ServerlessSecurity; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(serverless.Security, applicabilityDef, versioningSystem)); - } - - return items; + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDefinition.VersioningSystemId); + return ProcessApplicabilityCollection(collection, applicabilityDefinition, versioningSystem); } - private IEnumerable ProcessDeploymentTypes(DeploymentApplicability deployment) + /// + /// Processes multiple collections using a mapping dictionary to eliminate repetitive code + /// + private IEnumerable ProcessMappedCollections(T source, Dictionary, ApplicabilityMappings.ApplicabilityDefinition> mappings) { var items = new List(); - if (deployment.Ess is not null) - { - var applicabilityDef = ApplicabilityMappings.Ech; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Ess, applicabilityDef, versioningSystem)); - } - - if (deployment.Eck is not null) + foreach (var (propertySelector, applicabilityDefinition) in mappings) { - var applicabilityDef = ApplicabilityMappings.Eck; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Eck, applicabilityDef, versioningSystem)); + var collection = propertySelector(source); + if (collection is not null) + items.AddRange(ProcessSingleCollection(collection, applicabilityDefinition)); } - if (deployment.Ece is not null) - { - var applicabilityDef = ApplicabilityMappings.Ece; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Ece, applicabilityDef, versioningSystem)); - } - - if (deployment.Self is not null) - { - var applicabilityDef = ApplicabilityMappings.Self; - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDef.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(deployment.Self, applicabilityDef, versioningSystem)); - } - - return items; - } - - private IEnumerable ProcessProductApplicability(ProductApplicability productApplicability) - { - var items = new List(); - - // Process each product applicability property explicitly (AOT-compatible) using strongly-typed definitions - ProcessProductIfNotNull(productApplicability.Ecctl, ApplicabilityMappings.Ecctl, items); - ProcessProductIfNotNull(productApplicability.Curator, ApplicabilityMappings.Curator, items); - ProcessProductIfNotNull(productApplicability.EdotAndroid, ApplicabilityMappings.EdotAndroid, items); - ProcessProductIfNotNull(productApplicability.EdotCfAws, ApplicabilityMappings.EdotCfAws, items); - ProcessProductIfNotNull(productApplicability.EdotCollector, ApplicabilityMappings.EdotCollector, items); - ProcessProductIfNotNull(productApplicability.EdotDotnet, ApplicabilityMappings.EdotDotnet, items); - ProcessProductIfNotNull(productApplicability.EdotIos, ApplicabilityMappings.EdotIos, items); - ProcessProductIfNotNull(productApplicability.EdotJava, ApplicabilityMappings.EdotJava, items); - ProcessProductIfNotNull(productApplicability.EdotNode, ApplicabilityMappings.EdotNode, items); - ProcessProductIfNotNull(productApplicability.EdotPhp, ApplicabilityMappings.EdotPhp, items); - ProcessProductIfNotNull(productApplicability.EdotPython, ApplicabilityMappings.EdotPython, items); - ProcessProductIfNotNull(productApplicability.ApmAgentAndroid, ApplicabilityMappings.ApmAgentAndroid, items); - ProcessProductIfNotNull(productApplicability.ApmAgentDotnet, ApplicabilityMappings.ApmAgentDotnet, items); - ProcessProductIfNotNull(productApplicability.ApmAgentGo, ApplicabilityMappings.ApmAgentGo, items); - ProcessProductIfNotNull(productApplicability.ApmAgentIos, ApplicabilityMappings.ApmAgentIos, items); - ProcessProductIfNotNull(productApplicability.ApmAgentJava, ApplicabilityMappings.ApmAgentJava, items); - ProcessProductIfNotNull(productApplicability.ApmAgentNode, ApplicabilityMappings.ApmAgentNode, items); - ProcessProductIfNotNull(productApplicability.ApmAgentPhp, ApplicabilityMappings.ApmAgentPhp, items); - ProcessProductIfNotNull(productApplicability.ApmAgentPython, ApplicabilityMappings.ApmAgentPython, items); - ProcessProductIfNotNull(productApplicability.ApmAgentRuby, ApplicabilityMappings.ApmAgentRuby, items); - ProcessProductIfNotNull(productApplicability.ApmAgentRum, ApplicabilityMappings.ApmAgentRum, items); - return items; } - private void ProcessProductIfNotNull(AppliesCollection? collection, ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition, List items) - { - if (collection is null) - return; - - var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDefinition.VersioningSystemId); - items.AddRange(ProcessApplicabilityCollection(collection, applicabilityDefinition, versioningSystem)); - } - private IEnumerable ProcessApplicabilityCollection( AppliesCollection applications, ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition,