|
// 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;
namespace Aspire.Hosting;
/// <summary>
/// Marks a method, type, or assembly-level type as an ATS (Aspire Type System) export.
/// </summary>
/// <remarks>
/// <para>
/// This attribute serves multiple purposes:
/// </para>
/// <list type="number">
/// <item>
/// <description>
/// <b>Capability exports (on methods):</b> Marks a static method as an ATS capability.
/// Specify just the method name - the capability ID is computed as <c>{AssemblyName}/{methodName}</c>.
/// For example: <c>"addRedis"</c> in Aspire.Hosting.Redis becomes <c>Aspire.Hosting.Redis/addRedis</c>.
/// </description>
/// </item>
/// <item>
/// <description>
/// <b>Type exports (on types):</b> Marks a type as an ATS-exported type.
/// The type ID is automatically derived as <c>{AssemblyName}/{TypeName}</c>.
/// For example: <c>RedisResource</c> in Aspire.Hosting.Redis becomes <c>Aspire.Hosting.Redis/RedisResource</c>.
/// </description>
/// </item>
/// <item>
/// <description>
/// <b>Context types (on types with ExposeProperties):</b> When <see cref="ExposeProperties"/> is true,
/// the type's properties are automatically exposed as get/set capabilities for use in callbacks.
/// </description>
/// </item>
/// <item>
/// <description>
/// <b>External type exports (assembly-level):</b> For types you don't own, use assembly-level
/// with <see cref="Type"/> property to include them in the ATS type system.
/// </description>
/// </item>
/// </list>
/// </remarks>
/// <example>
/// <code>
/// // Capability export on a method - just specify the method name
/// [AspireExport("addRedis", Description = "Adds a Redis resource")]
/// public static IResourceBuilder<RedisResource> AddRedis(...) { }
/// // Scanner computes capability ID: Aspire.Hosting.Redis/addRedis
///
/// // Type export - type ID derived as {AssemblyName}/{TypeName}
/// [AspireExport]
/// public class RedisResource : ContainerResource { }
/// // Type ID: Aspire.Hosting.Redis/RedisResource
///
/// // Context type with properties exposed as capabilities
/// [AspireExport(ExposeProperties = true)]
/// public class EnvironmentCallbackContext
/// {
/// public Dictionary<string, object> EnvironmentVariables { get; }
/// // Getter: Aspire.Hosting.ApplicationModel/EnvironmentCallbackContext.environmentVariables
/// // Setter: Aspire.Hosting.ApplicationModel/EnvironmentCallbackContext.setEnvironmentVariables
/// }
///
/// // Member-level opt-in with ignore for specific members
/// [AspireExport(ExposeProperties = true)]
/// public class SomeContext
/// {
/// public string Name { get; } // Exposed
/// [AspireExportIgnore]
/// public ILogger Logger { get; } // Not exposed
/// }
///
/// // Assembly-level export for types you don't own
/// [assembly: AspireExport(typeof(IConfiguration))]
/// // Type ID: Microsoft.Extensions.Configuration.Abstractions/IConfiguration
/// </code>
/// </example>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Assembly | AttributeTargets.Property,
Inherited = false,
AllowMultiple = true)]
[Experimental("ASPIREATS001")]
public sealed class AspireExportAttribute : Attribute
{
/// <summary>
/// Initializes a new instance for a capability export (on methods).
/// </summary>
/// <param name="id">
/// The method name for this capability. The full capability ID is computed
/// as <c>{AssemblyName}/{methodName}</c>.
/// For example: <c>"addRedis"</c> in Aspire.Hosting.Redis becomes
/// <c>Aspire.Hosting.Redis/addRedis</c>.
/// </param>
public AspireExportAttribute(string id)
{
Id = id ?? throw new ArgumentNullException(nameof(id));
}
/// <summary>
/// Initializes a new instance for a type export.
/// </summary>
/// <remarks>
/// The type ID is automatically derived as <c>{AssemblyName}/{TypeName}</c>.
/// Set <see cref="ExposeProperties"/> to true for context types whose properties
/// should be exposed as get/set capabilities.
/// </remarks>
public AspireExportAttribute()
{
}
/// <summary>
/// Initializes a new instance for an assembly-level type export.
/// </summary>
/// <param name="type">The CLR type to export to ATS.</param>
/// <remarks>
/// Use this constructor at assembly level for types you don't own.
/// The type ID is derived as <c>{type.Assembly.Name}/{type.Name}</c>.
/// </remarks>
/// <example>
/// <code>
/// [assembly: AspireExport(typeof(IConfiguration))]
/// // Type ID: Microsoft.Extensions.Configuration.Abstractions/IConfiguration
/// </code>
/// </example>
public AspireExportAttribute(Type type)
{
Type = type ?? throw new ArgumentNullException(nameof(type));
}
/// <summary>
/// Gets the method name for capability exports.
/// </summary>
/// <remarks>
/// <para>
/// The full capability ID is computed as <c>{AssemblyName}/{Id}</c>.
/// </para>
/// <para>
/// This is null for type exports.
/// </para>
/// </remarks>
public string? Id { get; }
/// <summary>
/// Gets or sets the CLR type for assembly-level type exports.
/// </summary>
/// <remarks>
/// Use this at assembly level to export types you don't own to ATS.
/// </remarks>
public Type? Type { get; set; }
/// <summary>
/// Gets or sets a description of what this export does.
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Gets or sets the method name to use in generated polyglot SDKs.
/// </summary>
/// <remarks>
/// <para>
/// When not specified, the method name from <see cref="Id"/> is used directly.
/// </para>
/// <para>
/// Use this property to override the generated name when disambiguation is needed
/// (e.g., to avoid collisions with another integration's method of the same name).
/// Each language generator will apply its own formatting convention
/// (camelCase for TypeScript, snake_case for Python, etc.).
/// </para>
/// </remarks>
public string? MethodName { get; set; }
/// <summary>
/// Gets or sets whether to expose properties of this type as ATS capabilities.
/// </summary>
/// <remarks>
/// <para>
/// When true, the type's public instance properties that return ATS-compatible types
/// are automatically exposed as get/set capabilities (unless marked with <see cref="AspireExportIgnoreAttribute"/>).
/// </para>
/// <para>
/// Use this for context types passed to callbacks (like <c>EnvironmentCallbackContext</c>)
/// that provide access to runtime state.
/// </para>
/// <para>
/// Property capabilities are named as:
/// <list type="bullet">
/// <item><description><c>{Package}/{TypeName}.{propertyName}</c> for getters (camelCase property name)</description></item>
/// <item><description><c>{Package}/{TypeName}.set{PropertyName}</c> for setters (writable properties only)</description></item>
/// </list>
/// </para>
/// </remarks>
public bool ExposeProperties { get; set; }
/// <summary>
/// Gets or sets whether to expose public instance methods of this type as ATS capabilities.
/// </summary>
/// <remarks>
/// <para>
/// When true, the type's public instance methods are automatically exposed as capabilities
/// (unless marked with <see cref="AspireExportIgnoreAttribute"/>).
/// </para>
/// <para>
/// Method capabilities are named as <c>{Package}/{TypeName}.{methodName}</c> (camelCase method name).
/// The first parameter of the capability will be a handle to the instance.
/// </para>
/// </remarks>
public bool ExposeMethods { get; set; }
}
|