Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,34 @@
// 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;
using Elastic.Documentation.Configuration.LegacyUrlMappings;
using Elastic.Documentation.Configuration.Products;

namespace Elastic.Documentation.Configuration.Versions;

public interface IVersionInferrerService
{
VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<LegacyPageMapping>? legacyPages);
VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<LegacyPageMapping>? legacyPages, IReadOnlyCollection<Product>? products, ApplicableTo? applicableTo);
}

public class ProductVersionInferrerService(ProductsConfiguration productsConfiguration, VersionsConfiguration versionsConfiguration) : IVersionInferrerService
{
private ProductsConfiguration ProductsConfiguration { get; } = productsConfiguration;
private VersionsConfiguration VersionsConfiguration { get; } = versionsConfiguration;
public VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<LegacyPageMapping>? legacyPages)
public VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<LegacyPageMapping>? legacyPages, IReadOnlyCollection<Product>? products, ApplicableTo? applicableTo)
{
var versioning = legacyPages is not null && legacyPages.Count > 0
? legacyPages.ElementAt(0).Product.VersioningSystem! // If the page has a legacy page mapping, use the versioning system of the legacy page
: ProductsConfiguration.Products.TryGetValue(repositoryName, out var belonging)
if (legacyPages is { Count: > 0 })
return legacyPages.ElementAt(0).Product.VersioningSystem!; // If the page has legacy mappings, use the versioning system of the first mapping's product

if (applicableTo is not null)
{
var versioningFromApplicability = VersioningFromApplicability(applicableTo); // Try to infer the versioning system from the applicability metadata
if (versioningFromApplicability is not null)
return versioningFromApplicability;
}

var versioning = ProductsConfiguration.Products.TryGetValue(repositoryName, out var belonging)
? belonging.VersioningSystem! //If the page's docset has a name with a direct product match, use the versioning system of the product
: ProductsConfiguration.Products.Values.SingleOrDefault(p =>
p.Repository is not null && p.Repository.Equals(repositoryName, StringComparison.OrdinalIgnoreCase)) is { } repositoryMatch
Expand All @@ -29,11 +38,62 @@ public VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<

return versioning;
}

private VersioningSystem? VersioningFromApplicability(ApplicableTo applicableTo)
{
// Priority 1: Product applicability
var product = ProductFromApplicability(applicableTo.ProductApplicability);
if (product?.VersioningSystem is not null)
return product.VersioningSystem;

// Priority 2: Stack applicability
if (applicableTo.Stack is not null)
return VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack];
// Priority 3: Deployment applicability
if (applicableTo.Deployment is not null)
{
var versioning = applicableTo.Deployment switch
{
{ Ece: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.Ece],
{ Eck: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.Eck],
{ Ess: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.Ess],
{ Self: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.Self],
_ => null
};
if (versioning is not null)
return versioning;
}

// Priority 4: Serverless applicability
if (applicableTo.Serverless is not null)
{
var versioning = applicableTo.Serverless switch
{
{ Elasticsearch: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ElasticsearchProject],
{ Observability: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ObservabilityProject],
{ Security: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.SecurityProject],
_ => null
};
if (versioning is not null)
return versioning;
}

return null;
}
private Product? ProductFromApplicability(ProductApplicability? productApplicability)
{
if (productApplicability is null)
return null;

var productId = ProductApplicabilityConversion.ProductApplicabilityToProductId(productApplicability);

return productId is null ? null : ProductsConfiguration.Products.GetValueOrDefault(productId);
}
}

public class NoopVersionInferrer : IVersionInferrerService
{
public VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<LegacyPageMapping>? legacyPages) => new()
public VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<LegacyPageMapping>? legacyPages, IReadOnlyCollection<Product>? products, ApplicableTo? applicableTo) => new()
{
Id = VersioningSystemId.Stack,
Base = new SemVersion(0, 0, 0),
Expand Down
116 changes: 0 additions & 116 deletions src/Elastic.Documentation/AppliesTo/ApplicableTo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,119 +236,3 @@ public override string ToString()
return sb.ToString();
}
}

