Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
6 changes: 4 additions & 2 deletions src/Elastic.Markdown/DocumentationGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Text.Json;
using Elastic.Markdown.Exporters;
using Elastic.Markdown.IO;
using Elastic.Markdown.IO.HistoryMapping;
using Elastic.Markdown.IO.State;
using Elastic.Markdown.Links.CrossLinks;
using Elastic.Markdown.Slices;
Expand Down Expand Up @@ -44,7 +45,8 @@ public DocumentationGenerator(
INavigationHtmlWriter? navigationHtmlWriter = null,
IDocumentationFileOutputProvider? documentationFileOutputProvider = null,
IDocumentationFileExporter? documentationExporter = null,
IConversionCollector? conversionCollector = null
IConversionCollector? conversionCollector = null,
IHistoryMapper? historyMapper = null
)
{
_documentationFileOutputProvider = documentationFileOutputProvider;
Expand All @@ -55,7 +57,7 @@ public DocumentationGenerator(
DocumentationSet = docSet;
Context = docSet.Build;
Resolver = docSet.LinkResolver;
HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem, new DescriptionGenerator(), navigationHtmlWriter);
HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem, new DescriptionGenerator(), navigationHtmlWriter, historyMapper);
_documentationFileExporter =
documentationExporter
?? docSet.Build.Configuration.EnabledExtensions.FirstOrDefault(e => e.FileExporter != null)?.FileExporter
Expand Down
15 changes: 15 additions & 0 deletions src/Elastic.Markdown/IO/HistoryMapping/HistoryMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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

namespace Elastic.Markdown.IO.HistoryMapping;

public interface IHistoryMapper
{
string? MapLegacyUrl(string? currentUrl);
}

public record BypassHistoryMapper : IHistoryMapper
{
public string? MapLegacyUrl(string? currentUrl) => null;
}
3 changes: 3 additions & 0 deletions src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ public class YamlFrontMatter

[YamlMember(Alias = "applies_to")]
public ApplicableTo? AppliesTo { get; set; }

