File: Projects\IAppHostServerSession.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 Aspire.Cli.Processes;
using Aspire.Cli.Utils;
 
namespace Aspire.Cli.Projects;
 
/// <summary>
/// Abstraction over an AppHost server child-process session. Two shapes are served through this
/// seam: the short-lived codegen/scaffolding session (start, grab an RPC client, dispose) and the
/// longer-lived <c>aspire run</c> session that additionally exposes the process lifetime
/// (<see cref="WaitForExitAsync"/>), captured output, and the connection details the guest AppHost
/// needs. Tests substitute a fake implementation that returns canned RPC results without launching
/// a real process or opening a socket.
/// </summary>
internal interface IAppHostServerSession : IAsyncDisposable
{
    /// <summary>
    /// Gets the authentication token injected into the server environment. Available before
    /// <see cref="StartAsync"/> so callers can plumb it into the guest AppHost environment.
    /// </summary>
    string AuthenticationToken { get; }
 
    /// <summary>
    /// Gets the RPC socket path, or <see langword="null"/> if <see cref="StartAsync"/> has not
    /// been called (or threw before the process was published).
    /// </summary>
    string? SocketPath { get; }
 
    /// <summary>
    /// Gets the output collector for the server's stdout/stderr, or <see langword="null"/> if
    /// <see cref="StartAsync"/> has not been called (or threw before the process was published).
    /// </summary>
    OutputCollector? Output { get; }
 
    /// <summary>
    /// Gets whether the underlying AppHost server process has exited, or <see langword="null"/>
    /// if <see cref="StartAsync"/> has not been called (or threw before the process was published).
    /// </summary>
    bool? HasServerExited { get; }
 
    /// <summary>
    /// Reads the AppHost server process's exit code if it has exited, or <see langword="null"/>
    /// if the server is still running, has not been started, or the exit code cannot be read.
    /// </summary>
    int? TryGetServerExitCode();
 
    /// <summary>
    /// Launches the AppHost server process and wires lifecycle observation. The returned task
    /// completes once the process has been spawned.
    /// </summary>
    Task StartAsync();
 
    /// <summary>
    /// Returns the task that completes with the server process exit code. Used by the run path to
    /// observe the server lifetime alongside the guest AppHost.
    /// </summary>
    Task<int> WaitForExitAsync();
 
    /// <summary>
    /// Connects to the running AppHost server and returns an RPC client for code generation.
    /// </summary>
    Task<IAppHostRpcClient> GetRpcClientAsync(CancellationToken cancellationToken);
}
 
/// <summary>
/// Creates <see cref="IAppHostServerSession"/> instances. Production wires this to construct real
/// <see cref="AppHostServerSession"/> instances; tests inject a factory that returns a fake session.
/// </summary>
internal interface IAppHostServerSessionFactory
{
    /// <summary>
    /// Creates an unstarted session for the already-prepared <paramref name="appHostServerProject"/>.
    /// The caller drives it via <see cref="IAppHostServerSession.StartAsync"/>,
    /// <see cref="IAppHostServerSession.GetRpcClientAsync"/>, then disposal. Cancelling
    /// <paramref name="stopRequested"/> (or disposing the session) terminates the server process.
    /// </summary>
    /// <remarks>
    /// The short-lived codegen/scaffolding path passes no graceful-shutdown wiring
    /// (<paramref name="gracefulShutdownSignaler"/> and <paramref name="shutdownService"/> are
    /// <see langword="null"/>, <paramref name="isolateConsole"/> is <see langword="false"/>) because
    /// that session is started, queried over RPC, and disposed within a single operation. The
    /// <c>aspire run</c> path supplies the real signaler/window and isolates the console so a user
    /// Ctrl+C reaches the CLI and drives the shared shutdown ladder rather than the child directly.
    /// </remarks>
    IAppHostServerSession Create(
        IAppHostServerProject appHostServerProject,
        Dictionary<string, string>? environmentVariables,
        bool debug,
        IProcessTreeGracefulShutdownSignaler? gracefulShutdownSignaler,
        IGracefulShutdownWindow? shutdownService,
        bool isolateConsole,
        CancellationToken stopRequested);
}