[YamlSerializable]
public record ProductApplicability
{
[YamlMember(Alias = "ecctl")]
public AppliesCollection? Ecctl { get; set; }

[YamlMember(Alias = "curator")]
public AppliesCollection? Curator { get; set; }

[YamlMember(Alias = "apm-agent-android")]
public AppliesCollection? ApmAgentAndroid { get; set; }

[YamlMember(Alias = "apm-agent-dotnet")]
public AppliesCollection? ApmAgentDotnet { get; set; }

[YamlMember(Alias = "apm-agent-go")]
public AppliesCollection? ApmAgentGo { get; set; }

[YamlMember(Alias = "apm-agent-ios")]
public AppliesCollection? ApmAgentIos { get; set; }

[YamlMember(Alias = "apm-agent-java")]
public AppliesCollection? ApmAgentJava { get; set; }

[YamlMember(Alias = "apm-agent-node")]
public AppliesCollection? ApmAgentNode { get; set; }

[YamlMember(Alias = "apm-agent-php")]
public AppliesCollection? ApmAgentPhp { get; set; }

[YamlMember(Alias = "apm-agent-python")]
public AppliesCollection? ApmAgentPython { get; set; }

[YamlMember(Alias = "apm-agent-ruby")]
public AppliesCollection? ApmAgentRuby { get; set; }

[YamlMember(Alias = "apm-agent-rum-js")]
public AppliesCollection? ApmAgentRumJs { get; set; }

[YamlMember(Alias = "edot-ios")]
public AppliesCollection? EdotIos { get; set; }

[YamlMember(Alias = "edot-android")]
public AppliesCollection? EdotAndroid { get; set; }

[YamlMember(Alias = "edot-dotnet")]
public AppliesCollection? EdotDotnet { get; set; }

[YamlMember(Alias = "edot-java")]
public AppliesCollection? EdotJava { get; set; }

[YamlMember(Alias = "edot-node")]
public AppliesCollection? EdotNode { get; set; }

[YamlMember(Alias = "edot-php")]
public AppliesCollection? EdotPhp { get; set; }

[YamlMember(Alias = "edot-python")]
public AppliesCollection? EdotPython { get; set; }

[YamlMember(Alias = "edot-cf-aws")]
public AppliesCollection? EdotCfAws { get; set; }

[YamlMember(Alias = "edot-cf-azure")]
public AppliesCollection? EdotCfAzure { get; set; }

[YamlMember(Alias = "edot-cf-gcp")]
public AppliesCollection? EdotCfGcp { get; set; }

[YamlMember(Alias = "edot-collector")]
public AppliesCollection? EdotCollector { get; set; }

/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
var hasContent = false;

void AppendProduct(string name, AppliesCollection? value)
{
if (value is null)
return;
if (hasContent)
_ = sb.Append(", ");
_ = sb.Append(name).Append('=').Append(value);
hasContent = true;
}

AppendProduct("ecctl", Ecctl);
AppendProduct("curator", Curator);
AppendProduct("apm-agent-android", ApmAgentAndroid);
AppendProduct("apm-agent-dotnet", ApmAgentDotnet);
AppendProduct("apm-agent-go", ApmAgentGo);
AppendProduct("apm-agent-ios", ApmAgentIos);
AppendProduct("apm-agent-java", ApmAgentJava);
AppendProduct("apm-agent-node", ApmAgentNode);
AppendProduct("apm-agent-php", ApmAgentPhp);
AppendProduct("apm-agent-python", ApmAgentPython);
AppendProduct("apm-agent-ruby", ApmAgentRuby);
AppendProduct("apm-agent-rum-js", ApmAgentRumJs);
AppendProduct("edot-ios", EdotIos);
AppendProduct("edot-android", EdotAndroid);
AppendProduct("edot-dotnet", EdotDotnet);
AppendProduct("edot-java", EdotJava);
AppendProduct("edot-node", EdotNode);
AppendProduct("edot-php", EdotPhp);
AppendProduct("edot-python", EdotPython);
AppendProduct("edot-cf-aws", EdotCfAws);
AppendProduct("edot-cf-azure", EdotCfAzure);
AppendProduct("edot-cf-gcp", EdotCfGcp);
AppendProduct("edot-collector", EdotCollector);

return sb.ToString();
}
}
155 changes: 155 additions & 0 deletions src/Elastic.Documentation/AppliesTo/ProductApplicability.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// 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.Text;
using YamlDotNet.Serialization;

