File: ApplicationModel\ParameterResource.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>
/// Represents a parameter resource.
/// </summary>
public class ParameterResource : Resource, IResourceWithoutLifetime, IManifestExpressionProvider, IValueProvider
{
    private readonly Lazy<string> _lazyValue;
    private readonly Func<ParameterDefault?, string> _valueGetter;
    private string? _configurationKey;
 
    /// <summary>
    /// Initializes a new instance of <see cref="ParameterResource"/>.
    /// </summary>
    /// <param name="name">The name of the parameter resource.</param>
    /// <param name="callback">The callback function to retrieve the value of the parameter.</param>
    /// <param name="secret">A flag indicating whether the parameter is secret.</param>
    public ParameterResource(string name, Func<ParameterDefault?, string> callback, bool secret = false) : base(name)
    {
        ArgumentNullException.ThrowIfNull(name);
        ArgumentNullException.ThrowIfNull(callback);
 
        _valueGetter = callback;
        _lazyValue = new Lazy<string>(() => _valueGetter(Default));
        Secret = secret;
    }
 
    /// <summary>
    /// Gets the value of the parameter.
    /// </summary>
    /// <remarks>
    /// This property is obsolete. Use <see cref="GetValueAsync(CancellationToken)"/> for async access or pass the <see cref="ParameterResource"/> directly to methods that accept it (e.g., environment variables).
    /// </remarks>
    [Obsolete("Use GetValueAsync for async access or pass the ParameterResource directly to methods that accept it (e.g., environment variables).")]
    public string Value => GetValueAsync(default).AsTask().GetAwaiter().GetResult()!;
 
    internal string ValueInternal => _lazyValue.Value;
 
    /// <summary>
    /// Represents how the default value of the parameter should be retrieved.
    /// </summary>
    public ParameterDefault? Default { get; set; }
 
    /// <summary>
    /// Gets a value indicating whether the parameter is secret.
    /// </summary>
    public bool Secret { get; }
 
    /// <summary>
    /// Gets or sets a value indicating whether the parameter is a connection string.
    /// </summary>
    public bool IsConnectionString { get; set; }
 
    /// <summary>
    /// Gets the expression used in the manifest to reference the value of the parameter.
    /// </summary>
    public string ValueExpression => $"{{{Name}.value}}";
 
    /// <summary>
    /// The configuration key for this parameter. The default format is "ConnectionStrings:{Name}" if the parameter is a connection string,
    /// otherwise it is "Parameters:{Name}".
    /// </summary>
    internal string ConfigurationKey
    {
        get => _configurationKey ?? (IsConnectionString ? $"ConnectionStrings:{Name}" : $"Parameters:{Name}");
        set => _configurationKey = value;
    }
 
    /// <summary>
    /// A task completion source that can be used to wait for the value of the parameter to be set.
    /// </summary>
    internal TaskCompletionSource<string>? WaitForValueTcs { get; set; }
 
    /// <summary>
    /// Gets the value of the parameter asynchronously, waiting if necessary for the value to be set.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token to observe while waiting for the value.</param>
    /// <returns>A task that represents the asynchronous operation, containing the value of the parameter.</returns>
    public async ValueTask<string?> GetValueAsync(CancellationToken cancellationToken)
    {
        if (WaitForValueTcs is not null)
        {
            // Wait for the value to be set if the task completion source is available.
            return await WaitForValueTcs.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
        }
 
        // In publish mode, there's no WaitForValueTcs set.
        return ValueInternal;
    }
 
    /// <summary>
    /// Gets a description of the parameter resource.
    /// </summary>
    public string? Description { get; set; }
 
    /// <summary>
    /// Gets or sets a value indicating whether the description should be rendered as Markdown.
    /// </summary>
    public bool EnableDescriptionMarkdown { get; set; }
 
#pragma warning disable ASPIREINTERACTION001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
    internal InteractionInput CreateInput()
    {
        if (this.TryGetLastAnnotation<InputGeneratorAnnotation>(out var annotation))
        {
            // If the annotation is present, use it to create the input.
            return annotation.InputGenerator(this);
        }
 
        var input = new InteractionInput
        {
            InputType = Secret ? InputType.SecretText : InputType.Text,
            Label = Name,
            Description = Description,
            EnableDescriptionMarkdown = EnableDescriptionMarkdown,
            Placeholder = $"Enter value for {Name}"
        };
        return input;
    }
#pragma warning restore ASPIREINTERACTION001
}