Skip to content

Commit 8cb28db

Browse files
Copilotreakaleekcotti
authored
Add frontmatter mapped_pages URL validation with URI format checking (#1899)
* Initial plan * Add frontmatter mapped_pages URL validation - Added validation to ensure mapped_pages URLs start with https://www.elastic.co/guide - Emits clear error messages with guidance on how to fix invalid URLs - Added comprehensive test coverage for validation scenarios - All existing tests continue to pass Co-authored-by: reakaleek <[email protected]> * Add URI well-formed validation to mapped_pages URLs - Enhanced validation to include Uri.IsWellFormedUriString check - Added test cases for malformed URIs, invalid characters, and non-absolute URIs - URLs must now be both properly prefixed AND well-formed absolute URIs Co-authored-by: cotti <[email protected]> * Addressing PR comments Co-authored-by: reakaleek <[email protected]> * Restore global.json file to proper state - Restored global.json content that was accidentally emptied in commit 05a6385 - Proper .NET 9.0.100 SDK configuration restored - File now contains correct SDK version and rollForward settings Co-authored-by: reakaleek <[email protected]> * Run dotnet format --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: reakaleek <[email protected]> Co-authored-by: Jan Calanog <[email protected]> Co-authored-by: cotti <[email protected]> Co-authored-by: Felipe Cotti <[email protected]>
1 parent b767046 commit 8cb28db

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

src/Elastic.Markdown/IO/MarkdownFile.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,18 @@ private YamlFrontMatter ProcessYamlFrontMatter(MarkdownDocument document)
363363
Collector.Emit(severity, FilePath, message);
364364
}
365365

366+
// Validate mapped_pages URLs
367+
if (fm.MappedPages is not null)
368+
{
369+
foreach (var url in fm.MappedPages)
370+
{
371+
if (!string.IsNullOrEmpty(url) && (!url.StartsWith("https://www.elastic.co/guide", StringComparison.OrdinalIgnoreCase) || !Uri.IsWellFormedUriString(url, UriKind.Absolute)))
372+
{
373+
Collector.EmitError(FilePath, $"Invalid mapped_pages URL: \"{url}\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\". Please update the URL to reference content under the Elastic documentation guide.");
374+
}
375+
}
376+
}
377+
366378
// TODO remove when migration tool and our demo content sets are updated
367379
var deprecatedTitle = fm.Title;
368380
if (!string.IsNullOrEmpty(deprecatedTitle))

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

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,156 @@ public void HasErrors()
183183
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid products frontmatter value: \"Product 'id' field is required."));
184184
}
185185
}
186+
187+
public class MappedPagesValidUrl(ITestOutputHelper output) : DirectiveTest(output,
188+
"""
189+
---
190+
mapped_pages:
191+
- "https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html"
192+
---
193+
194+
# Test Page
195+
"""
196+
)
197+
{
198+
[Fact]
199+
public void NoErrors()
200+
{
201+
Collector.Diagnostics.Should().BeEmpty();
202+
}
203+
}
204+
205+
public class MappedPagesInvalidUrl(ITestOutputHelper output) : DirectiveTest(output,
206+
"""
207+
---
208+
mapped_pages:
209+
- "https://www.elastic.co/docs/get-started/deployment-options"
210+
---
211+
212+
# Test Page
213+
"""
214+
)
215+
{
216+
[Fact]
217+
public void HasErrors()
218+
{
219+
Collector.Diagnostics.Should().HaveCount(1);
220+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid mapped_pages URL: \"https://www.elastic.co/docs/get-started/deployment-options\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\". Please update the URL to reference content under the Elastic documentation guide."));
221+
}
222+
}
223+
224+
public class MappedPagesMixedUrls(ITestOutputHelper output) : DirectiveTest(output,
225+
"""
226+
---
227+
mapped_pages:
228+
- "https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html"
229+
- "https://www.elastic.co/docs/invalid-url"
230+
- "https://www.elastic.co/guide/en/kibana/current/index.html"
231+
---
232+
233+
# Test Page
234+
"""
235+
)
236+
{
237+
[Fact]
238+
public void HasErrorsForInvalidUrl()
239+
{
240+
Collector.Diagnostics.Should().HaveCount(1);
241+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid mapped_pages URL: \"https://www.elastic.co/docs/invalid-url\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\""));
242+
}
243+
}
244+
245+
public class MappedPagesEmptyUrl(ITestOutputHelper output) : DirectiveTest(output,
246+
"""
247+
---
248+
mapped_pages:
249+
- ""
250+
---
251+
252+
# Test Page
253+
"""
254+
)
255+
{
256+
[Fact]
257+
public void NoErrorsForEmptyUrl()
258+
{
259+
// Empty URLs are ignored, no validation error should occur
260+
Collector.Diagnostics.Should().BeEmpty();
261+
}
262+
}
263+
264+
public class MappedPagesExternalUrl(ITestOutputHelper output) : DirectiveTest(output,
265+
"""
266+
---
267+
mapped_pages:
268+
- "https://github.com/elastic/docs-builder"
269+
---
270+
271+
# Test Page
272+
"""
273+
)
274+
{
275+
[Fact]
276+
public void HasErrorsForExternalUrl()
277+
{
278+
Collector.Diagnostics.Should().HaveCount(1);
279+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid mapped_pages URL: \"https://github.com/elastic/docs-builder\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\""));
280+
}
281+
}
282+
283+
public class MappedPagesMalformedUri(ITestOutputHelper output) : DirectiveTest(output,
284+
"""
285+
---
286+
mapped_pages:
287+
- "https://www.elastic.co/guide/[invalid-characters]"
288+
---
289+
290+
# Test Page
291+
"""
292+
)
293+
{
294+
[Fact]
295+
public void HasErrorsForMalformedUri()
296+
{
297+
Collector.Diagnostics.Should().HaveCount(1);
298+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid mapped_pages URL: \"https://www.elastic.co/guide/[invalid-characters]\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\""));
299+
}
300+
}
301+
302+
public class MappedPagesInvalidScheme(ITestOutputHelper output) : DirectiveTest(output,
303+
"""
304+
---
305+
mapped_pages:
306+
- "https://www.elastic.co/guide/invalid uri with spaces"
307+
---
308+
309+
# Test Page
310+
"""
311+
)
312+
{
313+
[Fact]
314+
public void HasErrorsForInvalidScheme()
315+
{
316+
Collector.Diagnostics.Should().HaveCount(1);
317+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid mapped_pages URL: \"https://www.elastic.co/guide/invalid uri with spaces\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\""));
318+
}
319+
}
320+
321+
public class MappedPagesNotAbsoluteUri(ITestOutputHelper output) : DirectiveTest(output,
322+
"""
323+
---
324+
mapped_pages:
325+
- "not-a-uri-at-all"
326+
---
327+
328+
# Test Page
329+
"""
330+
)
331+
{
332+
[Fact]
333+
public void HasErrorsForNotAbsoluteUri()
334+
{
335+
Collector.Diagnostics.Should().HaveCount(1);
336+
Collector.Diagnostics.Should().Contain(d => d.Message.Contains("Invalid mapped_pages URL: \"not-a-uri-at-all\". All mapped_pages URLs must start with \"https://www.elastic.co/guide\""));
337+
}
338+
}

0 commit comments

Comments
 (0)