namespace Elastic.Documentation.AppliesTo;

[YamlSerializable]
public record ProductApplicability
{
[YamlMember(Alias = "ecctl")]
public AppliesCollection? Ecctl { get; set; }

[YamlMember(Alias = "curator")]
public AppliesCollection? Curator { get; set; }

[YamlMember(Alias = "apm-agent-android")]
public AppliesCollection? ApmAgentAndroid { get; set; }

[YamlMember(Alias = "apm-agent-dotnet")]
public AppliesCollection? ApmAgentDotnet { get; set; }

[YamlMember(Alias = "apm-agent-go")]
public AppliesCollection? ApmAgentGo { get; set; }

[YamlMember(Alias = "apm-agent-ios")]
public AppliesCollection? ApmAgentIos { get; set; }

[YamlMember(Alias = "apm-agent-java")]
public AppliesCollection? ApmAgentJava { get; set; }

[YamlMember(Alias = "apm-agent-node")]
public AppliesCollection? ApmAgentNode { get; set; }

[YamlMember(Alias = "apm-agent-php")]
public AppliesCollection? ApmAgentPhp { get; set; }

[YamlMember(Alias = "apm-agent-python")]
public AppliesCollection? ApmAgentPython { get; set; }

[YamlMember(Alias = "apm-agent-ruby")]
public AppliesCollection? ApmAgentRuby { get; set; }

[YamlMember(Alias = "apm-agent-rum-js")]
public AppliesCollection? ApmAgentRumJs { get; set; }

[YamlMember(Alias = "edot-ios")]
public AppliesCollection? EdotIos { get; set; }

[YamlMember(Alias = "edot-android")]
public AppliesCollection? EdotAndroid { get; set; }

[YamlMember(Alias = "edot-dotnet")]
public AppliesCollection? EdotDotnet { get; set; }

[YamlMember(Alias = "edot-java")]
public AppliesCollection? EdotJava { get; set; }

[YamlMember(Alias = "edot-node")]
public AppliesCollection? EdotNode { get; set; }

[YamlMember(Alias = "edot-php")]
public AppliesCollection? EdotPhp { get; set; }

[YamlMember(Alias = "edot-python")]
public AppliesCollection? EdotPython { get; set; }

[YamlMember(Alias = "edot-cf-aws")]
public AppliesCollection? EdotCfAws { get; set; }

[YamlMember(Alias = "edot-cf-azure")]
public AppliesCollection? EdotCfAzure { get; set; }

[YamlMember(Alias = "edot-cf-gcp")]
public AppliesCollection? EdotCfGcp { get; set; }

[YamlMember(Alias = "edot-collector")]
public AppliesCollection? EdotCollector { get; set; }

/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
var hasContent = false;

void AppendProduct(string name, AppliesCollection? value)
{
if (value is null)
return;
if (hasContent)
_ = sb.Append(": not null } => ");
_ = sb.Append(name).Append('=').Append(value);
hasContent = true;
}

AppendProduct("ecctl", Ecctl);
AppendProduct("curator", Curator);
AppendProduct("apm-agent-android", ApmAgentAndroid);
AppendProduct("apm-agent-dotnet", ApmAgentDotnet);
AppendProduct("apm-agent-go", ApmAgentGo);
AppendProduct("apm-agent-ios", ApmAgentIos);
AppendProduct("apm-agent-java", ApmAgentJava);
AppendProduct("apm-agent-node", ApmAgentNode);
AppendProduct("apm-agent-php", ApmAgentPhp);
AppendProduct("apm-agent-python", ApmAgentPython);
AppendProduct("apm-agent-ruby", ApmAgentRuby);
AppendProduct("apm-agent-rum-js", ApmAgentRumJs);
AppendProduct("edot-ios", EdotIos);
AppendProduct("edot-android", EdotAndroid);
AppendProduct("edot-dotnet", EdotDotnet);
AppendProduct("edot-java", EdotJava);
AppendProduct("edot-node", EdotNode);
AppendProduct("edot-php", EdotPhp);
AppendProduct("edot-python", EdotPython);
AppendProduct("edot-cf-aws", EdotCfAws);
AppendProduct("edot-cf-azure", EdotCfAzure);
AppendProduct("edot-cf-gcp", EdotCfGcp);
AppendProduct("edot-collector", EdotCollector);

return sb.ToString();
}
}

