Skip to content

Commit a46fbf2

Browse files
authored
Add id attribute to products list frontmatter (#1238)
* Fix merge conflicts * Fix * Optimize * Use Oridnal string comparer * Add `id` attribute to `products` list frontmatter * Also handle id in docset * Cleanup * fix
1 parent 6a52ba9 commit a46fbf2

File tree

6 files changed

+60
-32
lines changed

6 files changed

+60
-32
lines changed

docs/_docset.yml

-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ subs:
1313
serverless-short: Serverless
1414
ece: "Elastic Cloud Enterprise"
1515
eck: "Elastic Cloud on Kubernetes"
16-
17-
products:
18-
- elasticsearch
1916

2017
features:
2118
primary-nav: false

docs/syntax/code.md

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
---
2-
products:
3-
- apm
4-
---
5-
61
# Code
72

83
Code blocks can be used to display multiple lines of code. They preserve formatting and provide syntax highlighting when possible.

docs/syntax/frontmatter.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ description: This is a description of the page <2>
1313
applies_to: <3>
1414
serverless: all
1515
products: <4>
16-
- apm-java-agent
17-
- edot-java
16+
- id: apm-java-agent
17+
- id: edot-java
1818
---
1919
```
2020

@@ -45,7 +45,7 @@ See [](./applies.md)
4545
The products frontmatter is a list of products that the page relates to.
4646
This is used for the "Products" filter in the Search UI.
4747

48-
The products frontmatter is a list of strings, each string is the id of a product.
48+
The products frontmatter is a list of objects, each object has an `id` field.
4949

5050
| Product ID | Product Name |
5151
|---------------------------------------------|-----------------------------------------------|

src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs

+24-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Elastic.Documentation.Configuration.TableOfContents;
99
using Elastic.Documentation.Links;
1010
using Elastic.Documentation.Navigation;
11+
using YamlDotNet.RepresentationModel;
1112

1213
namespace Elastic.Documentation.Configuration.Builder;
1314

@@ -108,17 +109,33 @@ public ConfigurationFile(IDocumentationContext context)
108109
// read this later
109110
break;
110111
case "products":
111-
var productIds = YamlStreamReader.ReadStringArray(entry.Entry);
112-
foreach (var productId in productIds)
112+
if (entry.Entry.Value is not YamlSequenceNode sequence)
113113
{
114-
if (!Builder.Products.AllById.ContainsKey(productId))
114+
reader.EmitError("products must be a sequence", entry.Entry.Value);
115+
break;
116+
}
117+
118+
foreach (var node in sequence.Children.OfType<YamlMappingNode>())
119+
{
120+
YamlScalarNode? productId = null;
121+
foreach (var child in node.Children)
115122
{
116-
var message =
117-
$"Product \"{productId}\" not found in the product list. {new Suggestion(Builder.Products.All.Select(p => p.Id).ToHashSet(), productId).GetSuggestionQuestion()}";
118-
reader.EmitError(message, entry.Entry.Value);
123+
if (child.Key is YamlScalarNode { Value: "id" } && child.Value is YamlScalarNode scalarNode)
124+
{
125+
productId = scalarNode;
126+
break;
127+
}
119128
}
129+
if (productId?.Value is null)
130+
{
131+
reader.EmitError("products must contain an id", node);
132+
break;
133+
}
134+
135+
if (!Builder.Products.AllById.ContainsKey(productId.Value))
136+
reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(Builder.Products.All.Select(p => p.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node);
120137
else
121-
_ = Products.Add(productId);
138+
_ = Products.Add(productId.Value);
122139
}
123140
break;
124141
case "features":

src/Elastic.Markdown/Myst/FrontMatter/Products.cs

+25-6
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,33 @@ public class ProductConverter : IYamlTypeConverter
1717

1818
public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
1919
{
20-
var value = parser.Consume<Scalar>();
21-
if (string.IsNullOrWhiteSpace(value.Value))
22-
throw new InvalidProductException("");
23-
24-
if (Products.AllById.TryGetValue(value.Value, out var product))
20+
if (parser.Current is Scalar)
21+
{
22+
var value = parser.Consume<Scalar>().Value;
23+
throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm");
24+
}
25+
26+
_ = parser.Consume<MappingStart>();
27+
string? productId = null;
28+
29+
while (parser.Current is not MappingEnd)
30+
{
31+
var key = parser.Consume<Scalar>().Value;
32+
if (key == "id")
33+
productId = parser.Consume<Scalar>().Value;
34+
else
35+
parser.SkipThisAndNestedEvents();
36+
}
37+
38+
_ = parser.Consume<MappingEnd>();
39+
40+
if (string.IsNullOrWhiteSpace(productId))
41+
throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm");
42+
43+
if (Products.AllById.TryGetValue(productId, out var product))
2544
return product;
2645

27-
throw new InvalidProductException(value.Value);
46+
throw new InvalidProductException(productId);
2847
}
2948

3049
public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) => serializer.Invoke(value, type);

tests/Elastic.Markdown.Tests/FrontMatter/YamlFrontMatterTests.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public class ProductsSingle(ITestOutputHelper output) : DirectiveTest(output,
6969
"""
7070
---
7171
products:
72-
- "apm"
72+
- id: "apm"
7373
---
7474
7575
# APM
@@ -90,8 +90,8 @@ public class ProductsMultiple(ITestOutputHelper output) : DirectiveTest(output,
9090
"""
9191
---
9292
products:
93-
- "apm"
94-
- "elasticsearch"
93+
- id: "apm"
94+
- id: "elasticsearch"
9595
---
9696
9797
# APM
@@ -113,7 +113,7 @@ public class ProductsSuggestionWhenMispelled(ITestOutputHelper output) : Directi
113113
"""
114114
---
115115
products:
116-
- aapm
116+
- id: aapm
117117
---
118118
119119
# APM
@@ -132,7 +132,7 @@ public class ProductsSuggestionWhenMispelled2(ITestOutputHelper output) : Direct
132132
"""
133133
---
134134
products:
135-
- apm-javaagent
135+
- id: apm-javaagent
136136
---
137137
138138
# APM
@@ -151,7 +151,7 @@ public class ProductsSuggestionWhenCasingError(ITestOutputHelper output) : Direc
151151
"""
152152
---
153153
products:
154-
- Apm
154+
- id: Apm
155155
---
156156
157157
# APM
@@ -170,7 +170,7 @@ public class ProductsSuggestionWhenEmpty(ITestOutputHelper output) : DirectiveTe
170170
"""
171171
---
172172
products:
173-
- ""
173+
- id: ""
174174
---
175175
176176
# APM
@@ -181,6 +181,6 @@ public class ProductsSuggestionWhenEmpty(ITestOutputHelper output) : DirectiveTe
181181
public void HasErrors()
182182
{
183183
Collector.Diagnostics.Should().HaveCount(1);
184-
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid products frontmatter value: \"\".\nYou can find the full list at https://docs-v3-preview.elastic.dev/elastic/docs-builder/tree/main/syntax/frontmatter#products."));
184+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid products frontmatter value: \"Product 'id' field is required."));
185185
}
186186
}

0 commit comments

Comments
 (0)