Skip to content

Commit 945042a

Browse files
authored
Allow extensions to inject IDocumentationFileExporter and control the relative path published in links.json (#806)
* Allow reference to inject IDocumentationFileExporter and control the relative path published in links.json * revert inline markdown indentation
1 parent e0fdbe8 commit 945042a

10 files changed

+93
-16
lines changed

src/Elastic.Markdown/DocumentationGenerator.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public interface IDocumentationFileOutputProvider
2828
public class DocumentationGenerator
2929
{
3030
private readonly IDocumentationFileOutputProvider? _documentationFileOutputProvider;
31+
private readonly IConversionCollector? _conversionCollector;
3132
private readonly ILogger _logger;
3233
private readonly IFileSystem _writeFileSystem;
3334
private readonly IDocumentationFileExporter _documentationFileExporter;
@@ -46,6 +47,7 @@ public DocumentationGenerator(
4647
)
4748
{
4849
_documentationFileOutputProvider = documentationFileOutputProvider;
50+
_conversionCollector = conversionCollector;
4951
_writeFileSystem = docSet.Build.WriteFileSystem;
5052
_logger = logger.CreateLogger(nameof(DocumentationGenerator));
5153

@@ -55,7 +57,8 @@ public DocumentationGenerator(
5557
HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem, new DescriptionGenerator());
5658
_documentationFileExporter =
5759
documentationExporter
58-
?? new DocumentationFileExporter(docSet.Build.ReadFileSystem, _writeFileSystem, HtmlWriter, conversionCollector);
60+
?? docSet.Build.Configuration.EnabledExtensions.FirstOrDefault(e => e.FileExporter != null)?.FileExporter
61+
?? new DocumentationFileExporter(docSet.Build.ReadFileSystem, _writeFileSystem);
5962

6063
_logger.LogInformation("Created documentation set for: {DocumentationSetName}", DocumentationSet.Name);
6164
_logger.LogInformation("Source directory: {SourcePath} Exists: {SourcePathExists}", docSet.SourceDirectory, docSet.SourceDirectory.Exists);
@@ -205,7 +208,7 @@ private async Task ProcessFile(HashSet<string> offendingFiles, DocumentationFile
205208
//TODO send file to OutputFile() so we can validate its scope is defined in navigation.yml
206209
var outputFile = OutputFile(file.RelativePath);
207210
if (outputFile is not null)
208-
await _documentationFileExporter.ProcessFile(file, outputFile, token);
211+
await _documentationFileExporter.ProcessFile(Context, file, outputFile, HtmlWriter, _conversionCollector, token);
209212
}
210213

211214
private IFileInfo? OutputFile(string relativePath)

src/Elastic.Markdown/Exporters/DocumentationFileExporter.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@ public interface IDocumentationFileExporter
1313
/// Used in documentation state to ensure we break the build cache if a different exporter is chosen
1414
string Name { get; }
1515

16-
Task ProcessFile(DocumentationFile file, IFileInfo outputFile, Cancel token);
16+
Task ProcessFile(BuildContext context, DocumentationFile file, IFileInfo outputFile, HtmlWriter htmlWriter, IConversionCollector? conversionCollector,
17+
Cancel token);
1718

1819
Task CopyEmbeddedResource(IFileInfo outputFile, Stream resourceStream, Cancel ctx);
1920
}
2021

2122
public abstract class DocumentationFileExporterBase(IFileSystem readFileSystem, IFileSystem writeFileSystem) : IDocumentationFileExporter
2223
{
2324
public abstract string Name { get; }
24-
public abstract Task ProcessFile(DocumentationFile file, IFileInfo outputFile, Cancel token);
25+
26+
public abstract Task ProcessFile(BuildContext context, DocumentationFile file, IFileInfo outputFile, HtmlWriter htmlWriter,
27+
IConversionCollector? conversionCollector,
28+
Cancel token);
2529

2630
protected async Task CopyFileFsAware(DocumentationFile file, IFileInfo outputFile, Cancel ctx)
2731
{
@@ -47,14 +51,16 @@ public async Task CopyEmbeddedResource(IFileInfo outputFile, Stream resourceStre
4751

4852
public class DocumentationFileExporter(
4953
IFileSystem readFileSystem,
50-
IFileSystem writeFileSystem,
51-
HtmlWriter htmlWriter,
52-
IConversionCollector? conversionCollector
54+
IFileSystem writeFileSystem
5355
) : DocumentationFileExporterBase(readFileSystem, writeFileSystem)
5456
{
5557
public override string Name { get; } = nameof(DocumentationFileExporter);
5658

57-
public override async Task ProcessFile(DocumentationFile file, IFileInfo outputFile, Cancel token)
59+
public override async Task ProcessFile(BuildContext context, DocumentationFile file,
60+
IFileInfo outputFile,
61+
HtmlWriter htmlWriter,
62+
IConversionCollector? conversionCollector,
63+
Cancel token)
5864
{
5965
if (file is MarkdownFile markdown)
6066
await htmlWriter.WriteAsync(outputFile, markdown, conversionCollector, token);

src/Elastic.Markdown/Exporters/NoopDocumentationFileExporter.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44

55
using System.IO.Abstractions;
66
using Elastic.Markdown.IO;
7+
using Elastic.Markdown.Slices;
78

89
namespace Elastic.Markdown.Exporters;
910

1011
public class NoopDocumentationFileExporter : IDocumentationFileExporter
1112
{
1213
public string Name { get; } = nameof(NoopDocumentationFileExporter);
13-
public Task ProcessFile(DocumentationFile file, IFileInfo outputFile, Cancel token) => Task.CompletedTask;
14+
15+
public Task ProcessFile(BuildContext context, DocumentationFile file, IFileInfo outputFile, HtmlWriter htmlWriter,
16+
IConversionCollector? conversionCollector, Cancel token) =>
17+
Task.CompletedTask;
18+
1419
public Task CopyEmbeddedResource(IFileInfo outputFile, Stream resourceStream, Cancel ctx) => Task.CompletedTask;
1520
}

src/Elastic.Markdown/Extensions/DetectionRules/DetectionRule.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public record DetectionRule
6161
public required string Type { get; init; }
6262
public required string? Language { get; init; }
6363
public required string[]? Indices { get; init; }
64-
public required string RunsEvery { get; init; }
64+
public required string? RunsEvery { get; init; }
6565
public required string? IndicesFromDateMath { get; init; }
6666
public required string MaximumAlertsPerExecution { get; init; }
6767
public required string[]? References { get; init; }
@@ -108,7 +108,7 @@ public static DetectionRule From(IFileInfo source)
108108
Query = TryGetString(rule, "query"),
109109
Note = TryGetString(rule, "note"),
110110
Name = rule.GetString("name"),
111-
RunsEvery = "?",
111+
RunsEvery = TryGetString(rule, "interval"),
112112
MaximumAlertsPerExecution = "?",
113113
Version = "?",
114114
Threats = threats

src/Elastic.Markdown/Extensions/DetectionRules/DetectionRuleFile.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ public record DetectionRuleFile : MarkdownFile
1313
{
1414
public DetectionRule? Rule { get; set; }
1515

16+
public override string LinkReferenceRelativePath { get; }
17+
18+
public IFileInfo RuleSourceMarkdownPath { get; }
19+
1620
public DetectionRuleFile(
1721
IFileInfo sourceFile,
1822
IDirectoryInfo rootPath,
@@ -21,6 +25,25 @@ public DetectionRuleFile(
2125
DocumentationSet set
2226
) : base(sourceFile, rootPath, parser, build, set)
2327
{
28+
RuleSourceMarkdownPath = SourcePath(sourceFile, build);
29+
LinkReferenceRelativePath = Path.GetRelativePath(build.DocumentationSourceDirectory.FullName, RuleSourceMarkdownPath.FullName);
30+
}
31+
32+
private static IFileInfo SourcePath(IFileInfo rulePath, BuildContext build)
33+
{
34+
var relative = Path.GetRelativePath(build.DocumentationCheckoutDirectory!.FullName, rulePath.FullName);
35+
var newPath = Path.Combine(build.DocumentationSourceDirectory.FullName, relative);
36+
var md = Path.ChangeExtension(newPath, ".md");
37+
return rulePath.FileSystem.FileInfo.New(md);
38+
}
39+
40+
public static IFileInfo OutputPath(IFileInfo rulePath, BuildContext build)
41+
{
42+
var relative = Path.GetRelativePath(build.DocumentationOutputDirectory.FullName, rulePath.FullName);
43+
if (relative.StartsWith("../"))
44+
relative = relative[3..];
45+
var newPath = Path.Combine(build.DocumentationOutputDirectory.FullName, relative);
46+
return rulePath.FileSystem.FileInfo.New(newPath);
2447
}
2548

2649
protected override string RelativePathUrl => RelativePath.AsSpan().TrimStart("../").ToString();
@@ -29,14 +52,14 @@ protected override Task<MarkdownDocument> GetMinimalParseDocumentAsync(Cancel ct
2952
{
3053
Title = Rule?.Name;
3154
var markdown = GetMarkdown();
32-
var document = MarkdownParser.MinimalParseStringAsync(markdown, SourceFile, null);
55+
var document = MarkdownParser.MinimalParseStringAsync(markdown, RuleSourceMarkdownPath, null);
3356
return Task.FromResult(document);
3457
}
3558

3659
protected override Task<MarkdownDocument> GetParseDocumentAsync(Cancel ctx)
3760
{
3861
var markdown = GetMarkdown();
39-
var document = MarkdownParser.ParseStringAsync(markdown, SourceFile, null);
62+
var document = MarkdownParser.ParseStringAsync(markdown, RuleSourceMarkdownPath, null);
4063
return Task.FromResult(document);
4164
}
4265

src/Elastic.Markdown/Extensions/DetectionRules/DetectionRulesDocsBuilderExtension.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.IO.Abstractions;
6+
using Elastic.Markdown.Exporters;
67
using Elastic.Markdown.Helpers;
78
using Elastic.Markdown.IO;
89
using Elastic.Markdown.IO.Configuration;
@@ -16,6 +17,8 @@ public class DetectionRulesDocsBuilderExtension(BuildContext build) : IDocsBuild
1617

1718
public bool InjectsIntoNavigation(ITocItem tocItem) => false;
1819

20+
public IDocumentationFileExporter? FileExporter { get; } = new RuleDocumentationFileExporter(build.ReadFileSystem, build.WriteFileSystem);
21+
1922
public void CreateNavigationItem(
2023
DocumentationGroup? parent,
2124
ITocItem tocItem,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System.IO.Abstractions;
6+
using Elastic.Markdown.Exporters;
7+
using Elastic.Markdown.IO;
8+
using Elastic.Markdown.Slices;
9+
10+
namespace Elastic.Markdown.Extensions.DetectionRules;
11+
12+
public class RuleDocumentationFileExporter(IFileSystem readFileSystem, IFileSystem writeFileSystem)
13+
: DocumentationFileExporterBase(readFileSystem, writeFileSystem)
14+
{
15+
public override string Name { get; } = nameof(RuleDocumentationFileExporter);
16+
17+
public override async Task ProcessFile(BuildContext context, DocumentationFile file, IFileInfo outputFile, HtmlWriter htmlWriter,
18+
IConversionCollector? conversionCollector, Cancel token)
19+
{
20+
if (file is DetectionRuleFile df)
21+
await htmlWriter.WriteAsync(DetectionRuleFile.OutputPath(outputFile, context), df, conversionCollector, token);
22+
else if (file is MarkdownFile markdown)
23+
await htmlWriter.WriteAsync(outputFile, markdown, conversionCollector, token);
24+
else
25+
{
26+
if (outputFile.Directory is { Exists: false })
27+
outputFile.Directory.Create();
28+
await CopyFileFsAware(file, outputFile, token);
29+
}
30+
}
31+
}

src/Elastic.Markdown/Extensions/IDocsBuilderExtension.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.IO.Abstractions;
6+
using Elastic.Markdown.Exporters;
67
using Elastic.Markdown.IO;
78
using Elastic.Markdown.IO.Configuration;
89
using Elastic.Markdown.IO.Navigation;
@@ -11,6 +12,8 @@ namespace Elastic.Markdown.Extensions;
1112

1213
public interface IDocsBuilderExtension
1314
{
15+
IDocumentationFileExporter? FileExporter { get; }
16+
1417
/// Inject items into the current navigation
1518
void CreateNavigationItem(
1619
DocumentationGroup? parent,

src/Elastic.Markdown/IO/DocumentationFile.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public abstract record DocumentationFile(IFileInfo SourceFile, IDirectoryInfo Ro
1313
public string RelativePath { get; } = Path.GetRelativePath(RootPath.FullName, SourceFile.FullName);
1414
public string RelativeFolder { get; } = Path.GetRelativePath(RootPath.FullName, SourceFile.Directory!.FullName);
1515

16+
/// Allows documentation files of non markdown origins to advertise as their markdown equivalent in links.json
17+
public virtual string LinkReferenceRelativePath => RelativePath;
18+
1619
}
1720

1821
public record ImageFile(IFileInfo SourceFile, IDirectoryInfo RootPath, string MimeType = "image/png")

src/Elastic.Markdown/IO/State/LinkReference.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public static LinkReference Create(DocumentationSet set)
7070
var redirects = set.Configuration.Redirects;
7171
var crossLinks = set.Build.Collector.CrossLinks.ToHashSet().ToArray();
7272
var links = set.MarkdownFiles.Values
73-
.Select(m => (m.RelativePath, File: m))
73+
.Select(m => (m.LinkReferenceRelativePath, File: m))
7474
.ToDictionary(k => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
75-
? k.RelativePath.Replace('\\', '/')
76-
: k.RelativePath, v =>
75+
? k.LinkReferenceRelativePath.Replace('\\', '/')
76+
: k.LinkReferenceRelativePath, v =>
7777
{
7878
var anchors = v.File.Anchors.Count == 0 ? null : v.File.Anchors.ToArray();
7979
return new LinkMetadata { Anchors = anchors, Hidden = v.File.Hidden };

0 commit comments

Comments
 (0)