public static class ProductApplicabilityConversion
{
public static string? ProductApplicabilityToProductId(ProductApplicability p) => p switch
{
{ Ecctl: not null } => "cloud-control-ecctl",
{ Curator: not null } => "curator",
{ ApmAgentAndroid: not null } => "edot-android",
{ ApmAgentDotnet: not null } => "apm-agent-dotnet",
{ ApmAgentGo: not null } => "apm-agent-go",
{ ApmAgentIos: not null } => "edot-ios",
{ ApmAgentJava: not null } => "apm-agent-java",
{ ApmAgentNode: not null } => "apm-agent-node",
{ ApmAgentPhp: not null } => "apm-agent-php",
{ ApmAgentPython: not null } => "apm-agent-python",
{ ApmAgentRuby: not null } => "apm-agent-ruby",
{ ApmAgentRumJs: not null } => "apm-agent-rum-js",
{ EdotIos: not null } => "edot-ios",
{ EdotAndroid: not null } => "edot-android",
{ EdotDotnet: not null } => "edot-dotnet",
{ EdotJava: not null } => "edot-java",
{ EdotNode: not null } => "edot-node",
{ EdotPhp: not null } => "edot-php",
{ EdotPython: not null } => "edot-python",
{ EdotCfAws: not null } => "edot-cf-aws",
{ EdotCfAzure: not null } => "edot-cf-azure",
{ EdotCfGcp: not null } => "edot-cf-gcp",
{ EdotCollector: not null } => "edot-collector",
_ => null
};
}
3 changes: 2 additions & 1 deletion src/Elastic.Markdown/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ private async Task<RenderResult> RenderLayout(MarkdownFile markdown, MarkdownDoc

}

var pageVersioning = VersionInferrerService.InferVersion(DocumentationSet.Context.Git.RepositoryName, legacyPages);
var pageVersioning = VersionInferrerService.InferVersion(DocumentationSet.Context.Git.RepositoryName, legacyPages, markdown.YamlFrontMatter?.Products, markdown.YamlFrontMatter?.AppliesTo);

var currentBaseVersion = $"{pageVersioning.Base.Major}.{pageVersioning.Base.Minor}+";

Expand Down Expand Up @@ -154,6 +154,7 @@ private async Task<RenderResult> RenderLayout(MarkdownFile markdown, MarkdownDoc
LegacyPages = legacyPages?.ToArray(),
VersionDropdownItems = VersionDropDownItemViewModel.FromLegacyPageMappings(legacyPages?.ToArray()),
Products = pageProducts,
VersioningSystem = pageVersioning,
VersionsConfig = DocumentationSet.Context.VersionsConfiguration,
StructuredBreadcrumbsJson = structuredBreadcrumbsJsonString
});
Expand Down
Loading
Loading