File: Authoring\Resource.cs
Web Access
Project: src\src\Aspire.Hosting.Native.Client\Aspire.Hosting.Native.Client.csproj (Aspire.Hosting.Native.Client)
// 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.Native.Authoring;
 
/// <summary>Contains authoring data only; native runtime state is never stored on this object.</summary>
/// <param name="name">The logical native resource name.</param>
public abstract class Resource(string name)
{
    /// <summary>Gets the resource's declared name.</summary>
    public string Name { get; } = name;
}
 
/// <summary>Contains a generic container declaration, not a running container.</summary>
/// <param name="name">The logical resource name.</param>
/// <param name="image">The image selected by the integration author.</param>
public class ContainerResource(string name, string image) : Resource(name)
{
    /// <summary>Gets the declared image.</summary>
    public string Image { get; } = image;
}
 
/// <summary>Contains a host executable declaration.</summary>
/// <param name="name">The logical resource name.</param>
/// <param name="executablePath">The absolute executable path.</param>
/// <param name="workingDirectory">The absolute working directory.</param>
public sealed class ExecutableResource(string name, string executablePath, string workingDirectory) : Resource(name)
{
    /// <summary>Gets the executable path.</summary>
    public string ExecutablePath { get; } = executablePath;
    /// <summary>Gets the working directory.</summary>
    public string WorkingDirectory { get; } = workingDirectory;
}
 
/// <summary>Contains parameter authoring intent; generated credentials remain native-owned.</summary>
/// <param name="name">The native parameter name.</param>
/// <param name="secret">Whether payloads require native secret treatment.</param>
public sealed class ParameterResource(string name, bool secret) : Resource(name)
{
    /// <summary>Gets whether this is a secret parameter.</summary>
    public bool IsSecret { get; } = secret;
}
 
/// <summary>Exposes a symbolic connection reference, never resolved credentials.</summary>
public interface IResourceWithConnectionString
{
    /// <summary>Gets the connection expression materialized by the native server.</summary>
    ReferenceExpression ConnectionStringExpression { get; }
}
 
/// <summary>Provides typed fluent composition over a transport command plan.</summary>
public interface IResourceBuilder<out T> where T : Resource
{
    /// <summary>Gets the inert authored resource.</summary>
    T Resource { get; }
    /// <summary>Gets the shared authoring builder.</summary>
    DistributedApplicationBuilder ApplicationBuilder { get; }
}
 
/// <summary>Records symbolic syntax. Resolution and secret substitution occur only in the native server.</summary>
public sealed class ReferenceExpression
{
    internal Func<NativeBuildContext, Task<Client.DeclaredValue>> Materialize { get; }
    internal ReferenceExpression(Func<NativeBuildContext, Task<Client.DeclaredValue>> materialize) => Materialize = materialize;
    /// <summary>Creates literal syntax without resolving resource values.</summary>
    /// <param name="value">Literal authoring text, not a resolved reference.</param>
    /// <returns>Native-materialized literal syntax.</returns>
    public static ReferenceExpression Literal(string value) => new(context => context.Composition.CreateLiteralValueAsync(value, context.Token));
    /// <summary>Composes syntax without converting references or credentials into strings.</summary>
    /// <param name="parts">Symbolic components in order.</param>
    /// <returns>A native concatenation expression.</returns>
    public static ReferenceExpression Create(params ReferenceExpression[] parts)
    {
        if (parts.Length == 0)
        {
            throw new ArgumentException("A reference expression needs at least one part.", nameof(parts));
        }
        var syntax = parts.ToArray();
 
        return new(async context =>
        {
            var result = await context.Value(syntax[0]).ConfigureAwait(false);
            foreach (var part in syntax.Skip(1))
            {
                result = await result.ConcatValueAsync(await context.Value(part).ConfigureAwait(false), context.Token).ConfigureAwait(false);
            }
 
            return result;
        });
    }
    /// <summary>Records URI segment encoding performed natively at materialization.</summary>
    /// <returns>Encoded native expression syntax.</returns>
    public ReferenceExpression EncodeUri() => new(async context =>
        await (await context.Value(this).ConfigureAwait(false)).EncodeUriValueAsync(context.Token).ConfigureAwait(false));
    /// <summary>Converts literal authoring text to syntax.</summary>
    /// <param name="value">Literal authoring text.</param>
    /// <returns>A native literal expression.</returns>
    public static implicit operator ReferenceExpression(string value) => Literal(value);
    /// <summary>Never exposes value contents through diagnostics.</summary>
    /// <returns>A non-sensitive diagnostic classification.</returns>
    public override string ToString() => "[native reference expression]";
}
 
/// <summary>Provides late-bound named endpoint syntax rather than a local address.</summary>
public sealed class EndpointReference
{
    internal EndpointReference(Resource resource, string name)
    {
        Resource = resource;
        Name = name;
        Host = Property("host");
        Port = Property("port");
        HostAndPort = Property("hostAndPort");
        ReferenceExpression Property(string property) => new(context =>
            context.Composition.CreateEndpointValueAsync(context.Declaration(Resource), Name, property, context.Token));
    }
    /// <summary>Gets the inert resource owning the named endpoint.</summary>
    public Resource Resource { get; }
    /// <summary>Gets the endpoint's authored name.</summary>
    public string Name { get; }
    /// <summary>Gets symbolic host syntax.</summary>
    public ReferenceExpression Host { get; }
    /// <summary>Gets symbolic port syntax.</summary>
    public ReferenceExpression Port { get; }
    /// <summary>Gets symbolic host-and-port syntax.</summary>
    public ReferenceExpression HostAndPort { get; }
}