Skip to content

Add docset products attribute #1239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/_docset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ subs:
serverless-short: Serverless
ece: "Elastic Cloud Enterprise"
eck: "Elastic Cloud on Kubernetes"

products:
- elasticsearch

features:
primary-nav: false
Expand Down
5 changes: 5 additions & 0 deletions docs/syntax/code.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
products:
- apm
---

Comment on lines +1 to +5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops.. no.. but it doesn't hurt as well.

Thanks for noticing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove it in a follow-up.

# Code

Code blocks can be used to display multiple lines of code. They preserve formatting and provide syntax highlighting when possible.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.IO.Abstractions;
using DotNet.Globbing;
using Elastic.Documentation.Configuration.Suggestions;
using Elastic.Documentation.Configuration.TableOfContents;
using Elastic.Documentation.Links;
using Elastic.Documentation.Navigation;
Expand Down Expand Up @@ -33,6 +34,8 @@ public record ConfigurationFile : ITableOfContentsScope

public Dictionary<string, LinkRedirect>? Redirects { get; }

public HashSet<string> Products { get; } = new(StringComparer.Ordinal);

public HashSet<string> ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase);

public Glob[] Globs { get; } = [];
Expand Down Expand Up @@ -104,6 +107,20 @@ public ConfigurationFile(IDocumentationContext context)
case "toc":
// read this later
break;
case "products":
var productIds = YamlStreamReader.ReadStringArray(entry.Entry);
foreach (var productId in productIds)
{
if (!Builder.Products.AllById.ContainsKey(productId))
{
var message =
$"Product \"{productId}\" not found in the product list. {new Suggestion(Builder.Products.All.Select(p => p.Id).ToHashSet(), productId).GetSuggestionQuestion()}";
reader.EmitError(message, entry.Entry.Value);
}
else
_ = Products.Add(productId);
}
break;
case "features":
_features = reader.ReadDictionary(entry.Entry).ToDictionary(k => k.Key, v => bool.Parse(v.Value), StringComparer.OrdinalIgnoreCase);
break;
Expand Down
89 changes: 89 additions & 0 deletions src/Elastic.Documentation.Configuration/Builder/Products.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Collections.Frozen;

namespace Elastic.Documentation.Configuration.Builder;

public record Product(string Id, string DisplayName);

public static class Products
{
public static FrozenSet<Product> All { get; } = [
new("apm", "APM"),
new("apm-dotnet-agent", "APM .NET Agent"),
new("apm-android-agent", "APM Android Agent"),
new("apm-attacher", "APM Attacher"),
new("apm-aws-lambda-extension", "APM AWS Lambda extension"),
new("apm-go-agent", "APM Go Agent"),
new("apm-ios-agent", "APM iOS Agent"),
new("apm-java-agent", "APM Java Agent"),
new("apm-node-agent", "APM Node.js Agent"),
new("apm-php-agent", "APM PHP Agent"),
new("apm-python-agent", "APM Python Agent"),
new("apm-ruby-agent", "APM Ruby Agent"),
new("apm-rum-agent", "APM RUM Agent"),
new("beats-logging-plugin", "Beats Logging plugin"),
new("cloud-control-ecctl", "Cloud Control ECCTL"),
new("cloud-enterprise", "Cloud Enterprise"),
new("cloud-hosted", "Cloud Hosted"),
new("cloud-kubernetes", "Cloud Kubernetes"),
new("cloud-native-ingest", "Cloud Native Ingest"),
new("cloud-serverless", "Cloud Serverless"),
new("cloud-terraform", "Cloud Terraform"),
new("ecs-logging", "ECS Logging"),
new("ecs-logging-dotnet", "ECS Logging .NET"),
new("ecs-logging-go-logrus", "ECS Logging Go Logrus"),
new("ecs-logging-go-zap", "ECS Logging Go Zap"),
new("ecs-logging-go-zerolog", "ECS Logging Go Zerolog"),
new("ecs-logging-java", "ECS Logging Java"),
new("ecs-logging-node", "ECS Logging Node.js"),
new("ecs-logging-php", "ECS Logging PHP"),
new("ecs-logging-python", "ECS Logging Python"),
new("ecs-logging-ruby", "ECS Logging Ruby"),
new("elastic-agent", "Elastic Agent"),
new("ecs", "Elastic Common Schema (ECS)"),
new("elastic-products-platform", "Elastic Products platform"),
new("elastic-stack", "Elastic Stack"),
new("elasticsearch", "Elasticsearch"),
new("elasticsearch-dotnet-client", "Elasticsearch .NET Client"),
new("elasticsearch-apache-hadoop", "Elasticsearch Apache Hadoop"),
new("elasticsearch-cloud-hosted-heroku", "Elasticsearch Cloud Hosted Heroku"),
new("elasticsearch-community-clients", "Elasticsearch community clients"),
new("elasticsearch-curator", "Elasticsearch Curator"),
new("elasticsearch-eland-python-client", "Elasticsearch Eland Python Client"),
new("elasticsearch-go-client", "Elasticsearch Go Client"),
new("elasticsearch-groovy-client", "Elasticsearch Groovy Client"),
new("elasticsearch-java-client", "Elasticsearch Java Client"),
new("elasticsearch-java-script-client", "Elasticsearch JavaScript Client"),
new("elasticsearch-painless-scripting-language", "Elasticsearch Painless scripting language"),
new("elasticsearch-perl-client", "Elasticsearch Perl Client"),
new("elasticsearch-php-client", "Elasticsearch PHP Client"),
new("elasticsearch-plugins", "Elasticsearch plugins"),
new("elasticsearch-python-client", "Elasticsearch Python Client"),
new("elasticsearch-resiliency-status", "Elasticsearch Resiliency Status"),
new("elasticsearch-ruby-client", "Elasticsearch Ruby Client"),
new("elasticsearch-rust-client", "Elasticsearch Rust Client"),
new("fleet", "Fleet"),
new("ingest", "Ingest"),
new("integrations", "Integrations"),
new("kibana", "Kibana"),
new("logstash", "Logstash"),
new("machine-learning", "Machine Learning"),
new("observability", "Observability"),
new("reference-architectures", "Reference Architectures"),
new("search-ui", "Search UI"),
new("security", "Security"),
new("edot-collector", "Elastic Distribution of OpenTelemetry Collector"),
new("edot-java", "Elastic Distribution of OpenTelemetry Java"),
new("edot-dotnet", "Elastic Distribution of OpenTelemetry .NET"),
new("edot-nodejs", "Elastic Distribution of OpenTelemetry Node.js"),
new("edot-php", "Elastic Distribution of OpenTelemetry PHP"),
new("edot-python", "Elastic Distribution of OpenTelemetry Python"),
new("edot-android", "Elastic Distribution of OpenTelemetry Android"),
new("edot-ios", "Elastic Distribution of OpenTelemetry iOS")
];

public static FrozenDictionary<string, Product> AllById { get; } = All.ToDictionary(p => p.Id, StringComparer.Ordinal).ToFrozenDictionary();
}
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