[YamlMember(Alias = "mapped_pages")]
public IReadOnlyCollection<string>? MappedPages { get; set; }
}
12 changes: 8 additions & 4 deletions src/Elastic.Markdown/Slices/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO.Abstractions;
using Elastic.Markdown.IO;
using Elastic.Markdown.IO.Discovery;
using Elastic.Markdown.IO.HistoryMapping;
using Elastic.Markdown.IO.Navigation;
using Markdig.Syntax;
using RazorSlices;
Expand Down Expand Up @@ -66,13 +67,13 @@ public class HtmlWriter(
DocumentationSet documentationSet,
IFileSystem writeFileSystem,
IDescriptionGenerator descriptionGenerator,
INavigationHtmlWriter? navigationHtmlWriter = null)
INavigationHtmlWriter? navigationHtmlWriter = null,
IHistoryMapper? historyMapper = null)
{
private DocumentationSet DocumentationSet { get; } = documentationSet;
public INavigationHtmlWriter NavigationHtmlWriter { get; } = navigationHtmlWriter ?? new IsolatedBuildNavigationHtmlWriter(documentationSet);
private StaticFileContentHashProvider StaticFileContentHashProvider { get; } = new(new EmbeddedOrPhysicalFileProvider(documentationSet.Build));


private IHistoryMapper HistoryMapper { get; } = historyMapper ?? new BypassHistoryMapper();
public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = default)
{
var document = await markdown.ParseFullAsync(ctx);
Expand Down Expand Up @@ -106,6 +107,8 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument

var siteName = DocumentationSet.Tree.Index?.Title ?? "Elastic Documentation";

var legacyUrl = HistoryMapper.MapLegacyUrl(markdown.YamlFrontMatter?.MappedPages?.FirstOrDefault());

var slice = Index.Create(new IndexViewModel
{
SiteName = siteName,
Expand All @@ -128,7 +131,8 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument
GoogleTagManager = DocumentationSet.Build.GoogleTagManager,
Features = DocumentationSet.Configuration.Features,
StaticFileContentHashProvider = StaticFileContentHashProvider,
ReportIssueUrl = reportUrl
ReportIssueUrl = reportUrl,
LegacyUrl = legacyUrl
});
return await slice.RenderAsync(cancellationToken: ctx);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Elastic.Markdown/Slices/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
GoogleTagManager = Model.GoogleTagManager,
Features = Model.Features,
StaticFileContentHashProvider = Model.StaticFileContentHashProvider,
ReportIssueUrl = Model.ReportIssueUrl
ReportIssueUrl = Model.ReportIssueUrl,
LegacyUrl = Model.LegacyUrl
};
}
<section id="elastic-docs-v3">
Expand Down
62 changes: 39 additions & 23 deletions src/Elastic.Markdown/Slices/Layout/_TableOfContents.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,65 @@
<aside class="sidebar hidden lg:block order-3">
<nav id="toc-nav" class="sidebar-nav pl-4">
<div class="pt-6 pb-20">
@if (Model.PageTocItems.Count > 0)
@if (Model.LegacyUrl is not null)
{
<div class="mb-4">
<div class="font-bold mb-2">On this page</div>
<div class="relative toc-progress-container font-body">
<div class="toc-progress-indicator absolute top-0 h-0 left-2 w-[1px] bg-blue-elastic transition-all duration-200 ease-out "></div>
<ul class="block w-full">
@foreach (var item in Model.PageTocItems)
{
<li class="has-[:hover]:border-l-grey-80 items-center ml-2 pl-4 border-l-1 border-l-grey-20 has-[.current]:border-l-blue-elastic!">
<a
class="sidebar-link inline-block my-1.5 @(item.Level == 3 ? "ml-4" : string.Empty)"
href="#@item.Slug">
@item.Heading
</a>
</li>
}
</ul>
</div>
<div class="edit-this-page">
<a href="@Model.LegacyUrl" class="link" target="_blank">
<svg class="link-icon"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="1 0 22 22" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" />
</svg>
View previous version
</a>
</div>
}
@if (Model.GithubEditUrl is not null)
{
<div class="edit-this-page">
<a href="@Model.GithubEditUrl" class="link" target="_blank">
<svg class="link-icon"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" stroke-width="1" stroke="currentColor">
<path d="M13.488 2.513a1.75 1.75 0 0 0-2.475 0L6.75 6.774a2.75 2.75 0 0 0-.596.892l-.848 2.047a.75.75 0 0 0 .98.98l2.047-.848a2.75 2.75 0 0 0 .892-.596l4.261-4.262a1.75 1.75 0 0 0 0-2.474Z" />
<path d="M4.75 3.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h6.5c.69 0 1.25-.56 1.25-1.25V9A.75.75 0 0 1 14 9v2.25A2.75 2.75 0 0 1 11.25 14h-6.5A2.75 2.75 0 0 1 2 11.25v-6.5A2.75 2.75 0 0 1 4.75 2H7a.75.75 0 0 1 0 1.5H4.75Z" />
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" stroke-width="1" stroke="currentColor">
<path d="M13.488 2.513a1.75 1.75 0 0 0-2.475 0L6.75 6.774a2.75 2.75 0 0 0-.596.892l-.848 2.047a.75.75 0 0 0 .98.98l2.047-.848a2.75 2.75 0 0 0 .892-.596l4.261-4.262a1.75 1.75 0 0 0 0-2.474Z"/>
<path d="M4.75 3.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h6.5c.69 0 1.25-.56 1.25-1.25V9A.75.75 0 0 1 14 9v2.25A2.75 2.75 0 0 1 11.25 14h-6.5A2.75 2.75 0 0 1 2 11.25v-6.5A2.75 2.75 0 0 1 4.75 2H7a.75.75 0 0 1 0 1.5H4.75Z"/>
</svg>
Edit this page
</a>
</div>
<div class="report-an-issue">
<a href="@Model.ReportIssueUrl" class="link" target="_blank">
<svg class="link-icon"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" stroke="currentColor">
<path fill-rule="evenodd" d="M1 8.74c0 .983.713 1.825 1.69 1.943.904.108 1.817.19 2.737.243.363.02.688.231.85.556l1.052 2.103a.75.75 0 0 0 1.342 0l1.052-2.103c.162-.325.487-.535.85-.556.92-.053 1.833-.134 2.738-.243.976-.118 1.689-.96 1.689-1.942V4.259c0-.982-.713-1.824-1.69-1.942a44.45 44.45 0 0 0-10.62 0C1.712 2.435 1 3.277 1 4.26v4.482Zm3-3.49a.75.75 0 0 1 .75-.75h6.5a.75.75 0 0 1 0 1.5h-6.5A.75.75 0 0 1 4 5.25ZM4.75 7a.75.75 0 0 0 0 1.5h2.5a.75.75 0 0 0 0-1.5h-2.5Z" clip-rule="evenodd" />
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" stroke="currentColor">
<path fill-rule="evenodd" d="M1 8.74c0 .983.713 1.825 1.69 1.943.904.108 1.817.19 2.737.243.363.02.688.231.85.556l1.052 2.103a.75.75 0 0 0 1.342 0l1.052-2.103c.162-.325.487-.535.85-.556.92-.053 1.833-.134 2.738-.243.976-.118 1.689-.96 1.689-1.942V4.259c0-.982-.713-1.824-1.69-1.942a44.45 44.45 0 0 0-10.62 0C1.712 2.435 1 3.277 1 4.26v4.482Zm3-3.49a.75.75 0 0 1 .75-.75h6.5a.75.75 0 0 1 0 1.5h-6.5A.75.75 0 0 1 4 5.25ZM4.75 7a.75.75 0 0 0 0 1.5h2.5a.75.75 0 0 0 0-1.5h-2.5Z" clip-rule="evenodd"/>
</svg>
Report an issue
</a>
</div>
}
@if (Model.GithubEditUrl is not null || Model.LegacyUrl is not null)
{
<div class="mb-2"></div>
}
@if (Model.PageTocItems.Count > 0)
{
<div class="mb-4">
<div class="font-bold mb-2">On this page</div>
<div class="relative toc-progress-container font-body">
<div class="toc-progress-indicator absolute top-0 h-0 left-2 w-[1px] bg-blue-elastic transition-all duration-200 ease-out "></div>
<ul class="block w-full">
@foreach (var item in Model.PageTocItems)
{
<li class="has-[:hover]:border-l-grey-80 items-center ml-2 pl-4 border-l-1 border-l-grey-20 has-[.current]:border-l-blue-elastic!">
<a
class="sidebar-link inline-block my-1.5 @(item.Level == 3 ? "ml-4" : string.Empty)"
href="#@item.Slug">
@item.Heading
</a>
</li>
}
</ul>
</div>
</div>
}
</div>

