|
| 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.Collections.Frozen; |
| 6 | +using System.Diagnostics.CodeAnalysis; |
| 7 | +using System.Text.Json; |
| 8 | +using Elastic.Markdown.IO.Configuration; |
| 9 | +using Elastic.Markdown.IO.State; |
| 10 | +using Microsoft.Extensions.Logging; |
| 11 | + |
| 12 | +namespace Elastic.Markdown.CrossLinks; |
| 13 | + |
| 14 | +public interface ICrossLinkResolver |
| 15 | +{ |
| 16 | + Task FetchLinks(); |
| 17 | + bool TryResolve(Uri crosslinkUri, [NotNullWhen(true)]out Uri? resolvedUri); |
| 18 | +} |
| 19 | + |
| 20 | +public class CrossLinkResolver(ConfigurationFile configuration, ILoggerFactory logger) : ICrossLinkResolver |
| 21 | +{ |
| 22 | + private readonly string[] _links = configuration.CrossLinkRepositories; |
| 23 | + private FrozenDictionary<string, LinkReference> _linkReferences = new Dictionary<string, LinkReference>().ToFrozenDictionary(); |
| 24 | + private readonly ILogger _logger = logger.CreateLogger(nameof(CrossLinkResolver)); |
| 25 | + |
| 26 | + public static LinkReference Deserialize(string json) => |
| 27 | + JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LinkReference)!; |
| 28 | + |
| 29 | + public async Task FetchLinks() |
| 30 | + { |
| 31 | + using var client = new HttpClient(); |
| 32 | + var dictionary = new Dictionary<string, LinkReference>(); |
| 33 | + foreach (var link in _links) |
| 34 | + { |
| 35 | + var url = $"https://elastic-docs-link-index.s3.us-east-2.amazonaws.com/elastic/{link}/main/links.json"; |
| 36 | + _logger.LogInformation($"Fetching {url}"); |
| 37 | + var json = await client.GetStringAsync(url); |
| 38 | + var linkReference = Deserialize(json); |
| 39 | + dictionary.Add(link, linkReference); |
| 40 | + } |
| 41 | + _linkReferences = dictionary.ToFrozenDictionary(); |
| 42 | + } |
| 43 | + |
| 44 | + public bool TryResolve(Uri crosslinkUri, [NotNullWhen(true)]out Uri? resolvedUri) => |
| 45 | + TryResolve(_linkReferences, crosslinkUri, out resolvedUri); |
| 46 | + |
| 47 | + public static bool TryResolve(IDictionary<string, LinkReference> lookup, Uri crosslinkUri, [NotNullWhen(true)] out Uri? resolvedUri) |
| 48 | + { |
| 49 | + resolvedUri = null; |
| 50 | + if (!lookup.TryGetValue(crosslinkUri.Scheme, out var linkReference)) |
| 51 | + { |
| 52 | + //TODO emit error |
| 53 | + return false; |
| 54 | + } |
| 55 | + var lookupPath = crosslinkUri.AbsolutePath.TrimStart('/'); |
| 56 | + |
| 57 | + if (!linkReference.Links.TryGetValue(lookupPath, out var link)) |
| 58 | + { |
| 59 | + //TODO emit error |
| 60 | + return false; |
| 61 | + } |
| 62 | + |
| 63 | + //https://docs-v3-preview.elastic.dev/elastic/docs-content/tree/main/cloud-account/change-your-password |
| 64 | + var path = lookupPath.Replace(".md", ""); |
| 65 | + var baseUri = new Uri("https://docs-v3-preview.elastic.dev"); |
| 66 | + resolvedUri = new Uri(baseUri, $"elastic/{crosslinkUri.Scheme}/tree/main/{path}"); |
| 67 | + return true; |
| 68 | + } |
| 69 | +} |
0 commit comments