namespace Elastic.Markdown.Suggestions;
namespace Elastic.Documentation.Configuration.Suggestions;

public class Suggestion(IReadOnlySet<string> candidates, string input)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Documentation.Configuration.Builder;
using YamlDotNet.Serialization;

namespace Elastic.Markdown.Myst.FrontMatter;
Expand Down
86 changes: 2 additions & 84 deletions src/Elastic.Markdown/Myst/FrontMatter/Products.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,14 @@
// See the LICENSE file in the project root for more information

using System.Collections.Frozen;
using System.ComponentModel.DataAnnotations;
using Elastic.Markdown.Suggestions;
using Elastic.Documentation.Configuration.Builder;
using Elastic.Documentation.Configuration.Suggestions;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace Elastic.Markdown.Myst.FrontMatter;

public record Product(string Id, string DisplayName);

public static class Products
{
public static FrozenSet<Product> All { get; } = [
new("apm", "APM"),
new("apm-dotnet-agent", "APM .NET Agent"),
new("apm-android-agent", "APM Android Agent"),
new("apm-attacher", "APM Attacher"),
new("apm-aws-lambda-extension", "APM AWS Lambda extension"),
new("apm-go-agent", "APM Go Agent"),
new("apm-ios-agent", "APM iOS Agent"),
new("apm-java-agent", "APM Java Agent"),
new("apm-node-agent", "APM Node.js Agent"),
new("apm-php-agent", "APM PHP Agent"),
new("apm-python-agent", "APM Python Agent"),
new("apm-ruby-agent", "APM Ruby Agent"),
new("apm-rum-agent", "APM RUM Agent"),
new("beats-logging-plugin", "Beats Logging plugin"),
new("cloud-control-ecctl", "Cloud Control ECCTL"),
new("cloud-enterprise", "Cloud Enterprise"),
new("cloud-hosted", "Cloud Hosted"),
new("cloud-kubernetes", "Cloud Kubernetes"),
new("cloud-native-ingest", "Cloud Native Ingest"),
new("cloud-serverless", "Cloud Serverless"),
new("cloud-terraform", "Cloud Terraform"),
new("ecs-logging", "ECS Logging"),
new("ecs-logging-dotnet", "ECS Logging .NET"),
new("ecs-logging-go-logrus", "ECS Logging Go Logrus"),
new("ecs-logging-go-zap", "ECS Logging Go Zap"),
new("ecs-logging-go-zerolog", "ECS Logging Go Zerolog"),
new("ecs-logging-java", "ECS Logging Java"),
new("ecs-logging-node", "ECS Logging Node.js"),
new("ecs-logging-php", "ECS Logging PHP"),
new("ecs-logging-python", "ECS Logging Python"),
new("ecs-logging-ruby", "ECS Logging Ruby"),
new("elastic-agent", "Elastic Agent"),
new("ecs", "Elastic Common Schema (ECS)"),
new("elastic-products-platform", "Elastic Products platform"),
new("elastic-stack", "Elastic Stack"),
new("elasticsearch", "Elasticsearch"),
new("elasticsearch-dotnet-client", "Elasticsearch .NET Client"),
new("elasticsearch-apache-hadoop", "Elasticsearch Apache Hadoop"),
new("elasticsearch-cloud-hosted-heroku", "Elasticsearch Cloud Hosted Heroku"),
new("elasticsearch-community-clients", "Elasticsearch community clients"),
new("elasticsearch-curator", "Elasticsearch Curator"),
new("elasticsearch-eland-python-client", "Elasticsearch Eland Python Client"),
new("elasticsearch-go-client", "Elasticsearch Go Client"),
new("elasticsearch-groovy-client", "Elasticsearch Groovy Client"),
new("elasticsearch-java-client", "Elasticsearch Java Client"),
new("elasticsearch-java-script-client", "Elasticsearch JavaScript Client"),
new("elasticsearch-painless-scripting-language", "Elasticsearch Painless scripting language"),
new("elasticsearch-perl-client", "Elasticsearch Perl Client"),
new("elasticsearch-php-client", "Elasticsearch PHP Client"),
new("elasticsearch-plugins", "Elasticsearch plugins"),
new("elasticsearch-python-client", "Elasticsearch Python Client"),
new("elasticsearch-resiliency-status", "Elasticsearch Resiliency Status"),
new("elasticsearch-ruby-client", "Elasticsearch Ruby Client"),
new("elasticsearch-rust-client", "Elasticsearch Rust Client"),
new("fleet", "Fleet"),
new("ingest", "Ingest"),
new("integrations", "Integrations"),
new("kibana", "Kibana"),
new("logstash", "Logstash"),
new("machine-learning", "Machine Learning"),
new("observability", "Observability"),
new("reference-architectures", "Reference Architectures"),
new("search-ui", "Search UI"),
new("security", "Security"),
new("edot-collector", "Elastic Distribution of OpenTelemetry Collector"),
new("edot-java", "Elastic Distribution of OpenTelemetry Java"),
new("edot-dotnet", "Elastic Distribution of OpenTelemetry .NET"),
new("edot-nodejs", "Elastic Distribution of OpenTelemetry Node.js"),
new("edot-php", "Elastic Distribution of OpenTelemetry PHP"),
new("edot-python", "Elastic Distribution of OpenTelemetry Python"),
new("edot-android", "Elastic Distribution of OpenTelemetry Android"),
new("edot-ios", "Elastic Distribution of OpenTelemetry iOS")
];

public static FrozenDictionary<string, Product> AllById { get; } = All.ToDictionary(p => p.Id, StringComparer.Ordinal).ToFrozenDictionary();
}

