File: Pipelines\PipelineStepFactoryExtensions.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.
 
using System.Diagnostics.CodeAnalysis;
using Aspire.Hosting.ApplicationModel;
 
namespace Aspire.Hosting.Pipelines;
 
/// <summary>
/// Provides extension methods for adding pipeline steps to resources.
/// </summary>
[Experimental("ASPIREPIPELINES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
public static class PipelineStepFactoryExtensions
{
    /// <summary>
    /// Adds a pipeline step to the resource that will be executed during deployment.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="factory">A factory function that creates the pipeline step.</param>
    /// <returns>The resource builder for chaining.</returns>
    /// <remarks>This overload is not available in polyglot app hosts. Use the overload that takes a step name and callback instead.</remarks>
    [AspireExportIgnore(Reason = "Polyglot callbacks can't construct and return PipelineStep instances. Use the step-name overload instead.")]
    public static IResourceBuilder<T> WithPipelineStepFactory<T>(
        this IResourceBuilder<T> builder,
        Func<PipelineStepFactoryContext, PipelineStep> factory) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(factory);
 
        return builder.WithAnnotation(new PipelineStepAnnotation(factory));
    }
 
    /// <summary>
    /// Adds a pipeline step to the resource that will be executed during deployment.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="factory">An async factory function that creates the pipeline step.</param>
    /// <returns>The resource builder for chaining.</returns>
    /// <remarks>This overload is not available in polyglot app hosts. Use the overload that takes a step name and callback instead.</remarks>
    [AspireExportIgnore(Reason = "Polyglot callbacks can't construct and return PipelineStep instances. Use the step-name overload instead.")]
    public static IResourceBuilder<T> WithPipelineStepFactory<T>(
        this IResourceBuilder<T> builder,
        Func<PipelineStepFactoryContext, Task<PipelineStep>> factory) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(factory);
 
        return builder.WithAnnotation(new PipelineStepAnnotation(factory));
    }
 
    /// <summary>
    /// Adds multiple pipeline steps to the resource that will be executed during deployment.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="factory">A factory function that creates multiple pipeline steps.</param>
    /// <returns>The resource builder for chaining.</returns>
    /// <remarks>This overload is not available in polyglot app hosts. Use the overload that takes a step name and callback instead, and call it multiple times.</remarks>
    [AspireExportIgnore(Reason = "Polyglot callbacks can't construct and return PipelineStep instances. Use the step-name overload instead.")]
    public static IResourceBuilder<T> WithPipelineStepFactory<T>(
        this IResourceBuilder<T> builder,
        Func<PipelineStepFactoryContext, IEnumerable<PipelineStep>> factory) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(factory);
 
        return builder.WithAnnotation(new PipelineStepAnnotation(factory));
    }
 
    /// <summary>
    /// Adds multiple pipeline steps to the resource that will be executed during deployment.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="factory">An async factory function that creates multiple pipeline steps.</param>
    /// <returns>The resource builder for chaining.</returns>
    /// <remarks>This overload is not available in polyglot app hosts. Use the overload that takes a step name and callback instead, and call it multiple times.</remarks>
    [AspireExportIgnore(Reason = "Polyglot callbacks can't construct and return PipelineStep instances. Use the step-name overload instead.")]
    public static IResourceBuilder<T> WithPipelineStepFactory<T>(
        this IResourceBuilder<T> builder,
        Func<PipelineStepFactoryContext, Task<IEnumerable<PipelineStep>>> factory) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(factory);
 
        return builder.WithAnnotation(new PipelineStepAnnotation(factory));
    }
 
    /// <summary>
    /// Adds a pipeline step to the resource that will be executed during deployment.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="stepName">The unique name of the pipeline step.</param>
    /// <param name="callback">The callback to execute when the step runs.</param>
    /// <param name="dependsOn">Optional step names that this step depends on.</param>
    /// <param name="requiredBy">Optional step names that require this step.</param>
    /// <param name="tags">Optional tags that categorize this step.</param>
    /// <param name="description">An optional human-readable description of the step.</param>
    /// <returns>The resource builder for chaining.</returns>
    [AspireExport("withPipelineStepFactory", Description = "Adds a pipeline step to the resource")]
    public static IResourceBuilder<T> WithPipelineStepFactory<T>(
        this IResourceBuilder<T> builder,
        string stepName,
        Func<PipelineStepContext, Task> callback,
        string[]? dependsOn = null,
        string[]? requiredBy = null,
        string[]? tags = null,
        string? description = null) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentException.ThrowIfNullOrEmpty(stepName);
        ArgumentNullException.ThrowIfNull(callback);
 
        return builder.WithPipelineStepFactory(_ => new PipelineStep
        {
            Name = stepName,
            Description = description,
            Action = callback,
            DependsOnSteps = dependsOn is [..] ? [.. dependsOn] : [],
            RequiredBySteps = requiredBy is [..] ? [.. requiredBy] : [],
            Tags = tags is [..] ? [.. tags] : [],
            Resource = builder.Resource
        });
    }
 
    /// <summary>
    /// Registers a callback to be executed during the pipeline configuration phase,
    /// allowing modification of step dependencies and relationships.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="callback">The callback function to execute during the configuration phase.</param>
    /// <returns>The resource builder for chaining.</returns>
    [AspireExport("withPipelineConfigurationAsync", Description = "Configures pipeline step dependencies via an async callback")]
    public static IResourceBuilder<T> WithPipelineConfiguration<T>(
        this IResourceBuilder<T> builder,
        Func<PipelineConfigurationContext, Task> callback) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(callback);
 
        return builder.WithAnnotation(new PipelineConfigurationAnnotation(callback));
    }
 
    /// <summary>
    /// Registers a callback to be executed during the pipeline configuration phase,
    /// allowing modification of step dependencies and relationships.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="callback">The callback function to execute during the configuration phase.</param>
    /// <returns>The resource builder for chaining.</returns>
    [AspireExport("withPipelineConfiguration", Description = "Configures pipeline step dependencies via a callback")]
    public static IResourceBuilder<T> WithPipelineConfiguration<T>(
        this IResourceBuilder<T> builder,
        Action<PipelineConfigurationContext> callback) where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(callback);
 
        return builder.WithAnnotation(new PipelineConfigurationAnnotation(callback));
    }
}