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..138b04a0f --- /dev/null +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityMappings.cs @@ -0,0 +1,57 @@ +// 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); + + // 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/ApplicabilityRenderer.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs new file mode 100644 index 000000000..87a683c89 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs @@ -0,0 +1,141 @@ +// 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 BadgeLifecycleText, + string Version, + string TooltipText, + string LifecycleClass, + bool ShowLifecycleName, + 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 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( + BadgeLifecycleText: badgeLifecycleText, + Version: version, + TooltipText: tooltipText, + LifecycleClass: lifecycleClass, + ShowLifecycleName: 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 BuildBadgeLifecycleText( + Applicability applicability, + VersioningSystem versioningSystem, + SemVersion? realVersion, + AppliesCollection allApplications) + { + var badgeText = ""; + 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", + ProductLifecycle.Unavailable => "Unavailable", + _ => 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..f35d221e9 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml @@ -1,357 +1,29 @@ -@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 + @if (!string.IsNullOrEmpty(item.Key) && (item.RenderData.ShowLifecycleName || item.RenderData.ShowVersion || !string.IsNullOrEmpty(item.RenderData.BadgeLifecycleText))) { - 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) + + } + + @if (item.RenderData.ShowLifecycleName) { - tooltipText = $"{lifecycleFull} on {keyFull} version {realVersion} and later unless otherwise specified."; + @item.Applicability.GetLifeCycleName() } - else + @if (item.RenderData.ShowVersion) { - 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 - }; + + @item.RenderData.Version + } - - } - else - { - 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 - { - 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; - } - - @if (!string.IsNullOrEmpty(key) && (showLifeCycle || showVersion)) + else { - + @item.RenderData.BadgeLifecycleText } - - @if (showLifeCycle) - { - @applicability.GetLifeCycleName() - } - @if (showVersion) - { - @if (versioningSystem.Current >= applicability.Version!) - { - - @applicability.Version - - } - else - { - @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..ef50735ab 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -9,7 +9,127 @@ 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; } + + // 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) + items.AddRange(ProcessSingleCollection(AppliesTo.Stack, ApplicabilityMappings.Stack)); + + // Process Serverless + if (AppliesTo.Serverless is not null) + { + 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(ProcessMappedCollections(AppliesTo.Deployment, DeploymentMappings)); + + // Process Product Applicability + if (AppliesTo.ProductApplicability is not null) + items.AddRange(ProcessMappedCollections(AppliesTo.ProductApplicability, ProductMappings)); + + // Process Generic Product + if (AppliesTo.Product is not null) + items.AddRange(ProcessSingleCollection(AppliesTo.Product, ApplicabilityMappings.Product)); + + return items; + } + + /// + /// Processes a single collection with its corresponding applicability definition + /// + private IEnumerable ProcessSingleCollection(AppliesCollection collection, ApplicabilityMappings.ApplicabilityDefinition applicabilityDefinition) + { + var versioningSystem = VersionsConfig.GetVersioningSystem(applicabilityDefinition.VersioningSystemId); + return ProcessApplicabilityCollection(collection, applicabilityDefinition, versioningSystem); + } + + /// + /// Processes multiple collections using a mapping dictionary to eliminate repetitive code + /// + private IEnumerable ProcessMappedCollections(T source, Dictionary, ApplicabilityMappings.ApplicabilityDefinition> mappings) + { + var items = new List(); + + foreach (var (propertySelector, applicabilityDefinition) in mappings) + { + var collection = propertySelector(source); + if (collection is not null) + items.AddRange(ProcessSingleCollection(collection, applicabilityDefinition)); + } + + return items; + } + + 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`` () =