| File: Documentation\Docs\DocsFetcher.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace Aspire.Cli.Documentation.Docs; /// <summary> /// Service for fetching aspire.dev documentation content. /// </summary> internal interface IDocsFetcher { /// <summary> /// Fetches the small (abridged) documentation content. /// Uses ETag-based caching to avoid re-downloading unchanged content. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The documentation content, or null if fetch failed.</returns> Task<string?> FetchDocsAsync(CancellationToken cancellationToken = default); } /// <summary> /// Default implementation of <see cref="IDocsFetcher"/> that fetches from aspire.dev with ETag caching. /// </summary> internal sealed class DocsFetcher(HttpClient httpClient, IDocsCache cache, IConfiguration configuration, ILogger<DocsFetcher> logger) : IDocsFetcher { private readonly HttpClient _httpClient = httpClient; private readonly IDocsCache _cache = cache; private readonly string _llmsTxtUrl = DocsSourceConfiguration.GetLlmsTxtUrl(configuration); private readonly string _cacheKey = DocsSourceConfiguration.GetContentCacheKey(DocsSourceConfiguration.GetLlmsTxtUrl(configuration)); private readonly ILogger<DocsFetcher> _logger = logger; public async Task<string?> FetchDocsAsync(CancellationToken cancellationToken = default) { await MigrateLegacyCacheAsync(cancellationToken).ConfigureAwait(false); return await CachedHttpDocumentFetcher.FetchAsync(_httpClient, _cache, _llmsTxtUrl, _cacheKey, _logger, cancellationToken).ConfigureAwait(false); } private async Task MigrateLegacyCacheAsync(CancellationToken cancellationToken) { if (string.Equals(_cacheKey, _llmsTxtUrl, StringComparison.Ordinal)) { return; } var currentContent = await _cache.GetAsync(_cacheKey, cancellationToken).ConfigureAwait(false); var currentETag = await _cache.GetETagAsync(_cacheKey, cancellationToken).ConfigureAwait(false); if (currentContent is not null || currentETag is not null) { await ClearLegacyCacheAsync(cancellationToken).ConfigureAwait(false); return; } var legacyContent = await _cache.GetAsync(_llmsTxtUrl, cancellationToken).ConfigureAwait(false); var legacyETag = await _cache.GetETagAsync(_llmsTxtUrl, cancellationToken).ConfigureAwait(false); if (legacyContent is not null) { await _cache.SetAsync(_cacheKey, legacyContent, cancellationToken).ConfigureAwait(false); } if (!string.IsNullOrEmpty(legacyETag)) { await _cache.SetETagAsync(_cacheKey, legacyETag, cancellationToken).ConfigureAwait(false); } if (legacyContent is not null || !string.IsNullOrEmpty(legacyETag)) { await ClearLegacyCacheAsync(cancellationToken).ConfigureAwait(false); } } private async Task ClearLegacyCacheAsync(CancellationToken cancellationToken) { await _cache.InvalidateAsync(_llmsTxtUrl, cancellationToken).ConfigureAwait(false); await _cache.SetETagAsync(_llmsTxtUrl, null, cancellationToken).ConfigureAwait(false); } }