public class ProductConverter : IYamlTypeConverter
{
public bool Accepts(Type type) => type == typeof(Product);
Expand Down
19 changes: 18 additions & 1 deletion src/Elastic.Markdown/Slices/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
using System.Collections.Concurrent;
using System.IO.Abstractions;
using Elastic.Documentation;
using Elastic.Documentation.Configuration.Builder;
using Elastic.Documentation.Legacy;
using Elastic.Markdown.Extensions.DetectionRules;
using Elastic.Markdown.IO;
using Elastic.Markdown.IO.Navigation;
using Elastic.Markdown.Myst.FrontMatter;
using Markdig.Syntax;
using RazorSlices;
using IFileInfo = System.IO.Abstractions.IFileInfo;
Expand Down Expand Up @@ -115,6 +117,20 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument

var legacyPage = LegacyUrlMapper.MapLegacyUrl(markdown.YamlFrontMatter?.MappedPages);

var configProducts = DocumentationSet.Configuration.Products.Select(p =>
{
if (Products.AllById.TryGetValue(p, out var product))
return product;
throw new ArgumentException($"Invalid product id: {p}");
});

var frontMatterProducts = markdown.YamlFrontMatter?.Products ?? [];

var allProducts = frontMatterProducts
.Union(configProducts)
.Distinct()
.ToHashSet();

var slice = Index.Create(new IndexViewModel
{
SiteName = siteName,
Expand All @@ -139,7 +155,8 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument
Features = DocumentationSet.Configuration.Features,
StaticFileContentHashProvider = StaticFileContentHashProvider,
ReportIssueUrl = reportUrl,
LegacyPage = legacyPage
LegacyPage = legacyPage,
Products = allProducts
});
return await slice.RenderAsync(cancellationToken: ctx);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Slices/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
StaticFileContentHashProvider = Model.StaticFileContentHashProvider,
ReportIssueUrl = Model.ReportIssueUrl,
LegacyPage = Model.LegacyPage,
Products = Model.CurrentDocument.YamlFrontMatter?.Products is { Count: > 0} products ? string.Join(",", products.Select(p => p.DisplayName)) : null,
Products = Model.Products is { Count: > 0} products ? string.Join(",", products.Select(p => p.DisplayName)) : null,
};
}
<section id="elastic-docs-v3">
Expand Down
2 changes: 2 additions & 0 deletions src/Elastic.Markdown/Slices/_ViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class IndexViewModel

public required FeatureFlags Features { get; init; }
public required StaticFileContentHashProvider StaticFileContentHashProvider { get; init; }

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

public class LayoutViewModel
Expand Down
Loading