File: ApplicationModel\ResourceUrlsEditor.cs
Web Access
Project: src\src\Aspire.Hosting\Aspire.Hosting.csproj (Aspire.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Aspire.Hosting.ApplicationModel;
 
/// <summary>
/// Provides an ATS-first editor for resource URLs within polyglot callbacks.
/// </summary>
[AspireExport]
internal sealed class ResourceUrlsEditor(DistributedApplicationExecutionContext executionContext, List<ResourceUrlAnnotation> urls, CancellationToken cancellationToken = default)
{
    private readonly DistributedApplicationExecutionContext _executionContext = executionContext ?? throw new ArgumentNullException(nameof(executionContext));
    private readonly List<ResourceUrlAnnotation> _urls = urls ?? throw new ArgumentNullException(nameof(urls));
    private readonly CancellationToken _cancellationToken = cancellationToken;
 
    /// <summary>
    /// Adds a displayed URL.
    /// </summary>
    /// <param name="url">The URL to add, specified as a string or reference expression.</param>
    /// <param name="displayText">The optional display text to show for the URL.</param>
    [AspireExport("ResourceUrlsEditor.add", MethodName = "add")]
    public async Task Add([AspireUnion(typeof(string), typeof(ReferenceExpression))] object url, string? displayText = null)
    {
        ArgumentNullException.ThrowIfNull(url);
 
        switch (url)
        {
            case string stringUrl:
                AddUrlAnnotation(stringUrl, displayText);
                break;
            case ReferenceExpression referenceExpression:
                var endpoint = referenceExpression.ValueProviders.OfType<EndpointReference>().FirstOrDefault();
                var urlValue = await referenceExpression.GetValueAsync(_cancellationToken).ConfigureAwait(false);
                if (!string.IsNullOrEmpty(urlValue))
                {
                    AddUrlAnnotation(urlValue, displayText, endpoint);
                }
                break;
            default:
                throw new ArgumentException(
                    $"Unsupported URL type '{url.GetType().Name}'. Expected string or ReferenceExpression.",
                    nameof(url));
        }
    }
 
    /// <summary>
    /// Adds a displayed URL for a specific endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint the URL is associated with.</param>
    /// <param name="url">The URL to add, specified as a string or reference expression.</param>
    /// <param name="displayText">The optional display text to show for the URL.</param>
    [AspireExport("ResourceUrlsEditor.addForEndpoint", MethodName = "addForEndpoint")]
    public async Task AddForEndpoint(EndpointReference endpoint, [AspireUnion(typeof(string), typeof(ReferenceExpression))] object url, string? displayText = null)
    {
        ArgumentNullException.ThrowIfNull(endpoint);
        ArgumentNullException.ThrowIfNull(url);
 
        switch (url)
        {
            case string stringUrl:
                AddUrlAnnotation(stringUrl, displayText, endpoint);
                break;
            case ReferenceExpression referenceExpression:
                var urlValue = await referenceExpression.GetValueAsync(_cancellationToken).ConfigureAwait(false);
                if (!string.IsNullOrEmpty(urlValue))
                {
                    AddUrlAnnotation(urlValue, displayText, endpoint);
                }
                break;
            default:
                throw new ArgumentException(
                    $"Unsupported URL type '{url.GetType().Name}'. Expected string or ReferenceExpression.",
                    nameof(url));
        }
    }
 
    /// <summary>
    /// Gets the execution context associated with this editor.
    /// </summary>
    [AspireExport]
    public DistributedApplicationExecutionContext ExecutionContext => _executionContext;
 
    private void AddUrlAnnotation(string url, string? displayText, EndpointReference? endpoint = null)
    {
        _urls.Add(new ResourceUrlAnnotation
        {
            Endpoint = endpoint,
            Url = url,
            DisplayText = displayText
        });
    }
}