</nav>
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 @@ -24,6 +24,7 @@ public class IndexViewModel
public required MarkdownFile? NextDocument { get; init; }

public required string NavigationHtml { get; init; }
public required string? LegacyUrl { get; init; }
public required string? UrlPathPrefix { get; init; }
public required string? GithubEditUrl { get; init; }
public required string? ReportIssueUrl { get; init; }
Expand Down Expand Up @@ -52,6 +53,7 @@ public class LayoutViewModel
public required MarkdownFile? Previous { get; init; }
public required MarkdownFile? Next { get; init; }
public required string NavigationHtml { get; init; }
public required string? LegacyUrl { get; init; }
public required string? UrlPathPrefix { get; init; }
public required string? GithubEditUrl { get; init; }
public required string? ReportIssueUrl { get; init; }
Expand Down
7 changes: 7 additions & 0 deletions src/docs-assembler/AssembleContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class AssembleContext

public IFileInfo NavigationPath { get; }

public IFileInfo HistoryMappingPath { get; }

public IDirectoryInfo CheckoutDirectory { get; set; }

public IDirectoryInfo OutputDirectory { get; set; }
Expand Down Expand Up @@ -60,6 +62,11 @@ public AssembleContext(
ExtractAssemblerConfiguration(navigationPath, "navigation.yml");
NavigationPath = ReadFileSystem.FileInfo.New(navigationPath);

var historyMappingPath = Path.Combine(Paths.WorkingDirectoryRoot.FullName, "src", "docs-assembler", "historymapping.yml");
if (!ReadFileSystem.File.Exists(historyMappingPath))
ExtractAssemblerConfiguration(historyMappingPath, "historymapping.yml");
HistoryMappingPath = ReadFileSystem.FileInfo.New(historyMappingPath);

CheckoutDirectory = ReadFileSystem.DirectoryInfo.New(checkoutDirectory ?? ".artifacts/checkouts");
OutputDirectory = ReadFileSystem.DirectoryInfo.New(output ?? ".artifacts/assembly");

Expand Down
52 changes: 52 additions & 0 deletions src/docs-assembler/AssembleSources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public class AssembleSources

public FrozenDictionary<Uri, TocTopLevelMapping> TocTopLevelMappings { get; }

public FrozenDictionary<string, string> HistoryMappings { get; }

public FrozenDictionary<Uri, TocConfigurationMapping> TocConfigurationMapping { get; }

public TableOfContentsTreeCollector TreeCollector { get; } = new();
Expand All @@ -55,6 +57,7 @@ private AssembleSources(AssembleContext assembleContext, Checkout[] checkouts)
{
AssembleContext = assembleContext;
TocTopLevelMappings = GetConfiguredSources(assembleContext);
HistoryMappings = GetHistoryMapping(assembleContext);

var crossLinkFetcher = new AssemblerCrossLinkFetcher(NullLoggerFactory.Instance, assembleContext.Configuration);
UriResolver = new PublishEnvironmentUriResolver(TocTopLevelMappings, assembleContext.Environment);
Expand Down Expand Up @@ -102,6 +105,55 @@ private AssembleSources(AssembleContext assembleContext, Checkout[] checkouts)
.ToFrozenDictionary();
}

private static FrozenDictionary<string, string> GetHistoryMapping(AssembleContext context)
{
var dictionary = new Dictionary<string, string>();
var reader = new YamlStreamReader(context.HistoryMappingPath, context.Collector);
string? stack = null;
foreach (var entry in reader.Read())
{
switch (entry.Key)
{
case "stack":
stack = reader.ReadString(entry.Entry);
break;

case "mappings":
ReadHistoryMappings(dictionary, reader, entry, stack);
break;
}
}

return dictionary.OrderByDescending(x => x.Key.Length).ToFrozenDictionary();

static void ReadHistoryMappings(IDictionary<string, string> dictionary, YamlStreamReader reader, YamlToplevelKey entry, string? newStack)
{
if (entry.Entry.Value is not YamlMappingNode mappings)
{
reader.EmitWarning($"It wasn't possible to read the mappings");
return;
}

foreach (var mapping in mappings)
{
var mappingKey = $"{((YamlScalarNode)mapping.Key).Value}/";
var mappingValue = ((YamlScalarNode)mapping.Value).Value;
if (mappingKey.Length == 1 || mappingValue is null)
{
reader.EmitWarning($"'{mapping.Key}' or '{mapping.Value}' is not a valid mapping");
continue;
}

if (mappingValue.Equals("stack", StringComparison.OrdinalIgnoreCase) && newStack is not null)
mappingValue = newStack;
if (dictionary.TryGetValue(mappingKey, out _))
reader.EmitWarning($"'{mappingKey}' is already mapped to '{mappingValue}'");
else
dictionary[mappingKey] = mappingValue;
}
}
}


public static FrozenDictionary<Uri, TocTopLevelMapping> GetConfiguredSources(AssembleContext context)
{
Expand Down
8 changes: 6 additions & 2 deletions src/docs-assembler/Building/AssemblerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Frozen;
using Documentation.Assembler.Navigation;
using Elastic.Markdown;
using Elastic.Markdown.IO.HistoryMapping;
using Microsoft.Extensions.Logging;

namespace Documentation.Assembler.Building;
Expand All @@ -13,11 +14,14 @@ public class AssemblerBuilder(
ILoggerFactory logger,
AssembleContext context,
GlobalNavigationHtmlWriter writer,
GlobalNavigationPathProvider pathProvider
GlobalNavigationPathProvider pathProvider,
IHistoryMapper? historyMapper
)
{
private GlobalNavigationHtmlWriter HtmlWriter { get; } = writer;

private IHistoryMapper? HistoryMapper { get; } = historyMapper;

public async Task BuildAllAsync(FrozenDictionary<string, AssemblerDocumentationSet> assembleSets, Cancel ctx)
{
if (context.OutputDirectory.Exists)
Expand Down Expand Up @@ -54,7 +58,7 @@ public async Task BuildAllAsync(FrozenDictionary<string, AssemblerDocumentationS

private async Task BuildAsync(AssemblerDocumentationSet set, Cancel ctx)
{
var generator = new DocumentationGenerator(set.DocumentationSet, logger, HtmlWriter, pathProvider);
var generator = new DocumentationGenerator(set.DocumentationSet, logger, HtmlWriter, pathProvider, historyMapper: HistoryMapper);
await generator.GenerateAll(ctx);
}

Expand Down
5 changes: 4 additions & 1 deletion src/docs-assembler/Cli/RepositoryCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Actions.Core.Services;
using ConsoleAppFramework;
using Documentation.Assembler.Building;
using Documentation.Assembler.Mapping;
using Documentation.Assembler.Navigation;
using Documentation.Assembler.Sourcing;
using Elastic.Documentation.Tooling.Diagnostics.Console;
Expand Down Expand Up @@ -97,7 +98,9 @@ public async Task<int> BuildAll(
var pathProvider = new GlobalNavigationPathProvider(navigationFile, assembleSources, assembleContext);
var htmlWriter = new GlobalNavigationHtmlWriter(navigationFile, assembleContext, navigation, assembleSources);

var builder = new AssemblerBuilder(logger, assembleContext, htmlWriter, pathProvider);
var historyMapper = new PageHistoryMapper(assembleSources.HistoryMappings);

var builder = new AssemblerBuilder(logger, assembleContext, htmlWriter, pathProvider, historyMapper);
await builder.BuildAllAsync(assembleSources.AssembleSets, ctx);

var sitemapBuilder = new SitemapBuilder(navigation.NavigationItems, assembleContext.WriteFileSystem, assembleContext.OutputDirectory);
Expand Down
26 changes: 26 additions & 0 deletions src/docs-assembler/Mapping/PageHistoryMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 Elastic.Markdown.IO.HistoryMapping;

namespace Documentation.Assembler.Mapping;

public record PageHistoryMapper : IHistoryMapper
{
private IReadOnlyDictionary<string, string> PreviousUrls { get; }

public PageHistoryMapper(IReadOnlyDictionary<string, string> previousUrls) => PreviousUrls = previousUrls;

public string? MapLegacyUrl(string? currentUrl)
{
if (currentUrl is null)
return null;

var versionMarker = PreviousUrls.FirstOrDefault(x => currentUrl.Contains(x.Key));
if (versionMarker.Key == string.Empty)
return null;

return !currentUrl.Contains("current") ? null : currentUrl.Replace($"{versionMarker.Key}/current/", $"{versionMarker.Key}/{versionMarker.Value}/");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ public AssemblerDocumentationSet(
AssembleContext context,
Checkout checkout,
CrossLinkResolver crossLinkResolver,
TableOfContentsTreeCollector treeCollector
)
TableOfContentsTreeCollector treeCollector)
{
AssembleContext = context;
Checkout = checkout;
Expand Down
1 change: 1 addition & 0 deletions src/docs-assembler/docs-assembler.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
<ItemGroup>
<EmbeddedResource Include="assembler.yml" />
<EmbeddedResource Include="navigation.yml" />
<EmbeddedResource Include="historymapping.yml" />
</ItemGroup>
</Project>
Loading
Loading