Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,39 @@
// 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)
// docs-content handles content from multiple products which should preferably be inferred through frontmatter metadata
if (repositoryName.Equals("docs-content", StringComparison.OrdinalIgnoreCase))
{
if (products is { Count: > 0 }) // If the page is from multiple products, use the versioning system of the first product
return products.First().VersioningSystem!;
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;
}
}
if (legacyPages is { Count: > 0 })
return legacyPages.ElementAt(0).Product.VersioningSystem!; // If the page has a legacy page mapping, use the versioning system of the legacy page

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 +43,59 @@ public VersioningSystem InferVersion(string repositoryName, IReadOnlyCollection<

return versioning;
}

private VersioningSystem? VersioningFromApplicability(ApplicableTo applicableTo)
{
VersioningSystem? versioning = null;
if (applicableTo.ProductApplicability is not null)
{
versioning = applicableTo.ProductApplicability switch
{
{ ApmAgentAndroid: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentAndroid],
{ ApmAgentIos: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentIos],
{ ApmAgentJava: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentJava],
{ ApmAgentDotnet: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentDotnet],
{ ApmAgentGo: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentGo],
{ ApmAgentNode: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentNode],
{ ApmAgentPhp: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentPhp],
{ ApmAgentPython: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentPython],
{ ApmAgentRuby: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentRuby],
{ ApmAgentRumJs: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.ApmAgentRumJs],
{ Curator: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.Curator],
{ Ecctl: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.Ecctl],
{ EdotAndroid: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotAndroid],
{ EdotCfAws: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotCfAws],
{ EdotCfAzure: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotCfAzure],
{ EdotCollector: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotCollector],
{ EdotIos: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotIos],
{ EdotJava: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotJava],
{ EdotNode: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotNode],
{ EdotDotnet: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotDotnet],
{ EdotPhp: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotPhp],
{ EdotPython: not null } => VersionsConfiguration.VersioningSystems[VersioningSystemId.EdotPython],
_ => null
};
}
if (versioning is not null)
return versioning;
if (applicableTo.Deployment is not null)
{
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
};
}
return versioning;
}
}

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
3 changes: 2 additions & 1 deletion src/Elastic.Markdown/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private async Task<RenderResult> RenderLayout(MarkdownFile markdown, MarkdownDoc
fullNavigationRenderResult
);

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 @@ -147,6 +147,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
15 changes: 10 additions & 5 deletions src/Elastic.Markdown/Layout/_TableOfContents.cshtml
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
@using Elastic.Documentation.Configuration.Versions
@inherits RazorSlice<Elastic.Markdown.MarkdownLayoutViewModel>
<aside class="sidebar md:block w-full lg:max-w-65 md:order-2 px-6 md:px-10 lg:px-0">
<nav id="toc-nav" class="sidebar-nav lg:h-full flex flex-row-reverse lg:block items-center gap-4">
<div class="mt-6">
<version-dropdown all-versions-url="@Model.AllVersionsUrl" current-version='@(Model.CurrentVersion)' items='@(new HtmlString(Model.VersionDropdownSerializedModel))'>
<div class="h-8">@*height of dropdown button*@</div>
</version-dropdown>
</div>
@if (Model.VersioningSystem.Id is not
(VersioningSystemId.All or VersioningSystemId.Serverless or VersioningSystemId.Ech or VersioningSystemId.Ess))
{
<div class="mt-6">
<version-dropdown all-versions-url="@Model.AllVersionsUrl" current-version='@(Model.CurrentVersion)' items='@(new HtmlString(Model.VersionDropdownSerializedModel))'>
<div class="h-8">@*height of dropdown button*@</div>
</version-dropdown>
</div>
}
<ul class="mt-6 hidden md:flex items-center lg:block gap-4">
<li class="view-as-markdown hidden lg:block lg:not-first:mt-1">
<a href="@Model.MarkdownUrl" class="link text-sm" target="_blank">
Expand Down
4 changes: 3 additions & 1 deletion src/Elastic.Markdown/MarkdownLayoutViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Documentation.Configuration.LegacyUrlMappings;
using Elastic.Documentation.Configuration.Versions;
using Elastic.Documentation.Site;
using Elastic.Documentation.Site.Navigation;

Expand All @@ -23,6 +23,8 @@ public record MarkdownLayoutViewModel : GlobalLayoutViewModel

public required MarkdownPageLayout? Layout { get; init; }

public required VersioningSystem VersioningSystem { get; init; }

public required string? VersionDropdownSerializedModel { get; init; }

public required string? CurrentVersion { get; init; }
Expand Down
1 change: 1 addition & 0 deletions src/Elastic.Markdown/Page/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
Breadcrumbs = Model.Breadcrumbs,
CurrentVersion = Model.CurrentDocument.YamlFrontMatter?.Layout == MarkdownPageLayout.LandingPage ? Model.VersionsConfig.VersioningSystems[0].Current : Model.CurrentVersion,
AllVersionsUrl = Model.AllVersionsUrl,
VersioningSystem = Model.VersioningSystem,
VersionDropdownSerializedModel = JsonSerializer.Serialize(Model.VersionDropdownItems,
ViewModelSerializerContext.Default.VersionDropDownItemViewModelArray),
};
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Page/IndexViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using Elastic.Documentation.Site.FileProviders;
using Elastic.Documentation.Site.Navigation;
using Elastic.Markdown.IO;
using Elastic.Markdown.IO.Navigation;

namespace Elastic.Markdown.Page;

Expand Down Expand Up @@ -55,6 +54,7 @@ public class IndexViewModel

public required HashSet<Product> Products { get; init; }

public required VersioningSystem VersioningSystem { get; init; }
public required VersionsConfiguration VersionsConfig { get; init; }

// https://developers.google.com/search/docs/appearance/structured-data/breadcrumb#json-ld
Expand Down
Loading