From ed6aef36907106a8a7c09d02ce08fab2c075f6ba Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 9 Jun 2026 10:52:46 -0700 Subject: [PATCH 1/6] Expose IServiceProvider and callback contexts to ATS polyglot hosts Several callback contexts that previously carried [AspireExportIgnore] on their IServiceProvider members were ignored entirely because polyglot hosts could not consume IServiceProvider. Now that IServiceProvider is an exported ATS handle, expose those members (named "services") and the contexts that wrap them, and close the WithContainerFiles callback gap so polyglot hosts can build file-system entries through factory methods. - Expose ServiceProvider/Services members as "services" on the command, https endpoint, container build, required-command, and container-file callback contexts; keep only accurate, documented ignores. - Export the WithContainerBuildOptions (async), WithHttpsCertificateConfiguration, SubscribeHttpsEndpointsUpdate, and WithRequiredCommand validation overloads. - Add internal static factory shims (CreateFile/CreateCertificateFile/ CreateDirectory) on ContainerFileSystemCallbackContext plus a WithContainerFilesCallbackExport shim that accepts integer file-mode options. - Add Success()/Failure() helpers on RequiredCommandValidationContext. - Wire all four polyglot app hosts (Go/Java/Python/TypeScript) with real extension-method usage on services, regenerate the five codegen snapshots, and add unit tests for the new factories and validation helpers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ContainerBuildOptionsCallbackAnnotation.cs | 1 + .../ContainerFileSystemCallbackAnnotation.cs | 135 + ...rtificateConfigurationCallbackAnnotaion.cs | 20 + .../HttpsEndpointUpdateCallbackContext.cs | 1 + .../RequiredCommandValidationContext.cs | 14 + .../RequiredCommandValidationResult.cs | 1 + .../ResourceCommandAnnotation.cs | 4 +- .../ApplicationModel/ResourceExtensions.cs | 6 +- .../ContainerResourceBuilderExtensions.cs | 65 +- src/Aspire.Hosting/Dcp/Model/Container.cs | 1 + src/Aspire.Hosting/IInteractionService.cs | 1 - .../RequiredCommandResourceExtensions.cs | 6 +- .../ResourceBuilderExtensions.cs | 6 +- ...TwoPassScanningGeneratedAspire.verified.go | 2237 +++++++- ...oPassScanningGeneratedAspire.verified.java | 1721 +++++- ...TwoPassScanningGeneratedAspire.verified.py | 717 ++- ...TwoPassScanningGeneratedAspire.verified.rs | 1272 ++++- ...TwoPassScanningGeneratedAspire.verified.ts | 4755 +++++++++++++++-- ...ContainerFileSystemCallbackContextTests.cs | 137 + .../RequiredCommandAnnotationTests.cs | 24 + .../Aspire.Hosting/Go/apphost.go | 85 + .../Aspire.Hosting/Java/AppHost.java | 74 + .../Aspire.Hosting/Python/apphost.py | 88 +- .../Aspire.Hosting/TypeScript/apphost.mts | 85 +- 24 files changed, 10982 insertions(+), 474 deletions(-) create mode 100644 tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs diff --git a/src/Aspire.Hosting/ApplicationModel/ContainerBuildOptionsCallbackAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ContainerBuildOptionsCallbackAnnotation.cs index 740397b71d2..08aa150d58c 100644 --- a/src/Aspire.Hosting/ApplicationModel/ContainerBuildOptionsCallbackAnnotation.cs +++ b/src/Aspire.Hosting/ApplicationModel/ContainerBuildOptionsCallbackAnnotation.cs @@ -37,6 +37,7 @@ public ContainerBuildOptionsCallbackAnnotation(Action [Experimental("ASPIREPIPELINES003", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +[AspireExport(ExposeProperties = true)] public sealed class ContainerBuildOptionsCallbackContext { /// diff --git a/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs index f458fb4e711..5caa2927570 100644 --- a/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs +++ b/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs @@ -9,6 +9,12 @@ namespace Aspire.Hosting.ApplicationModel; /// /// Represents a base class for file system entries in a container. /// +/// +/// Exported to ATS as an opaque handle type. Polyglot app hosts never construct or inspect these +/// directly; they create concrete entries through the factory methods on +/// and pass the resulting handles back via the callback. +/// +[AspireExport] public abstract class ContainerFileSystemItem { private string? _name; @@ -266,25 +272,154 @@ public sealed class ContainerFileSystemCallbackAnnotation : IResourceAnnotation /// /// Represents the context for a callback. /// +[AspireExport] public sealed class ContainerFileSystemCallbackContext { /// /// A that can be used to resolve services in the callback. /// + [AspireExport(MethodName = "services")] public required IServiceProvider ServiceProvider { get; init; } /// /// The app model resource the callback is associated with. /// + [AspireExport] public required IResource Model { get; init; } /// /// The path to the server authentication certificate file inside the container. /// [Experimental("ASPIRECERTIFICATES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] + [AspireExportIgnore(Reason = "HttpsCertificateContext is an experimental certificate-specific type that is not yet part of the ATS surface.")] public ContainerFileSystemCallbackHttpsCertificateContext? HttpsCertificateContext { get; set; } } +// The CreateFile/CreateCertificateFile/CreateDirectory shims below exist ONLY so that polyglot app hosts can +// construct ContainerFileSystemItem entries to return from the callback. In C# the callback creates the +// concrete entry types (ContainerFile/ContainerOpenSSLCertificateFile/ContainerDirectory) directly via object +// initializers, so there is no reason to surface these as public C# API. They are therefore kept as internal +// static extension methods on the (exported) context — the same shim pattern used for the builder exports — +// which keeps them out of the public C# surface while still being picked up by the ATS exporter. ATS cannot +// represent the abstract, recursive, polymorphic entry hierarchy as DTOs, so each shim returns the abstract +// base type as an opaque handle, which keeps entries assignable to the directory `entries` parameter and the +// callback result across all guest languages. +internal static class ContainerFileSystemCallbackContextExtensions +{ + /// + /// Creates a file entry to return from the callback. + /// + /// The callback context. + /// The simple file name (no path separators). + /// The inline UTF-8 contents of the file. Mutually exclusive with . + /// An absolute path to a file on the host to copy. Mutually exclusive with . + /// The owner UID, or to inherit. + /// The group GID, or to inherit. + /// The Unix file mode as an integer (for example 0o644), or to inherit. + /// Whether to ignore errors creating this file. + /// The created file entry. + /// Creates a container file entry with inline contents or a host source path. + /// The created file entry. + [AspireExport] + internal static ContainerFileSystemItem CreateFile(this ContainerFileSystemCallbackContext context, string name, string? contents = null, string? sourcePath = null, int? owner = null, int? group = null, int? mode = null, bool? continueOnError = null) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + + return new ContainerFile + { + Name = name, + Contents = contents, + SourcePath = sourcePath, + Owner = owner, + Group = group, + Mode = ConvertMode(mode), + ContinueOnError = continueOnError, + }; + } + + /// + /// Creates an OpenSSL public certificate file entry to return from the callback. An OpenSSL-compatible + /// subject-hash symlink is created alongside it in the container. + /// + /// The callback context. + /// The simple file name (no path separators). + /// The inline PEM-encoded contents of the certificate. Mutually exclusive with . + /// An absolute path to a PEM file on the host to copy. Mutually exclusive with . + /// The owner UID, or to inherit. + /// The group GID, or to inherit. + /// The Unix file mode as an integer (for example 0o644), or to inherit. + /// Whether to ignore errors creating this file. + /// The created certificate file entry. + /// Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. + /// The created certificate file entry. + [AspireExport] + internal static ContainerFileSystemItem CreateCertificateFile(this ContainerFileSystemCallbackContext context, string name, string? contents = null, string? sourcePath = null, int? owner = null, int? group = null, int? mode = null, bool? continueOnError = null) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + + return new ContainerOpenSSLCertificateFile + { + Name = name, + Contents = contents, + SourcePath = sourcePath, + Owner = owner, + Group = group, + Mode = ConvertMode(mode), + ContinueOnError = continueOnError, + }; + } + + /// + /// Creates a directory entry containing the specified child entries, to return from the callback. + /// + /// The callback context. + /// The simple directory name (no path separators). + /// The child entries (files and/or directories) created via this context. + /// The owner UID, or to inherit. + /// The group GID, or to inherit. + /// The Unix file mode as an integer (for example 0o755), or to inherit. + /// The created directory entry. + /// Creates a container directory entry containing the specified child entries. + /// The created directory entry. + [AspireExport] + internal static ContainerFileSystemItem CreateDirectory(this ContainerFileSystemCallbackContext context, string name, IEnumerable entries, int? owner = null, int? group = null, int? mode = null) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentNullException.ThrowIfNull(entries); + + return new ContainerDirectory + { + Name = name, + // Materialize so the caller-provided (possibly lazily-resolved handle) sequence is captured eagerly. + Entries = entries.ToList(), + Owner = owner, + Group = group, + Mode = ConvertMode(mode), + }; + } + + // Mode is supplied as an integer because ATS has no UnixFileMode type. A value of 0 (the default for + // ContainerFileSystemItem.Mode) means "inherit from the parent directory or defaults". Valid values use + // the low 12 bits (rwx for owner/group/other plus setuid/setgid/sticky), i.e. 0..0o7777. + private static UnixFileMode ConvertMode(int? mode) + { + if (mode is null) + { + return (UnixFileMode)0; + } + + if (mode.Value is < 0 or > 0xFFF) + { + throw new ArgumentOutOfRangeException(nameof(mode), mode.Value, "File mode must be between 0 and 0o7777."); + } + + return (UnixFileMode)mode.Value; + } +} + /// /// Represents the context for server authentication certificate files in a . /// diff --git a/src/Aspire.Hosting/ApplicationModel/HttpsCertificateConfigurationCallbackAnnotaion.cs b/src/Aspire.Hosting/ApplicationModel/HttpsCertificateConfigurationCallbackAnnotaion.cs index 2fb95f352fd..733f22a820a 100644 --- a/src/Aspire.Hosting/ApplicationModel/HttpsCertificateConfigurationCallbackAnnotaion.cs +++ b/src/Aspire.Hosting/ApplicationModel/HttpsCertificateConfigurationCallbackAnnotaion.cs @@ -22,16 +22,19 @@ public sealed class HttpsCertificateConfigurationCallbackAnnotation(Func callback. /// [Experimental("ASPIRECERTIFICATES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +[AspireExport] public sealed class HttpsCertificateConfigurationCallbackAnnotationContext { /// /// Gets the for this session. /// + [AspireExport] public required DistributedApplicationExecutionContext ExecutionContext { get; init; } /// /// Gets the resource to which the annotation is applied. /// + [AspireExport] public required IResource Resource { get; init; } /// @@ -78,25 +81,42 @@ public sealed class HttpsCertificateConfigurationCallbackAnnotationContext /// /// A value provider that will resolve to a path to the certificate file. /// + [AspireExport] public required ReferenceExpression CertificatePath { get; init; } /// /// A value provider that will resolve to a path to the private key for the certificate. /// + [AspireExport] public required ReferenceExpression KeyPath { get; init; } /// /// A value provider that will resolve to a path to a PFX file for the key pair. /// + [AspireExport] public required ReferenceExpression PfxPath { get; init; } /// /// A value provider that will resolve to the password for the private key, if applicable. /// + [AspireExportIgnore(Reason = "Password is typed as IValueProvider, which has no ATS-exported representation and no guaranteed concrete type to expose it as. The certificate paths (exposed as ReferenceExpression) cover the common configuration scenarios.")] public required IValueProvider? Password { get; init; } /// /// Gets the that can be used to cancel the operation. /// + [AspireExport] public required CancellationToken CancellationToken { get; init; } + + /// + /// Gets the editor used to manipulate the command-line arguments in polyglot callbacks. + /// + [AspireExport("HttpsCertificateConfigurationCallbackAnnotationContext.arguments", MethodName = "arguments")] + internal CommandLineArgsEditor ArgumentsEditor => new(Arguments); + + /// + /// Gets the editor used to set environment variables in polyglot callbacks. + /// + [AspireExport] + internal EnvironmentEditor Environment => new(EnvironmentVariables); } diff --git a/src/Aspire.Hosting/ApplicationModel/HttpsEndpointUpdateCallbackContext.cs b/src/Aspire.Hosting/ApplicationModel/HttpsEndpointUpdateCallbackContext.cs index e38facccc13..4a174df9b1e 100644 --- a/src/Aspire.Hosting/ApplicationModel/HttpsEndpointUpdateCallbackContext.cs +++ b/src/Aspire.Hosting/ApplicationModel/HttpsEndpointUpdateCallbackContext.cs @@ -10,6 +10,7 @@ namespace Aspire.Hosting.ApplicationModel; /// when an HTTPS certificate is determined to be available for the resource. /// [Experimental("ASPIRECERTIFICATES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +[AspireExport(ExposeProperties = true)] public sealed class HttpsEndpointUpdateCallbackContext { /// diff --git a/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationContext.cs b/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationContext.cs index 7896004d79a..c695c2ae858 100644 --- a/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationContext.cs +++ b/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationContext.cs @@ -12,6 +12,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The service provider for accessing application services. /// A cancellation token that can be used to cancel the validation. [Experimental("ASPIRECOMMAND001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +[AspireExport(ExposeProperties = true, ExposeMethods = true)] public sealed class RequiredCommandValidationContext(string resolvedPath, IServiceProvider services, CancellationToken cancellationToken) { /// @@ -28,4 +29,17 @@ public sealed class RequiredCommandValidationContext(string resolvedPath, IServi /// Gets a cancellation token that can be used to cancel the validation. /// public CancellationToken CancellationToken { get; } = cancellationToken; + + /// + /// Creates a successful validation result. + /// + /// A indicating the command is valid. + public RequiredCommandValidationResult Success() => RequiredCommandValidationResult.Success(); + + /// + /// Creates a failed validation result with the specified message. + /// + /// A message describing why validation failed. + /// A indicating the command is invalid. + public RequiredCommandValidationResult Failure(string validationMessage) => RequiredCommandValidationResult.Failure(validationMessage); } diff --git a/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationResult.cs b/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationResult.cs index 4943dd8f7cd..75135946b7c 100644 --- a/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationResult.cs +++ b/src/Aspire.Hosting/ApplicationModel/RequiredCommandValidationResult.cs @@ -9,6 +9,7 @@ namespace Aspire.Hosting.ApplicationModel; /// Represents the result of validating a required command. /// [Experimental("ASPIRECOMMAND001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +[AspireExport(ExposeProperties = true)] public sealed class RequiredCommandValidationResult { private RequiredCommandValidationResult(bool isValid, string? validationMessage) diff --git a/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs index 3560a24daff..a882a33ba3f 100644 --- a/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs +++ b/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs @@ -374,7 +374,7 @@ public sealed class UpdateCommandStateContext /// /// The service provider. /// - [AspireExportIgnore(Reason = "IServiceProvider is not usable from polyglot command state callbacks.")] + [AspireExport(MethodName = "services")] public required IServiceProvider ServiceProvider { get; init; } } @@ -432,7 +432,7 @@ public sealed class ExecuteCommandContext /// /// The service provider. /// - [AspireExportIgnore(Reason = "IServiceProvider is not usable from polyglot command callbacks.")] + [AspireExport(MethodName = "services")] public required IServiceProvider ServiceProvider { get; init; } /// diff --git a/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs b/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs index 8533253d008..ec7b095028d 100644 --- a/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs +++ b/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs @@ -491,9 +491,8 @@ internal static async ValueTask ProcessCon /// The resource builder. /// A callback to configure container build options. /// A reference to the . - /// This method is not available in polyglot app hosts. [Experimental("ASPIREPIPELINES003", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] - [AspireExportIgnore(Reason = "ContainerBuildOptionsCallbackContext exposes IResource and IServiceProvider — .NET runtime types not usable from polyglot hosts.")] + [AspireExportIgnore(Reason = "Polyglot app hosts use the async callback overload.")] public static IResourceBuilder WithContainerBuildOptions( this IResourceBuilder builder, Action callback) @@ -512,9 +511,8 @@ public static IResourceBuilder WithContainerBuildOptions( /// The resource builder. /// An async callback to configure container build options. /// A reference to the . - /// This method is not available in polyglot app hosts. [Experimental("ASPIREPIPELINES003", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] - [AspireExportIgnore(Reason = "ContainerBuildOptionsCallbackContext exposes IResource and IServiceProvider — .NET runtime types not usable from polyglot hosts.")] + [AspireExport] public static IResourceBuilder WithContainerBuildOptions( this IResourceBuilder builder, Func callback) diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs index c41ebc3878e..f62f2753b48 100644 --- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs @@ -1457,8 +1457,8 @@ public static IResourceBuilder WithContainerFiles(this IResourceBuilder /// /// /// - /// This method is not available in polyglot app hosts. - [AspireExportIgnore(Reason = "ContainerFileSystemCallbackContext exposes IServiceProvider and IResource — .NET runtime types not usable from polyglot hosts.")] + /// This overload is exposed to polyglot app hosts via the withContainerFilesCallback shim, which constructs entries through factory methods on . + [AspireExportIgnore(Reason = "Exposed to ATS via the WithContainerFilesCallbackExport shim, which accepts integer file-mode options and lets polyglot callbacks build the IEnumerable result through ContainerFileSystemCallbackContext factory methods (createFile/createDirectory/createCertificateFile).")] public static IResourceBuilder WithContainerFiles(this IResourceBuilder builder, string destinationPath, Func>> callback, int? defaultOwner = null, int? defaultGroup = null, UnixFileMode? umask = null) where T : ContainerResource { ArgumentNullException.ThrowIfNull(builder); @@ -1490,8 +1490,8 @@ public static IResourceBuilder WithContainerFiles(this IResourceBuilder /// The default group ID for the created or updated file system. Defaults to 0 for root if not set. /// The umask permissions to exclude from the default file and folder permissions. This takes away (rather than granting) default permissions to files and folders without an explicit mode permission set. /// The . - /// This method is not available in polyglot app hosts. - [AspireExportIgnore(Reason = "Uses UnixFileMode parameter which is not ATS-compatible.")] + /// This overload is exposed to polyglot app hosts via the withContainerFiles shim, which accepts integer file-mode options. + [AspireExportIgnore(Reason = "Exposed to ATS via the WithContainerFilesExport shim overload, which accepts integer file-mode options (ContainerFilesOptions) in place of the UnixFileMode parameter.")] public static IResourceBuilder WithContainerFiles(this IResourceBuilder builder, string destinationPath, string sourcePath, int? defaultOwner = null, int? defaultGroup = null, UnixFileMode? umask = null) where T : ContainerResource { ArgumentNullException.ThrowIfNull(builder); @@ -1542,8 +1542,10 @@ public static IResourceBuilder WithContainerFiles(this IResourceBuilder /// In publish mode, Aspire creates a read-only bind mount and ignores those options. /// /// - /// Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - /// polymorphic hierarchy or callbacks that use .NET services. + /// To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + /// use the withContainerFilesCallback overload and build the entries via the factory methods on + /// . Passing a pre-built collection + /// remains .NET-only. /// /// /// Creates or updates files and folders in a container by copying them from a source path on the host. @@ -1585,6 +1587,57 @@ internal static IResourceBuilder WithContainerFilesExport( return builder.WithContainerFiles(destinationPath, sourcePath, defaultOwner, defaultGroup, umask); } + /// + /// Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. + /// + /// The type of container resource. + /// The resource builder for the container resource. + /// The destination absolute path in the container. + /// + /// A callback that returns the file system entries to create or update. Use the factory methods on + /// (createFile, createDirectory, createCertificateFile) to build the entries. + /// + /// Options for the created or updated file system entries. + /// The resource builder. + /// Creates or updates files and folders in a container using entries produced by a callback. + /// The resource builder. + [AspireExport("withContainerFilesCallback", MethodName = "withContainerFilesCallback")] + internal static IResourceBuilder WithContainerFilesCallbackExport( + this IResourceBuilder builder, + string destinationPath, + Func>> callback, + ContainerFilesOptions? options = null) + where T : ContainerResource + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(destinationPath); + ArgumentNullException.ThrowIfNull(callback); + + if (options is null) + { + return builder.WithContainerFiles(destinationPath, callback); + } + + var defaultOwner = GetIntegralContainerFilesOption( + options.DefaultOwner, + nameof(ContainerFilesOptions.DefaultOwner), + minValue: 0, + maxValue: int.MaxValue); + var defaultGroup = GetIntegralContainerFilesOption( + options.DefaultGroup, + nameof(ContainerFilesOptions.DefaultGroup), + minValue: 0, + maxValue: int.MaxValue); + var umaskValue = GetIntegralContainerFilesOption( + options.Umask, + nameof(ContainerFilesOptions.Umask), + minValue: 0, + maxValue: 0xFFF); + var umask = umaskValue is { } value ? (UnixFileMode)value : (UnixFileMode?)null; + + return builder.WithContainerFiles(destinationPath, callback, defaultOwner, defaultGroup, umask); + } + private static int? GetIntegralContainerFilesOption(double? value, string paramName, int minValue, int maxValue) { if (value is null) diff --git a/src/Aspire.Hosting/Dcp/Model/Container.cs b/src/Aspire.Hosting/Dcp/Model/Container.cs index 2a762104712..550917af113 100644 --- a/src/Aspire.Hosting/Dcp/Model/Container.cs +++ b/src/Aspire.Hosting/Dcp/Model/Container.cs @@ -365,6 +365,7 @@ public bool Equals(ContainerCreateFileSystem? other) internal static class ContainerFileSystemItemExtensions { + [AspireExportIgnore(Reason = "Internal conversion to the DCP ContainerFileSystemEntry model, which is not part of the ATS surface.")] public static ContainerFileSystemEntry ToContainerFileSystemEntry(this ContainerFileSystemItem item) { var type = item switch diff --git a/src/Aspire.Hosting/IInteractionService.cs b/src/Aspire.Hosting/IInteractionService.cs index 9dc6643c925..f927f409b2b 100644 --- a/src/Aspire.Hosting/IInteractionService.cs +++ b/src/Aspire.Hosting/IInteractionService.cs @@ -614,7 +614,6 @@ public sealed class InputsDialogValidationContext /// /// Gets the service provider for resolving services during validation. /// - [AspireExportIgnore(Reason = "IServiceProvider is not part of the polyglot validation surface.")] public required IServiceProvider Services { get; init; } /// diff --git a/src/Aspire.Hosting/RequiredCommandResourceExtensions.cs b/src/Aspire.Hosting/RequiredCommandResourceExtensions.cs index fc47b6457ce..d2b1528f00a 100644 --- a/src/Aspire.Hosting/RequiredCommandResourceExtensions.cs +++ b/src/Aspire.Hosting/RequiredCommandResourceExtensions.cs @@ -55,13 +55,13 @@ public static IResourceBuilder WithRequiredCommand( /// An optional help link URL to guide users when the command is missing or fails validation. /// The resource builder. /// - /// This method is not available in polyglot app hosts. Use the overload without validation callback instead. /// The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. - /// The callback should return a indicating whether the command is valid. + /// The callback should return a indicating whether the command is valid, + /// which can be created via or . /// If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. /// [Experimental("ASPIRECOMMAND001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] - [AspireExportIgnore(Reason = "RequiredCommandValidationContext exposes IServiceProvider — not usable from polyglot hosts.")] + [AspireExport("withRequiredCommandValidation", MethodName = "withRequiredCommandValidation")] public static IResourceBuilder WithRequiredCommand( this IResourceBuilder builder, string command, diff --git a/src/Aspire.Hosting/ResourceBuilderExtensions.cs b/src/Aspire.Hosting/ResourceBuilderExtensions.cs index 7229ec6ca29..73f3c7139be 100644 --- a/src/Aspire.Hosting/ResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ResourceBuilderExtensions.cs @@ -4273,10 +4273,9 @@ public static IResourceBuilder WithoutHttpsCertificate(thi /// }); /// /// - /// This method is not available in polyglot app hosts. /// [Experimental("ASPIRECERTIFICATES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] - [AspireExportIgnore(Reason = "HttpsCertificateConfigurationCallbackAnnotationContext exposes IServiceProvider and IResource — not usable from polyglot hosts.")] + [AspireExport] public static IResourceBuilder WithHttpsCertificateConfiguration(this IResourceBuilder builder, Func callback) where TResource : IResourceWithEnvironment, IResourceWithArgs { @@ -4314,10 +4313,9 @@ public static IResourceBuilder WithHttpsCertificateConfiguration /// - /// This method is not available in polyglot app hosts. /// [Experimental("ASPIRECERTIFICATES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] - [AspireExportIgnore(Reason = "HttpsEndpointUpdateCallbackContext exposes IServiceProvider and IResource — not usable from polyglot hosts.")] + [AspireExport] public static IResourceBuilder SubscribeHttpsEndpointsUpdate(this IResourceBuilder builder, Action callback) where TResource : IResource { diff --git a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go index 02263595088..26fbd593a94 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go +++ b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go @@ -132,6 +132,35 @@ const ( ProbeTypeLiveness ProbeType = "Liveness" ) +// ContainerImageDestination represents ContainerImageDestination. +type ContainerImageDestination string + +const ( + ContainerImageDestinationRegistry ContainerImageDestination = "Registry" + ContainerImageDestinationArchive ContainerImageDestination = "Archive" +) + +// ContainerImageFormat represents ContainerImageFormat. +type ContainerImageFormat string + +const ( + ContainerImageFormatDocker ContainerImageFormat = "Docker" + ContainerImageFormatOci ContainerImageFormat = "Oci" +) + +// ContainerTargetPlatform represents ContainerTargetPlatform. +type ContainerTargetPlatform string + +const ( + ContainerTargetPlatformLinuxAmd64 ContainerTargetPlatform = "LinuxAmd64" + ContainerTargetPlatformLinuxArm64 ContainerTargetPlatform = "LinuxArm64" + ContainerTargetPlatformAllLinux ContainerTargetPlatform = "AllLinux" + ContainerTargetPlatformLinuxArm ContainerTargetPlatform = "LinuxArm" + ContainerTargetPlatformLinux386 ContainerTargetPlatform = "Linux386" + ContainerTargetPlatformWindowsAmd64 ContainerTargetPlatform = "WindowsAmd64" + ContainerTargetPlatformWindowsArm64 ContainerTargetPlatform = "WindowsArm64" +) + // EndpointProperty represents EndpointProperty. type EndpointProperty string @@ -1136,6 +1165,7 @@ type Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource interface { OnResourceStopped(callback func(arg ResourceStoppedEvent)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource PublishAsConnectionString() Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource PublishAsContainer() Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource TestWaitFor(dependency Resource) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WaitFor(dependency Resource, options ...*WaitForOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource @@ -1152,8 +1182,10 @@ type Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithConfig(config *TestConfigDto) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithContainerFiles(destinationPath string, sourcePath string, options ...*WithContainerFilesOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource + WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithContainerName(name string) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithContainerNetworkAlias(alias string) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithContainerRegistry(registry Resource) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource @@ -1184,6 +1216,7 @@ type Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource @@ -1223,6 +1256,7 @@ type Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource interface { WithRemoteImageName(remoteImageName string) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithRemoteImageTag(remoteImageTag string) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithSessionLifetime() Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithStatus(status TestResourceStatus) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource WithUnionDependency(dependency any) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource @@ -1450,6 +1484,25 @@ func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) PublishAsConta return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) TestWaitFor(dependency Resource) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { if s.err != nil { return s } @@ -1723,6 +1776,25 @@ func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithConfig(con return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerCertificatePaths adds container certificate path overrides used for certificate trust at run time. func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { if s.err != nil { return s } @@ -1761,6 +1833,32 @@ func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerF return s } +// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["destinationPath"] = serializeValue(destinationPath) + if callback != nil { + cb := callback + shim := func(args ...any) any { + return cb(callbackArg[ContainerFileSystemCallbackContext](args, 0), callbackArg[*CancellationToken](args, 1)) + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithContainerFilesCallbackOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerFilesCallback", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerName overrides the default container name for this resource. By default Aspire generates a unique container name based on the resource name and a random postfix (or a postfix based on a hash of the AppHost project path for persistent container resources). This method allows you to override that behavior with a custom name, but could lead to naming conflicts if the specified name is not unique. func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerName(name string) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { if s.err != nil { return s } @@ -2245,6 +2343,25 @@ func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithHttpProbe( return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { if s.err != nil { return s } @@ -2881,6 +2998,32 @@ func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithRequiredCo return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithSessionLifetime() Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { if s.err != nil { return s } @@ -3234,6 +3377,7 @@ type CSharpAppResource interface { OnResourceStopped(callback func(arg ResourceStoppedEvent)) CSharpAppResource PublishAsDockerFile(options ...*PublishAsDockerFileOptions) CSharpAppResource PublishWithContainerFiles(source ResourceWithContainerFiles, destinationPath string) CSharpAppResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) CSharpAppResource TestWaitFor(dependency Resource) CSharpAppResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) CSharpAppResource WaitFor(dependency Resource, options ...*WaitForOptions) CSharpAppResource @@ -3247,6 +3391,7 @@ type CSharpAppResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) CSharpAppResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) CSharpAppResource WithConfig(config *TestConfigDto) CSharpAppResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) CSharpAppResource WithContainerRegistry(registry Resource) CSharpAppResource WithCorrelationId(correlationId string) CSharpAppResource WithCreatedAt(createdAt string) CSharpAppResource @@ -3271,6 +3416,7 @@ type CSharpAppResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) CSharpAppResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) CSharpAppResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) CSharpAppResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) CSharpAppResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) CSharpAppResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) CSharpAppResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) CSharpAppResource @@ -3305,6 +3451,7 @@ type CSharpAppResource interface { WithRemoteImageTag(remoteImageTag string) CSharpAppResource WithReplicas(replicas float64) CSharpAppResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) CSharpAppResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) CSharpAppResource WithSessionLifetime() CSharpAppResource WithStatus(status TestResourceStatus) CSharpAppResource WithUnionDependency(dependency any) CSharpAppResource @@ -3559,6 +3706,25 @@ func (s *cSharpAppResource) PublishWithContainerFiles(source ResourceWithContain return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *cSharpAppResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) CSharpAppResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *cSharpAppResource) TestWaitFor(dependency Resource) CSharpAppResource { if s.err != nil { return s } @@ -3778,6 +3944,25 @@ func (s *cSharpAppResource) WithConfig(config *TestConfigDto) CSharpAppResource return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *cSharpAppResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) CSharpAppResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *cSharpAppResource) WithContainerRegistry(registry Resource) CSharpAppResource { if s.err != nil { return s } @@ -4154,6 +4339,25 @@ func (s *cSharpAppResource) WithHttpProbe(probeType ProbeType, options ...*WithH return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *cSharpAppResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) CSharpAppResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *cSharpAppResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) CSharpAppResource { if s.err != nil { return s } @@ -4723,6 +4927,32 @@ func (s *cSharpAppResource) WithRequiredCommand(command string, options ...*With return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *cSharpAppResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) CSharpAppResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *cSharpAppResource) WithSessionLifetime() CSharpAppResource { if s.err != nil { return s } @@ -5227,124 +5457,563 @@ func (s *connectionStringAvailableEvent) Services() ServiceProvider { return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} } -// ContainerImagePushOptions is the public interface for handle type ContainerImagePushOptions. -type ContainerImagePushOptions interface { +// ContainerBuildOptionsCallbackContext is the public interface for handle type ContainerBuildOptionsCallbackContext. +type ContainerBuildOptionsCallbackContext interface { handleReference - RemoteImageName() (string, error) - RemoteImageTag() (string, error) - SetRemoteImageName(value string) ContainerImagePushOptions - SetRemoteImageTag(value string) ContainerImagePushOptions + CancellationToken() (*CancellationToken, error) + Destination() (ContainerImageDestination, error) + ExecutionContext() DistributedApplicationExecutionContext + ImageFormat() (ContainerImageFormat, error) + LocalImageName() (string, error) + LocalImageTag() (string, error) + Logger() Logger + OutputPath() (string, error) + Resource() Resource + Services() ServiceProvider + SetDestination(value ContainerImageDestination) ContainerBuildOptionsCallbackContext + SetImageFormat(value ContainerImageFormat) ContainerBuildOptionsCallbackContext + SetLocalImageName(value string) ContainerBuildOptionsCallbackContext + SetLocalImageTag(value string) ContainerBuildOptionsCallbackContext + SetOutputPath(value string) ContainerBuildOptionsCallbackContext + SetTargetPlatform(value ContainerTargetPlatform) ContainerBuildOptionsCallbackContext + TargetPlatform() (ContainerTargetPlatform, error) Err() error } -// containerImagePushOptions is the unexported impl of ContainerImagePushOptions. -type containerImagePushOptions struct { +// containerBuildOptionsCallbackContext is the unexported impl of ContainerBuildOptionsCallbackContext. +type containerBuildOptionsCallbackContext struct { *resourceBuilderBase } -// newContainerImagePushOptionsFromHandle wraps an existing handle as ContainerImagePushOptions. -func newContainerImagePushOptionsFromHandle(h *handle, c *client) ContainerImagePushOptions { - return &containerImagePushOptions{resourceBuilderBase: newResourceBuilderBase(h, c)} +// newContainerBuildOptionsCallbackContextFromHandle wraps an existing handle as ContainerBuildOptionsCallbackContext. +func newContainerBuildOptionsCallbackContextFromHandle(h *handle, c *client) ContainerBuildOptionsCallbackContext { + return &containerBuildOptionsCallbackContext{resourceBuilderBase: newResourceBuilderBase(h, c)} } -// RemoteImageName gets or sets the remote image name (repository path without registry endpoint or tag). -func (s *containerImagePushOptions) RemoteImageName() (string, error) { - if s.err != nil { var zero string; return zero, s.err } +// CancellationToken gets the cancellation token. +func (s *containerBuildOptionsCallbackContext) CancellationToken() (*CancellationToken, error) { + if s.err != nil { var zero *CancellationToken; return zero, s.err } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.remoteImageName", reqArgs) + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.cancellationToken", reqArgs) if err != nil { - var zero string + var zero *CancellationToken return zero, err } - return decodeAs[string](result) + return decodeAs[*CancellationToken](result) } -// RemoteImageTag gets or sets the remote image tag. -func (s *containerImagePushOptions) RemoteImageTag() (string, error) { - if s.err != nil { var zero string; return zero, s.err } +// Destination gets or sets the destination for the container image. +func (s *containerBuildOptionsCallbackContext) Destination() (ContainerImageDestination, error) { + if s.err != nil { var zero ContainerImageDestination; return zero, s.err } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.remoteImageTag", reqArgs) + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.destination", reqArgs) if err != nil { - var zero string + var zero ContainerImageDestination return zero, err } - return decodeAs[string](result) + return decodeAs[ContainerImageDestination](result) } -// SetRemoteImageName sets the RemoteImageName property -func (s *containerImagePushOptions) SetRemoteImageName(value string) ContainerImagePushOptions { - if s.err != nil { return s } +// ExecutionContext gets the distributed application execution context. +func (s *containerBuildOptionsCallbackContext) ExecutionContext() DistributedApplicationExecutionContext { + if s.err != nil { return &distributedApplicationExecutionContext{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - reqArgs["value"] = serializeValue(value) - if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.setRemoteImageName", reqArgs); err != nil { s.setErr(err) } - return s + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.executionContext", reqArgs) + if err != nil { + return &distributedApplicationExecutionContext{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.executionContext returned unexpected type %T", result) + return &distributedApplicationExecutionContext{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &distributedApplicationExecutionContext{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} } -// SetRemoteImageTag sets the RemoteImageTag property -func (s *containerImagePushOptions) SetRemoteImageTag(value string) ContainerImagePushOptions { - if s.err != nil { return s } +// ImageFormat gets or sets the container image format. +func (s *containerBuildOptionsCallbackContext) ImageFormat() (ContainerImageFormat, error) { + if s.err != nil { var zero ContainerImageFormat; return zero, s.err } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - reqArgs["value"] = serializeValue(value) - if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.setRemoteImageTag", reqArgs); err != nil { s.setErr(err) } - return s -} - -// ContainerImagePushOptionsCallbackContext is the public interface for handle type ContainerImagePushOptionsCallbackContext. -type ContainerImagePushOptionsCallbackContext interface { - handleReference - CancellationToken() (*CancellationToken, error) - Options() ContainerImagePushOptions - Resource() Resource - Err() error -} - -// containerImagePushOptionsCallbackContext is the unexported impl of ContainerImagePushOptionsCallbackContext. -type containerImagePushOptionsCallbackContext struct { - *resourceBuilderBase + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.imageFormat", reqArgs) + if err != nil { + var zero ContainerImageFormat + return zero, err + } + return decodeAs[ContainerImageFormat](result) } -// newContainerImagePushOptionsCallbackContextFromHandle wraps an existing handle as ContainerImagePushOptionsCallbackContext. -func newContainerImagePushOptionsCallbackContextFromHandle(h *handle, c *client) ContainerImagePushOptionsCallbackContext { - return &containerImagePushOptionsCallbackContext{resourceBuilderBase: newResourceBuilderBase(h, c)} +// LocalImageName gets or sets the local image name for the built container. +func (s *containerBuildOptionsCallbackContext) LocalImageName() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageName", reqArgs) + if err != nil { + var zero string + return zero, err + } + return decodeAs[string](result) } -// CancellationToken gets the cancellation token to observe while configuring image push options. -func (s *containerImagePushOptionsCallbackContext) CancellationToken() (*CancellationToken, error) { - if s.err != nil { var zero *CancellationToken; return zero, s.err } +// LocalImageTag gets or sets the local image tag for the built container. +func (s *containerBuildOptionsCallbackContext) LocalImageTag() (string, error) { + if s.err != nil { var zero string; return zero, s.err } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptionsCallbackContext.cancellationToken", reqArgs) + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageTag", reqArgs) if err != nil { - var zero *CancellationToken + var zero string return zero, err } - return decodeAs[*CancellationToken](result) + return decodeAs[string](result) } -// Options gets the container image push options that can be modified by the callback. -func (s *containerImagePushOptionsCallbackContext) Options() ContainerImagePushOptions { - if s.err != nil { return &containerImagePushOptions{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } +// Logger gets the logger instance. +func (s *containerBuildOptionsCallbackContext) Logger() Logger { + if s.err != nil { return &logger{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptionsCallbackContext.options", reqArgs) + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.logger", reqArgs) if err != nil { - return &containerImagePushOptions{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + return &logger{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.logger returned unexpected type %T", result) + return &logger{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &logger{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// OutputPath gets or sets the output path for the container archive. +func (s *containerBuildOptionsCallbackContext) OutputPath() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.outputPath", reqArgs) + if err != nil { + var zero string + return zero, err + } + return decodeAs[string](result) +} + +// Resource gets the resource being built. +func (s *containerBuildOptionsCallbackContext) Resource() Resource { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.resource", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(Resource) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.resource returned unexpected type %T", result)) + return nil + } + return typed +} + +// Services gets the service provider. +func (s *containerBuildOptionsCallbackContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// SetDestination sets the Destination property +func (s *containerBuildOptionsCallbackContext) SetDestination(value ContainerImageDestination) ContainerBuildOptionsCallbackContext { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setDestination", reqArgs); err != nil { s.setErr(err) } + return s +} + +// SetImageFormat sets the ImageFormat property +func (s *containerBuildOptionsCallbackContext) SetImageFormat(value ContainerImageFormat) ContainerBuildOptionsCallbackContext { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setImageFormat", reqArgs); err != nil { s.setErr(err) } + return s +} + +// SetLocalImageName sets the LocalImageName property +func (s *containerBuildOptionsCallbackContext) SetLocalImageName(value string) ContainerBuildOptionsCallbackContext { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageName", reqArgs); err != nil { s.setErr(err) } + return s +} + +// SetLocalImageTag sets the LocalImageTag property +func (s *containerBuildOptionsCallbackContext) SetLocalImageTag(value string) ContainerBuildOptionsCallbackContext { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageTag", reqArgs); err != nil { s.setErr(err) } + return s +} + +// SetOutputPath sets the OutputPath property +func (s *containerBuildOptionsCallbackContext) SetOutputPath(value string) ContainerBuildOptionsCallbackContext { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setOutputPath", reqArgs); err != nil { s.setErr(err) } + return s +} + +// SetTargetPlatform sets the TargetPlatform property +func (s *containerBuildOptionsCallbackContext) SetTargetPlatform(value ContainerTargetPlatform) ContainerBuildOptionsCallbackContext { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setTargetPlatform", reqArgs); err != nil { s.setErr(err) } + return s +} + +// TargetPlatform gets or sets the target platform for the container. +func (s *containerBuildOptionsCallbackContext) TargetPlatform() (ContainerTargetPlatform, error) { + if s.err != nil { var zero ContainerTargetPlatform; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.targetPlatform", reqArgs) + if err != nil { + var zero ContainerTargetPlatform + return zero, err + } + return decodeAs[ContainerTargetPlatform](result) +} + +// ContainerFileSystemCallbackContext is the public interface for handle type ContainerFileSystemCallbackContext. +type ContainerFileSystemCallbackContext interface { + handleReference + CreateCertificateFile(name string, options ...*CreateCertificateFileOptions) ContainerFileSystemItem + CreateDirectory(name string, entries []ContainerFileSystemItem, options ...*CreateDirectoryOptions) ContainerFileSystemItem + CreateFile(name string, options ...*CreateFileOptions) ContainerFileSystemItem + Model() Resource + Services() ServiceProvider + Err() error +} + +// containerFileSystemCallbackContext is the unexported impl of ContainerFileSystemCallbackContext. +type containerFileSystemCallbackContext struct { + *resourceBuilderBase +} + +// newContainerFileSystemCallbackContextFromHandle wraps an existing handle as ContainerFileSystemCallbackContext. +func newContainerFileSystemCallbackContextFromHandle(h *handle, c *client) ContainerFileSystemCallbackContext { + return &containerFileSystemCallbackContext{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// CreateCertificateFile creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. +func (s *containerFileSystemCallbackContext) CreateCertificateFile(name string, options ...*CreateCertificateFileOptions) ContainerFileSystemItem { + if s.err != nil { return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["name"] = serializeValue(name) + if len(options) > 0 { + merged := &CreateCertificateFileOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting/createCertificateFile", reqArgs) + if err != nil { + return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting/createCertificateFile returned unexpected type %T", result) + return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &containerFileSystemItem{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// CreateDirectory creates a container directory entry containing the specified child entries. +func (s *containerFileSystemCallbackContext) CreateDirectory(name string, entries []ContainerFileSystemItem, options ...*CreateDirectoryOptions) ContainerFileSystemItem { + if s.err != nil { return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["name"] = serializeValue(name) + if entries != nil { reqArgs["entries"] = serializeValue(entries) } + if len(options) > 0 { + merged := &CreateDirectoryOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting/createDirectory", reqArgs) + if err != nil { + return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting/createDirectory returned unexpected type %T", result) + return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &containerFileSystemItem{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// CreateFile creates a container file entry with inline contents or a host source path. +func (s *containerFileSystemCallbackContext) CreateFile(name string, options ...*CreateFileOptions) ContainerFileSystemItem { + if s.err != nil { return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["name"] = serializeValue(name) + if len(options) > 0 { + merged := &CreateFileOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting/createFile", reqArgs) + if err != nil { + return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting/createFile returned unexpected type %T", result) + return &containerFileSystemItem{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &containerFileSystemItem{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// Model the app model resource the callback is associated with. +func (s *containerFileSystemCallbackContext) Model() Resource { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.model", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(Resource) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.model returned unexpected type %T", result)) + return nil + } + return typed +} + +// Services a `IServiceProvider` that can be used to resolve services in the callback. +func (s *containerFileSystemCallbackContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// ContainerFileSystemItem is the public interface for handle type ContainerFileSystemItem. +type ContainerFileSystemItem interface { + handleReference + Err() error +} + +// containerFileSystemItem is the unexported impl of ContainerFileSystemItem. +type containerFileSystemItem struct { + *resourceBuilderBase +} + +// newContainerFileSystemItemFromHandle wraps an existing handle as ContainerFileSystemItem. +func newContainerFileSystemItemFromHandle(h *handle, c *client) ContainerFileSystemItem { + return &containerFileSystemItem{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// ContainerImagePushOptions is the public interface for handle type ContainerImagePushOptions. +type ContainerImagePushOptions interface { + handleReference + RemoteImageName() (string, error) + RemoteImageTag() (string, error) + SetRemoteImageName(value string) ContainerImagePushOptions + SetRemoteImageTag(value string) ContainerImagePushOptions + Err() error +} + +// containerImagePushOptions is the unexported impl of ContainerImagePushOptions. +type containerImagePushOptions struct { + *resourceBuilderBase +} + +// newContainerImagePushOptionsFromHandle wraps an existing handle as ContainerImagePushOptions. +func newContainerImagePushOptionsFromHandle(h *handle, c *client) ContainerImagePushOptions { + return &containerImagePushOptions{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// RemoteImageName gets or sets the remote image name (repository path without registry endpoint or tag). +func (s *containerImagePushOptions) RemoteImageName() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.remoteImageName", reqArgs) + if err != nil { + var zero string + return zero, err + } + return decodeAs[string](result) +} + +// RemoteImageTag gets or sets the remote image tag. +func (s *containerImagePushOptions) RemoteImageTag() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.remoteImageTag", reqArgs) + if err != nil { + var zero string + return zero, err + } + return decodeAs[string](result) +} + +// SetRemoteImageName sets the RemoteImageName property +func (s *containerImagePushOptions) SetRemoteImageName(value string) ContainerImagePushOptions { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.setRemoteImageName", reqArgs); err != nil { s.setErr(err) } + return s +} + +// SetRemoteImageTag sets the RemoteImageTag property +func (s *containerImagePushOptions) SetRemoteImageTag(value string) ContainerImagePushOptions { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["value"] = serializeValue(value) + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptions.setRemoteImageTag", reqArgs); err != nil { s.setErr(err) } + return s +} + +// ContainerImagePushOptionsCallbackContext is the public interface for handle type ContainerImagePushOptionsCallbackContext. +type ContainerImagePushOptionsCallbackContext interface { + handleReference + CancellationToken() (*CancellationToken, error) + Options() ContainerImagePushOptions + Resource() Resource + Err() error +} + +// containerImagePushOptionsCallbackContext is the unexported impl of ContainerImagePushOptionsCallbackContext. +type containerImagePushOptionsCallbackContext struct { + *resourceBuilderBase +} + +// newContainerImagePushOptionsCallbackContextFromHandle wraps an existing handle as ContainerImagePushOptionsCallbackContext. +func newContainerImagePushOptionsCallbackContextFromHandle(h *handle, c *client) ContainerImagePushOptionsCallbackContext { + return &containerImagePushOptionsCallbackContext{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// CancellationToken gets the cancellation token to observe while configuring image push options. +func (s *containerImagePushOptionsCallbackContext) CancellationToken() (*CancellationToken, error) { + if s.err != nil { var zero *CancellationToken; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptionsCallbackContext.cancellationToken", reqArgs) + if err != nil { + var zero *CancellationToken + return zero, err + } + return decodeAs[*CancellationToken](result) +} + +// Options gets the container image push options that can be modified by the callback. +func (s *containerImagePushOptionsCallbackContext) Options() ContainerImagePushOptions { + if s.err != nil { return &containerImagePushOptions{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ContainerImagePushOptionsCallbackContext.options", reqArgs) + if err != nil { + return &containerImagePushOptions{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} } href, ok := result.(handleReference) if !ok { @@ -5562,11 +6231,13 @@ type ContainerRegistryResource interface { OnInitializeResource(callback func(arg InitializeResourceEvent)) ContainerRegistryResource OnResourceReady(callback func(arg ResourceReadyEvent)) ContainerRegistryResource OnResourceStopped(callback func(arg ResourceStoppedEvent)) ContainerRegistryResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ContainerRegistryResource TestWaitFor(dependency Resource) ContainerRegistryResource WithCancellableOperation(operation func(arg *CancellationToken)) ContainerRegistryResource WithChildRelationship(child Resource) ContainerRegistryResource WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) ContainerRegistryResource WithConfig(config *TestConfigDto) ContainerRegistryResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ContainerRegistryResource WithContainerRegistry(registry Resource) ContainerRegistryResource WithCorrelationId(correlationId string) ContainerRegistryResource WithCreatedAt(createdAt string) ContainerRegistryResource @@ -5600,6 +6271,7 @@ type ContainerRegistryResource interface { WithProcessCommandFactory(commandName string, displayName string, createProcessSpec func(arg ExecuteCommandContext) *ProcessCommandSpecExportData, options ...*WithProcessCommandFactoryOptions) ContainerRegistryResource WithRelationship(resourceBuilder Resource, type_ string) ContainerRegistryResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) ContainerRegistryResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ContainerRegistryResource WithSessionLifetime() ContainerRegistryResource WithStatus(status TestResourceStatus) ContainerRegistryResource WithUnionDependency(dependency any) ContainerRegistryResource @@ -5724,17 +6396,36 @@ func (s *containerRegistryResource) OnResourceReady(callback func(arg ResourceRe if callback != nil { cb := callback shim := func(args ...any) any { - cb(callbackArg[ResourceReadyEvent](args, 0)) + cb(callbackArg[ResourceReadyEvent](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/onResourceReady", reqArgs); err != nil { s.setErr(err) } + return s +} + +// OnResourceStopped subscribes to the ResourceStopped event. +func (s *containerRegistryResource) OnResourceStopped(callback func(arg ResourceStoppedEvent)) ContainerRegistryResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ResourceStoppedEvent](args, 0)) return nil } reqArgs["callback"] = s.client.registerCallback(shim) } - if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/onResourceReady", reqArgs); err != nil { s.setErr(err) } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/onResourceStopped", reqArgs); err != nil { s.setErr(err) } return s } -// OnResourceStopped subscribes to the ResourceStopped event. -func (s *containerRegistryResource) OnResourceStopped(callback func(arg ResourceStoppedEvent)) ContainerRegistryResource { +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *containerRegistryResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ContainerRegistryResource { if s.err != nil { return s } ctx := context.Background() reqArgs := map[string]any{ @@ -5743,12 +6434,12 @@ func (s *containerRegistryResource) OnResourceStopped(callback func(arg Resource if callback != nil { cb := callback shim := func(args ...any) any { - cb(callbackArg[ResourceStoppedEvent](args, 0)) + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) return nil } reqArgs["callback"] = s.client.registerCallback(shim) } - if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/onResourceStopped", reqArgs); err != nil { s.setErr(err) } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } return s } @@ -5836,6 +6527,25 @@ func (s *containerRegistryResource) WithConfig(config *TestConfigDto) ContainerR return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *containerRegistryResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ContainerRegistryResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *containerRegistryResource) WithContainerRegistry(registry Resource) ContainerRegistryResource { if s.err != nil { return s } @@ -6346,6 +7056,32 @@ func (s *containerRegistryResource) WithRequiredCommand(command string, options return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *containerRegistryResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ContainerRegistryResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *containerRegistryResource) WithSessionLifetime() ContainerRegistryResource { if s.err != nil { return s } @@ -6490,6 +7226,7 @@ type ContainerResource interface { OnResourceStopped(callback func(arg ResourceStoppedEvent)) ContainerResource PublishAsConnectionString() ContainerResource PublishAsContainer() ContainerResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ContainerResource TestWaitFor(dependency Resource) ContainerResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) ContainerResource WaitFor(dependency Resource, options ...*WaitForOptions) ContainerResource @@ -6506,8 +7243,10 @@ type ContainerResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) ContainerResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) ContainerResource WithConfig(config *TestConfigDto) ContainerResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ContainerResource WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) ContainerResource WithContainerFiles(destinationPath string, sourcePath string, options ...*WithContainerFilesOptions) ContainerResource + WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) ContainerResource WithContainerName(name string) ContainerResource WithContainerNetworkAlias(alias string) ContainerResource WithContainerRegistry(registry Resource) ContainerResource @@ -6538,6 +7277,7 @@ type ContainerResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) ContainerResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) ContainerResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) ContainerResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) ContainerResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) ContainerResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) ContainerResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) ContainerResource @@ -6577,6 +7317,7 @@ type ContainerResource interface { WithRemoteImageName(remoteImageName string) ContainerResource WithRemoteImageTag(remoteImageTag string) ContainerResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) ContainerResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ContainerResource WithSessionLifetime() ContainerResource WithStatus(status TestResourceStatus) ContainerResource WithUnionDependency(dependency any) ContainerResource @@ -6803,6 +7544,25 @@ func (s *containerResource) PublishAsContainer() ContainerResource { return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *containerResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ContainerResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *containerResource) TestWaitFor(dependency Resource) ContainerResource { if s.err != nil { return s } @@ -7076,6 +7836,25 @@ func (s *containerResource) WithConfig(config *TestConfigDto) ContainerResource return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *containerResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ContainerResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerCertificatePaths adds container certificate path overrides used for certificate trust at run time. func (s *containerResource) WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) ContainerResource { if s.err != nil { return s } @@ -7114,6 +7893,32 @@ func (s *containerResource) WithContainerFiles(destinationPath string, sourcePat return s } +// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +func (s *containerResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) ContainerResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["destinationPath"] = serializeValue(destinationPath) + if callback != nil { + cb := callback + shim := func(args ...any) any { + return cb(callbackArg[ContainerFileSystemCallbackContext](args, 0), callbackArg[*CancellationToken](args, 1)) + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithContainerFilesCallbackOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerFilesCallback", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerName overrides the default container name for this resource. By default Aspire generates a unique container name based on the resource name and a random postfix (or a postfix based on a hash of the AppHost project path for persistent container resources). This method allows you to override that behavior with a custom name, but could lead to naming conflicts if the specified name is not unique. func (s *containerResource) WithContainerName(name string) ContainerResource { if s.err != nil { return s } @@ -7598,6 +8403,25 @@ func (s *containerResource) WithHttpProbe(probeType ProbeType, options ...*WithH return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *containerResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) ContainerResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *containerResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) ContainerResource { if s.err != nil { return s } @@ -8234,6 +9058,32 @@ func (s *containerResource) WithRequiredCommand(command string, options ...*With return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *containerResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ContainerResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *containerResource) WithSessionLifetime() ContainerResource { if s.err != nil { return s } @@ -9993,6 +10843,7 @@ type DotnetToolResource interface { OnResourceReady(callback func(arg ResourceReadyEvent)) DotnetToolResource OnResourceStopped(callback func(arg ResourceStoppedEvent)) DotnetToolResource PublishAsDockerFile(configure func(obj ContainerResource)) DotnetToolResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) DotnetToolResource TestWaitFor(dependency Resource) DotnetToolResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) DotnetToolResource WaitFor(dependency Resource, options ...*WaitForOptions) DotnetToolResource @@ -10006,6 +10857,7 @@ type DotnetToolResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) DotnetToolResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) DotnetToolResource WithConfig(config *TestConfigDto) DotnetToolResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) DotnetToolResource WithContainerRegistry(registry Resource) DotnetToolResource WithCorrelationId(correlationId string) DotnetToolResource WithCreatedAt(createdAt string) DotnetToolResource @@ -10030,6 +10882,7 @@ type DotnetToolResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) DotnetToolResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) DotnetToolResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) DotnetToolResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) DotnetToolResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) DotnetToolResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) DotnetToolResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) DotnetToolResource @@ -10063,6 +10916,7 @@ type DotnetToolResource interface { WithRemoteImageName(remoteImageName string) DotnetToolResource WithRemoteImageTag(remoteImageTag string) DotnetToolResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) DotnetToolResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) DotnetToolResource WithSessionLifetime() DotnetToolResource WithStatus(status TestResourceStatus) DotnetToolResource WithToolIgnoreExistingFeeds() DotnetToolResource @@ -10292,6 +11146,25 @@ func (s *dotnetToolResource) PublishAsDockerFile(configure func(obj ContainerRes return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *dotnetToolResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) DotnetToolResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *dotnetToolResource) TestWaitFor(dependency Resource) DotnetToolResource { if s.err != nil { return s } @@ -10511,6 +11384,25 @@ func (s *dotnetToolResource) WithConfig(config *TestConfigDto) DotnetToolResourc return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *dotnetToolResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) DotnetToolResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *dotnetToolResource) WithContainerRegistry(registry Resource) DotnetToolResource { if s.err != nil { return s } @@ -10887,6 +11779,25 @@ func (s *dotnetToolResource) WithHttpProbe(probeType ProbeType, options ...*With return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *dotnetToolResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) DotnetToolResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *dotnetToolResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) DotnetToolResource { if s.err != nil { return s } @@ -11444,6 +12355,32 @@ func (s *dotnetToolResource) WithRequiredCommand(command string, options ...*Wit return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *dotnetToolResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) DotnetToolResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *dotnetToolResource) WithSessionLifetime() DotnetToolResource { if s.err != nil { return s } @@ -12694,6 +13631,7 @@ type ExecutableResource interface { OnResourceReady(callback func(arg ResourceReadyEvent)) ExecutableResource OnResourceStopped(callback func(arg ResourceStoppedEvent)) ExecutableResource PublishAsDockerFile(configure func(obj ContainerResource)) ExecutableResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ExecutableResource TestWaitFor(dependency Resource) ExecutableResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) ExecutableResource WaitFor(dependency Resource, options ...*WaitForOptions) ExecutableResource @@ -12707,6 +13645,7 @@ type ExecutableResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) ExecutableResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) ExecutableResource WithConfig(config *TestConfigDto) ExecutableResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ExecutableResource WithContainerRegistry(registry Resource) ExecutableResource WithCorrelationId(correlationId string) ExecutableResource WithCreatedAt(createdAt string) ExecutableResource @@ -12731,6 +13670,7 @@ type ExecutableResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) ExecutableResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) ExecutableResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) ExecutableResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) ExecutableResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) ExecutableResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) ExecutableResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) ExecutableResource @@ -12764,6 +13704,7 @@ type ExecutableResource interface { WithRemoteImageName(remoteImageName string) ExecutableResource WithRemoteImageTag(remoteImageTag string) ExecutableResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) ExecutableResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ExecutableResource WithSessionLifetime() ExecutableResource WithStatus(status TestResourceStatus) ExecutableResource WithUnionDependency(dependency any) ExecutableResource @@ -12987,6 +13928,25 @@ func (s *executableResource) PublishAsDockerFile(configure func(obj ContainerRes return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *executableResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ExecutableResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *executableResource) TestWaitFor(dependency Resource) ExecutableResource { if s.err != nil { return s } @@ -13206,6 +14166,25 @@ func (s *executableResource) WithConfig(config *TestConfigDto) ExecutableResourc return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *executableResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ExecutableResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *executableResource) WithContainerRegistry(registry Resource) ExecutableResource { if s.err != nil { return s } @@ -13582,6 +14561,25 @@ func (s *executableResource) WithHttpProbe(probeType ProbeType, options ...*With return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *executableResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) ExecutableResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *executableResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) ExecutableResource { if s.err != nil { return s } @@ -14129,13 +15127,39 @@ func (s *executableResource) WithRequiredCommand(command string, options ...*Wit } reqArgs["command"] = serializeValue(command) if len(options) > 0 { - merged := &WithRequiredCommandOptions{} + merged := &WithRequiredCommandOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommand", reqArgs); err != nil { s.setErr(err) } + return s +} + +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *executableResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ExecutableResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} for _, opt := range options { if opt != nil { merged = deepUpdate(merged, opt) } } for k, v := range merged.ToMap() { reqArgs[k] = v } } - if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommand", reqArgs); err != nil { s.setErr(err) } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } return s } @@ -14297,6 +15321,7 @@ type ExecuteCommandContext interface { CancellationToken() (*CancellationToken, error) Logger() Logger ResourceName() (string, error) + Services() ServiceProvider Err() error } @@ -14378,6 +15403,25 @@ func (s *executeCommandContext) ResourceName() (string, error) { return decodeAs[string](result) } +// Services the service provider. +func (s *executeCommandContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + // ExecutionConfigurationBuilder is the public interface for handle type ExecutionConfigurationBuilder. type ExecutionConfigurationBuilder interface { handleReference @@ -14550,11 +15594,13 @@ type ExternalServiceResource interface { OnInitializeResource(callback func(arg InitializeResourceEvent)) ExternalServiceResource OnResourceReady(callback func(arg ResourceReadyEvent)) ExternalServiceResource OnResourceStopped(callback func(arg ResourceStoppedEvent)) ExternalServiceResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ExternalServiceResource TestWaitFor(dependency Resource) ExternalServiceResource WithCancellableOperation(operation func(arg *CancellationToken)) ExternalServiceResource WithChildRelationship(child Resource) ExternalServiceResource WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) ExternalServiceResource WithConfig(config *TestConfigDto) ExternalServiceResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ExternalServiceResource WithContainerRegistry(registry Resource) ExternalServiceResource WithCorrelationId(correlationId string) ExternalServiceResource WithCreatedAt(createdAt string) ExternalServiceResource @@ -14589,6 +15635,7 @@ type ExternalServiceResource interface { WithProcessCommandFactory(commandName string, displayName string, createProcessSpec func(arg ExecuteCommandContext) *ProcessCommandSpecExportData, options ...*WithProcessCommandFactoryOptions) ExternalServiceResource WithRelationship(resourceBuilder Resource, type_ string) ExternalServiceResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) ExternalServiceResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ExternalServiceResource WithSessionLifetime() ExternalServiceResource WithStatus(status TestResourceStatus) ExternalServiceResource WithUnionDependency(dependency any) ExternalServiceResource @@ -14741,6 +15788,25 @@ func (s *externalServiceResource) OnResourceStopped(callback func(arg ResourceSt return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *externalServiceResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ExternalServiceResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *externalServiceResource) TestWaitFor(dependency Resource) ExternalServiceResource { if s.err != nil { return s } @@ -14825,6 +15891,25 @@ func (s *externalServiceResource) WithConfig(config *TestConfigDto) ExternalServ return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *externalServiceResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ExternalServiceResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *externalServiceResource) WithContainerRegistry(registry Resource) ExternalServiceResource { if s.err != nil { return s } @@ -15353,6 +16438,32 @@ func (s *externalServiceResource) WithRequiredCommand(command string, options .. return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *externalServiceResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ExternalServiceResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *externalServiceResource) WithSessionLifetime() ExternalServiceResource { if s.err != nil { return s } @@ -15722,19 +16833,273 @@ func (s *httpCommandPrepareRequestContext) Endpoint() EndpointReference { return &endpointReference{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} } -// ResourceName the name of the resource the command was configured on. -func (s *httpCommandPrepareRequestContext) ResourceName() (string, error) { - if s.err != nil { var zero string; return zero, s.err } +// ResourceName the name of the resource the command was configured on. +func (s *httpCommandPrepareRequestContext) ResourceName() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpCommandPrepareRequestContext.resourceName", reqArgs) + if err != nil { + var zero string + return zero, err + } + return decodeAs[string](result) +} + +// HttpsCertificateConfigurationCallbackAnnotationContext is the public interface for handle type HttpsCertificateConfigurationCallbackAnnotationContext. +type HttpsCertificateConfigurationCallbackAnnotationContext interface { + handleReference + Arguments() CommandLineArgsEditor + CancellationToken() (*CancellationToken, error) + CertificatePath() *ReferenceExpression + Environment() EnvironmentEditor + ExecutionContext() DistributedApplicationExecutionContext + KeyPath() *ReferenceExpression + PfxPath() *ReferenceExpression + Resource() Resource + Err() error +} + +// httpsCertificateConfigurationCallbackAnnotationContext is the unexported impl of HttpsCertificateConfigurationCallbackAnnotationContext. +type httpsCertificateConfigurationCallbackAnnotationContext struct { + *resourceBuilderBase +} + +// newHttpsCertificateConfigurationCallbackAnnotationContextFromHandle wraps an existing handle as HttpsCertificateConfigurationCallbackAnnotationContext. +func newHttpsCertificateConfigurationCallbackAnnotationContextFromHandle(h *handle, c *client) HttpsCertificateConfigurationCallbackAnnotationContext { + return &httpsCertificateConfigurationCallbackAnnotationContext{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// Arguments gets the editor used to manipulate the command-line arguments in polyglot callbacks. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) Arguments() CommandLineArgsEditor { + if s.err != nil { return &commandLineArgsEditor{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.arguments", reqArgs) + if err != nil { + return &commandLineArgsEditor{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.arguments returned unexpected type %T", result) + return &commandLineArgsEditor{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &commandLineArgsEditor{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// CancellationToken gets the `CancellationToken` that can be used to cancel the operation. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) CancellationToken() (*CancellationToken, error) { + if s.err != nil { var zero *CancellationToken; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.cancellationToken", reqArgs) + if err != nil { + var zero *CancellationToken + return zero, err + } + return decodeAs[*CancellationToken](result) +} + +// CertificatePath a value provider that will resolve to a path to the certificate file. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) CertificatePath() *ReferenceExpression { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.certificatePath", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(*ReferenceExpression) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.certificatePath returned unexpected type %T", result)) + return nil + } + return typed +} + +// Environment gets the editor used to set environment variables in polyglot callbacks. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) Environment() EnvironmentEditor { + if s.err != nil { return &environmentEditor{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.environment", reqArgs) + if err != nil { + return &environmentEditor{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.environment returned unexpected type %T", result) + return &environmentEditor{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &environmentEditor{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// ExecutionContext gets the `DistributedApplicationExecutionContext` for this session. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) ExecutionContext() DistributedApplicationExecutionContext { + if s.err != nil { return &distributedApplicationExecutionContext{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.executionContext", reqArgs) + if err != nil { + return &distributedApplicationExecutionContext{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.executionContext returned unexpected type %T", result) + return &distributedApplicationExecutionContext{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &distributedApplicationExecutionContext{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// KeyPath a value provider that will resolve to a path to the private key for the certificate. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) KeyPath() *ReferenceExpression { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.keyPath", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(*ReferenceExpression) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.keyPath returned unexpected type %T", result)) + return nil + } + return typed +} + +// PfxPath a value provider that will resolve to a path to a PFX file for the key pair. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) PfxPath() *ReferenceExpression { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.pfxPath", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(*ReferenceExpression) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.pfxPath returned unexpected type %T", result)) + return nil + } + return typed +} + +// Resource gets the resource to which the annotation is applied. +func (s *httpsCertificateConfigurationCallbackAnnotationContext) Resource() Resource { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.resource", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(Resource) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.resource returned unexpected type %T", result)) + return nil + } + return typed +} + +// HttpsEndpointUpdateCallbackContext is the public interface for handle type HttpsEndpointUpdateCallbackContext. +type HttpsEndpointUpdateCallbackContext interface { + handleReference + CancellationToken() (*CancellationToken, error) + Model() DistributedApplicationModel + Resource() Resource + Services() ServiceProvider + Err() error +} + +// httpsEndpointUpdateCallbackContext is the unexported impl of HttpsEndpointUpdateCallbackContext. +type httpsEndpointUpdateCallbackContext struct { + *resourceBuilderBase +} + +// newHttpsEndpointUpdateCallbackContextFromHandle wraps an existing handle as HttpsEndpointUpdateCallbackContext. +func newHttpsEndpointUpdateCallbackContextFromHandle(h *handle, c *client) HttpsEndpointUpdateCallbackContext { + return &httpsEndpointUpdateCallbackContext{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// CancellationToken gets the `CancellationToken` for the operation. +func (s *httpsEndpointUpdateCallbackContext) CancellationToken() (*CancellationToken, error) { + if s.err != nil { var zero *CancellationToken; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.cancellationToken", reqArgs) + if err != nil { + var zero *CancellationToken + return zero, err + } + return decodeAs[*CancellationToken](result) +} + +// Model gets the `DistributedApplicationModel` instance. +func (s *httpsEndpointUpdateCallbackContext) Model() DistributedApplicationModel { + if s.err != nil { return &distributedApplicationModel{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.model", reqArgs) + if err != nil { + return &distributedApplicationModel{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.model returned unexpected type %T", result) + return &distributedApplicationModel{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &distributedApplicationModel{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// Resource gets the `IResource` that is being configured for HTTPS. +func (s *httpsEndpointUpdateCallbackContext) Resource() Resource { + if s.err != nil { return nil } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.resource", reqArgs) + if err != nil { s.setErr(err); return nil } + typed, ok := result.(Resource) + if !ok { + s.setErr(fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.resource returned unexpected type %T", result)) + return nil + } + return typed +} + +// Services gets the `IServiceProvider` instance from the application. +func (s *httpsEndpointUpdateCallbackContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } ctx := context.Background() reqArgs := map[string]any{ "context": s.handle.ToJSON(), } - result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpCommandPrepareRequestContext.resourceName", reqArgs) + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.services", reqArgs) if err != nil { - var zero string - return zero, err + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} } - return decodeAs[string](result) + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} } // IComputeResource is the public interface for handle type IComputeResource. @@ -15889,6 +17254,7 @@ type InputsDialogValidationContext interface { AddValidationError(inputName string, errorMessage string) error CancellationToken() (*CancellationToken, error) Inputs() InteractionInputCollection + Services() ServiceProvider Err() error } @@ -15949,6 +17315,25 @@ func (s *inputsDialogValidationContext) Inputs() InteractionInputCollection { return &interactionInputCollection{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} } +// Services gets the service provider for resolving services during validation. +func (s *inputsDialogValidationContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting/InputsDialogValidationContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting/InputsDialogValidationContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + // InteractionInputCollection is the public interface for handle type InteractionInputCollection. type InteractionInputCollection interface { handleReference @@ -16179,11 +17564,13 @@ type ParameterResource interface { OnInitializeResource(callback func(arg InitializeResourceEvent)) ParameterResource OnResourceReady(callback func(arg ResourceReadyEvent)) ParameterResource OnResourceStopped(callback func(arg ResourceStoppedEvent)) ParameterResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ParameterResource TestWaitFor(dependency Resource) ParameterResource WithCancellableOperation(operation func(arg *CancellationToken)) ParameterResource WithChildRelationship(child Resource) ParameterResource WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) ParameterResource WithConfig(config *TestConfigDto) ParameterResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ParameterResource WithContainerRegistry(registry Resource) ParameterResource WithCorrelationId(correlationId string) ParameterResource WithCreatedAt(createdAt string) ParameterResource @@ -16219,6 +17606,7 @@ type ParameterResource interface { WithProcessCommandFactory(commandName string, displayName string, createProcessSpec func(arg ExecuteCommandContext) *ProcessCommandSpecExportData, options ...*WithProcessCommandFactoryOptions) ParameterResource WithRelationship(resourceBuilder Resource, type_ string) ParameterResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) ParameterResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ParameterResource WithSessionLifetime() ParameterResource WithStatus(status TestResourceStatus) ParameterResource WithUnionDependency(dependency any) ParameterResource @@ -16371,6 +17759,25 @@ func (s *parameterResource) OnResourceStopped(callback func(arg ResourceStoppedE return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *parameterResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ParameterResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *parameterResource) TestWaitFor(dependency Resource) ParameterResource { if s.err != nil { return s } @@ -16455,6 +17862,25 @@ func (s *parameterResource) WithConfig(config *TestConfigDto) ParameterResource return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *parameterResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ParameterResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *parameterResource) WithContainerRegistry(registry Resource) ParameterResource { if s.err != nil { return s } @@ -16996,6 +18422,32 @@ func (s *parameterResource) WithRequiredCommand(command string, options ...*With return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *parameterResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ParameterResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *parameterResource) WithSessionLifetime() ParameterResource { if s.err != nil { return s } @@ -17807,6 +19259,7 @@ type ProjectResource interface { OnResourceStopped(callback func(arg ResourceStoppedEvent)) ProjectResource PublishAsDockerFile(options ...*PublishAsDockerFileOptions) ProjectResource PublishWithContainerFiles(source ResourceWithContainerFiles, destinationPath string) ProjectResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ProjectResource TestWaitFor(dependency Resource) ProjectResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) ProjectResource WaitFor(dependency Resource, options ...*WaitForOptions) ProjectResource @@ -17820,6 +19273,7 @@ type ProjectResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) ProjectResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) ProjectResource WithConfig(config *TestConfigDto) ProjectResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ProjectResource WithContainerRegistry(registry Resource) ProjectResource WithCorrelationId(correlationId string) ProjectResource WithCreatedAt(createdAt string) ProjectResource @@ -17844,6 +19298,7 @@ type ProjectResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) ProjectResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) ProjectResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) ProjectResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) ProjectResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) ProjectResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) ProjectResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) ProjectResource @@ -17878,6 +19333,7 @@ type ProjectResource interface { WithRemoteImageTag(remoteImageTag string) ProjectResource WithReplicas(replicas float64) ProjectResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) ProjectResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ProjectResource WithSessionLifetime() ProjectResource WithStatus(status TestResourceStatus) ProjectResource WithUnionDependency(dependency any) ProjectResource @@ -18132,6 +19588,25 @@ func (s *projectResource) PublishWithContainerFiles(source ResourceWithContainer return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *projectResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) ProjectResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *projectResource) TestWaitFor(dependency Resource) ProjectResource { if s.err != nil { return s } @@ -18351,6 +19826,25 @@ func (s *projectResource) WithConfig(config *TestConfigDto) ProjectResource { return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *projectResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) ProjectResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerRegistry configures the resource to use the specified container registry for container image operations. func (s *projectResource) WithContainerRegistry(registry Resource) ProjectResource { if s.err != nil { return s } @@ -18727,6 +20221,25 @@ func (s *projectResource) WithHttpProbe(probeType ProbeType, options ...*WithHtt return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *projectResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) ProjectResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *projectResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) ProjectResource { if s.err != nil { return s } @@ -19296,6 +20809,32 @@ func (s *projectResource) WithRequiredCommand(command string, options ...*WithRe return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *projectResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) ProjectResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *projectResource) WithSessionLifetime() ProjectResource { if s.err != nil { return s } @@ -19904,24 +21443,181 @@ func (s *reportingTask) UpdateTaskMarkdown(markdownString string, options ...*Up if s.err != nil { return s.err } ctx := context.Background() reqArgs := map[string]any{ - "reportingTask": s.handle.ToJSON(), + "reportingTask": s.handle.ToJSON(), + } + reqArgs["markdownString"] = serializeValue(markdownString) + if len(options) > 0 { + merged := &UpdateTaskMarkdownOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + if merged.CancellationToken != nil { + ctx = merged.CancellationToken.Context() + if id := s.client.registerCancellation(merged.CancellationToken); id != "" { + reqArgs["cancellationToken"] = id + } + } + } + _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/updateTaskMarkdown", reqArgs) + return err +} + +// RequiredCommandValidationContext is the public interface for handle type RequiredCommandValidationContext. +type RequiredCommandValidationContext interface { + handleReference + CancellationToken() (*CancellationToken, error) + Failure(validationMessage string) RequiredCommandValidationResult + ResolvedPath() (string, error) + Services() ServiceProvider + Success() RequiredCommandValidationResult + Err() error +} + +// requiredCommandValidationContext is the unexported impl of RequiredCommandValidationContext. +type requiredCommandValidationContext struct { + *resourceBuilderBase +} + +// newRequiredCommandValidationContextFromHandle wraps an existing handle as RequiredCommandValidationContext. +func newRequiredCommandValidationContextFromHandle(h *handle, c *client) RequiredCommandValidationContext { + return &requiredCommandValidationContext{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// CancellationToken gets a cancellation token that can be used to cancel the validation. +func (s *requiredCommandValidationContext) CancellationToken() (*CancellationToken, error) { + if s.err != nil { var zero *CancellationToken; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.cancellationToken", reqArgs) + if err != nil { + var zero *CancellationToken + return zero, err + } + return decodeAs[*CancellationToken](result) +} + +// Failure creates a failed validation result with the specified message. +func (s *requiredCommandValidationContext) Failure(validationMessage string) RequiredCommandValidationResult { + if s.err != nil { return &requiredCommandValidationResult{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + reqArgs["validationMessage"] = serializeValue(validationMessage) + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.failure", reqArgs) + if err != nil { + return &requiredCommandValidationResult{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.failure returned unexpected type %T", result) + return &requiredCommandValidationResult{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &requiredCommandValidationResult{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// ResolvedPath gets the resolved full path to the command executable. +func (s *requiredCommandValidationContext) ResolvedPath() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.resolvedPath", reqArgs) + if err != nil { + var zero string + return zero, err + } + return decodeAs[string](result) +} + +// Services gets the service provider for accessing application services. +func (s *requiredCommandValidationContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// Success creates a successful validation result. +func (s *requiredCommandValidationContext) Success() RequiredCommandValidationResult { + if s.err != nil { return &requiredCommandValidationResult{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.success", reqArgs) + if err != nil { + return &requiredCommandValidationResult{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.success returned unexpected type %T", result) + return &requiredCommandValidationResult{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &requiredCommandValidationResult{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + +// RequiredCommandValidationResult is the public interface for handle type RequiredCommandValidationResult. +type RequiredCommandValidationResult interface { + handleReference + IsValid() (bool, error) + ValidationMessage() (string, error) + Err() error +} + +// requiredCommandValidationResult is the unexported impl of RequiredCommandValidationResult. +type requiredCommandValidationResult struct { + *resourceBuilderBase +} + +// newRequiredCommandValidationResultFromHandle wraps an existing handle as RequiredCommandValidationResult. +func newRequiredCommandValidationResultFromHandle(h *handle, c *client) RequiredCommandValidationResult { + return &requiredCommandValidationResult{resourceBuilderBase: newResourceBuilderBase(h, c)} +} + +// IsValid gets a value indicating whether the command validation succeeded. +func (s *requiredCommandValidationResult) IsValid() (bool, error) { + if s.err != nil { var zero bool; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.isValid", reqArgs) + if err != nil { + var zero bool + return zero, err + } + return decodeAs[bool](result) +} + +// ValidationMessage gets an optional validation message describing why validation failed. +func (s *requiredCommandValidationResult) ValidationMessage() (string, error) { + if s.err != nil { var zero string; return zero, s.err } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), } - reqArgs["markdownString"] = serializeValue(markdownString) - if len(options) > 0 { - merged := &UpdateTaskMarkdownOptions{} - for _, opt := range options { - if opt != nil { merged = deepUpdate(merged, opt) } - } - for k, v := range merged.ToMap() { reqArgs[k] = v } - if merged.CancellationToken != nil { - ctx = merged.CancellationToken.Context() - if id := s.client.registerCancellation(merged.CancellationToken); id != "" { - reqArgs["cancellationToken"] = id - } - } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.validationMessage", reqArgs) + if err != nil { + var zero string + return zero, err } - _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/updateTaskMarkdown", reqArgs) - return err + return decodeAs[string](result) } // ResourceCommandService is the public interface for handle type ResourceCommandService. @@ -20898,6 +22594,7 @@ type TestDatabaseResource interface { OnResourceStopped(callback func(arg ResourceStoppedEvent)) TestDatabaseResource PublishAsConnectionString() TestDatabaseResource PublishAsContainer() TestDatabaseResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) TestDatabaseResource TestWaitFor(dependency Resource) TestDatabaseResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) TestDatabaseResource WaitFor(dependency Resource, options ...*WaitForOptions) TestDatabaseResource @@ -20914,8 +22611,10 @@ type TestDatabaseResource interface { WithCommand(name string, displayName string, executeCommand func(arg ExecuteCommandContext) *ExecuteCommandResult, options ...*WithCommandOptions) TestDatabaseResource WithComputeEnvironment(computeEnvironmentResource ComputeEnvironmentResource) TestDatabaseResource WithConfig(config *TestConfigDto) TestDatabaseResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) TestDatabaseResource WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) TestDatabaseResource WithContainerFiles(destinationPath string, sourcePath string, options ...*WithContainerFilesOptions) TestDatabaseResource + WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) TestDatabaseResource WithContainerName(name string) TestDatabaseResource WithContainerNetworkAlias(alias string) TestDatabaseResource WithContainerRegistry(registry Resource) TestDatabaseResource @@ -20946,6 +22645,7 @@ type TestDatabaseResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) TestDatabaseResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) TestDatabaseResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) TestDatabaseResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) TestDatabaseResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) TestDatabaseResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) TestDatabaseResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) TestDatabaseResource @@ -20985,6 +22685,7 @@ type TestDatabaseResource interface { WithRemoteImageName(remoteImageName string) TestDatabaseResource WithRemoteImageTag(remoteImageTag string) TestDatabaseResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) TestDatabaseResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) TestDatabaseResource WithSessionLifetime() TestDatabaseResource WithStatus(status TestResourceStatus) TestDatabaseResource WithUnionDependency(dependency any) TestDatabaseResource @@ -21211,6 +22912,25 @@ func (s *testDatabaseResource) PublishAsContainer() TestDatabaseResource { return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *testDatabaseResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) TestDatabaseResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *testDatabaseResource) TestWaitFor(dependency Resource) TestDatabaseResource { if s.err != nil { return s } @@ -21484,6 +23204,25 @@ func (s *testDatabaseResource) WithConfig(config *TestConfigDto) TestDatabaseRes return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *testDatabaseResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) TestDatabaseResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerCertificatePaths adds container certificate path overrides used for certificate trust at run time. func (s *testDatabaseResource) WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) TestDatabaseResource { if s.err != nil { return s } @@ -21522,6 +23261,32 @@ func (s *testDatabaseResource) WithContainerFiles(destinationPath string, source return s } +// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +func (s *testDatabaseResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) TestDatabaseResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["destinationPath"] = serializeValue(destinationPath) + if callback != nil { + cb := callback + shim := func(args ...any) any { + return cb(callbackArg[ContainerFileSystemCallbackContext](args, 0), callbackArg[*CancellationToken](args, 1)) + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithContainerFilesCallbackOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerFilesCallback", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerName overrides the default container name for this resource. By default Aspire generates a unique container name based on the resource name and a random postfix (or a postfix based on a hash of the AppHost project path for persistent container resources). This method allows you to override that behavior with a custom name, but could lead to naming conflicts if the specified name is not unique. func (s *testDatabaseResource) WithContainerName(name string) TestDatabaseResource { if s.err != nil { return s } @@ -22006,6 +23771,25 @@ func (s *testDatabaseResource) WithHttpProbe(probeType ProbeType, options ...*Wi return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *testDatabaseResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) TestDatabaseResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *testDatabaseResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) TestDatabaseResource { if s.err != nil { return s } @@ -22642,6 +24426,32 @@ func (s *testDatabaseResource) WithRequiredCommand(command string, options ...*W return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *testDatabaseResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) TestDatabaseResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *testDatabaseResource) WithSessionLifetime() TestDatabaseResource { if s.err != nil { return s } @@ -22986,6 +24796,7 @@ type TestRedisResource interface { OnResourceStopped(callback func(arg ResourceStoppedEvent)) TestRedisResource PublishAsConnectionString() TestRedisResource PublishAsContainer() TestRedisResource + SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) TestRedisResource TestWaitFor(dependency Resource) TestRedisResource TestWithEnvironmentCallback(callback func(arg TestEnvironmentContext)) TestRedisResource WaitFor(dependency Resource, options ...*WaitForOptions) TestRedisResource @@ -23006,8 +24817,10 @@ type TestRedisResource interface { WithConnectionProperty(name string, value any) TestRedisResource WithConnectionString(connectionString *ReferenceExpression) TestRedisResource WithConnectionStringDirect(connectionString string) TestRedisResource + WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) TestRedisResource WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) TestRedisResource WithContainerFiles(destinationPath string, sourcePath string, options ...*WithContainerFilesOptions) TestRedisResource + WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) TestRedisResource WithContainerName(name string) TestRedisResource WithContainerNetworkAlias(alias string) TestRedisResource WithContainerRegistry(registry Resource) TestRedisResource @@ -23039,6 +24852,7 @@ type TestRedisResource interface { WithHttpEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpEndpointCallbackOptions) TestRedisResource WithHttpHealthCheck(options ...*WithHttpHealthCheckOptions) TestRedisResource WithHttpProbe(probeType ProbeType, options ...*WithHttpProbeOptions) TestRedisResource + WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) TestRedisResource WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) TestRedisResource WithHttpsEndpoint(options ...*WithHttpsEndpointOptions) TestRedisResource WithHttpsEndpointCallback(callback func(obj EndpointUpdateContext), options ...*WithHttpsEndpointCallbackOptions) TestRedisResource @@ -23081,6 +24895,7 @@ type TestRedisResource interface { WithRemoteImageName(remoteImageName string) TestRedisResource WithRemoteImageTag(remoteImageTag string) TestRedisResource WithRequiredCommand(command string, options ...*WithRequiredCommandOptions) TestRedisResource + WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) TestRedisResource WithSessionLifetime() TestRedisResource WithStatus(status TestResourceStatus) TestRedisResource WithUnionDependency(dependency any) TestRedisResource @@ -23434,6 +25249,25 @@ func (s *testRedisResource) PublishAsContainer() TestRedisResource { return s } +// SubscribeHttpsEndpointsUpdate subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. +func (s *testRedisResource) SubscribeHttpsEndpointsUpdate(callback func(obj HttpsEndpointUpdateCallbackContext)) TestRedisResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsEndpointUpdateCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); err != nil { s.setErr(err) } + return s +} + // TestWaitFor waits for another resource (test version) func (s *testRedisResource) TestWaitFor(dependency Resource) TestRedisResource { if s.err != nil { return s } @@ -23781,6 +25615,25 @@ func (s *testRedisResource) WithConnectionStringDirect(connectionString string) return s } +// WithContainerBuildOptions configures container build options for a compute resource using an async callback. +func (s *testRedisResource) WithContainerBuildOptions(callback func(arg ContainerBuildOptionsCallbackContext)) TestRedisResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[ContainerBuildOptionsCallbackContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerBuildOptions", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerCertificatePaths adds container certificate path overrides used for certificate trust at run time. func (s *testRedisResource) WithContainerCertificatePaths(options ...*WithContainerCertificatePathsOptions) TestRedisResource { if s.err != nil { return s } @@ -23819,6 +25672,32 @@ func (s *testRedisResource) WithContainerFiles(destinationPath string, sourcePat return s } +// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +func (s *testRedisResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) TestRedisResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["destinationPath"] = serializeValue(destinationPath) + if callback != nil { + cb := callback + shim := func(args ...any) any { + return cb(callbackArg[ContainerFileSystemCallbackContext](args, 0), callbackArg[*CancellationToken](args, 1)) + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithContainerFilesCallbackOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withContainerFilesCallback", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithContainerName overrides the default container name for this resource. By default Aspire generates a unique container name based on the resource name and a random postfix (or a postfix based on a hash of the AppHost project path for persistent container resources). This method allows you to override that behavior with a custom name, but could lead to naming conflicts if the specified name is not unique. func (s *testRedisResource) WithContainerName(name string) TestRedisResource { if s.err != nil { return s } @@ -24321,6 +26200,25 @@ func (s *testRedisResource) WithHttpProbe(probeType ProbeType, options ...*WithH return s } +// WithHttpsCertificateConfiguration adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. +func (s *testRedisResource) WithHttpsCertificateConfiguration(callback func(arg HttpsCertificateConfigurationCallbackAnnotationContext)) TestRedisResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + if callback != nil { + cb := callback + shim := func(args ...any) any { + cb(callbackArg[HttpsCertificateConfigurationCallbackAnnotationContext](args, 0)) + return nil + } + reqArgs["callback"] = s.client.registerCallback(shim) + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithHttpsDeveloperCertificate indicates that a resource should use the developer certificate key pair for HTTPS endpoints at run time. Currently this indicates use of the ASP.NET Core developer certificate. The developer certificate will only be used when running in local development scenarios; in publish mode resources will use their default certificate configuration. func (s *testRedisResource) WithHttpsDeveloperCertificate(options ...*WithHttpsDeveloperCertificateOptions) TestRedisResource { if s.err != nil { return s } @@ -25006,6 +26904,32 @@ func (s *testRedisResource) WithRequiredCommand(command string, options ...*With return s } +// WithRequiredCommandValidation declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. +func (s *testRedisResource) WithRequiredCommandValidation(command string, validationCallback func(arg RequiredCommandValidationContext) RequiredCommandValidationResult, options ...*WithRequiredCommandValidationOptions) TestRedisResource { + if s.err != nil { return s } + ctx := context.Background() + reqArgs := map[string]any{ + "builder": s.handle.ToJSON(), + } + reqArgs["command"] = serializeValue(command) + if validationCallback != nil { + cb := validationCallback + shim := func(args ...any) any { + return cb(callbackArg[RequiredCommandValidationContext](args, 0)) + } + reqArgs["validationCallback"] = s.client.registerCallback(shim) + } + if len(options) > 0 { + merged := &WithRequiredCommandValidationOptions{} + for _, opt := range options { + if opt != nil { merged = deepUpdate(merged, opt) } + } + for k, v := range merged.ToMap() { reqArgs[k] = v } + } + if _, err := s.client.invokeCapability(ctx, "Aspire.Hosting/withRequiredCommandValidation", reqArgs); err != nil { s.setErr(err) } + return s +} + // WithSessionLifetime configures a resource to use a session lifetime. func (s *testRedisResource) WithSessionLifetime() TestRedisResource { if s.err != nil { return s } @@ -25287,6 +27211,7 @@ func (s *testResourceContext) Value() (float64, error) { type UpdateCommandStateContext interface { handleReference ResourceSnapshot() (*UpdateCommandStateResourceSnapshot, error) + Services() ServiceProvider Err() error } @@ -25315,6 +27240,25 @@ func (s *updateCommandStateContext) ResourceSnapshot() (*UpdateCommandStateResou return decodeAs[*UpdateCommandStateResourceSnapshot](result) } +// Services the service provider. +func (s *updateCommandStateContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + // UserSecretsManager is the public interface for handle type UserSecretsManager. type UserSecretsManager interface { handleReference @@ -25572,6 +27516,18 @@ func (o *WithContainerFilesOptions) ToMap() map[string]any { return m } +// WithContainerFilesCallbackOptions carries optional parameters for WithContainerFilesCallback. +type WithContainerFilesCallbackOptions struct { + Options *ContainerFilesOptions `json:"options,omitempty"` +} + +func (o *WithContainerFilesCallbackOptions) ToMap() map[string]any { + m := map[string]any{} + if o == nil { return m } + if o.Options != nil { m["options"] = serializeValue(o.Options) } + return m +} + // WithDockerfileBuilderOptions carries optional parameters for WithDockerfileBuilder. type WithDockerfileBuilderOptions struct { Stage *string `json:"stage,omitempty"` @@ -25764,6 +27720,18 @@ func (o *WithRequiredCommandOptions) ToMap() map[string]any { return m } +// WithRequiredCommandValidationOptions carries optional parameters for WithRequiredCommandValidation. +type WithRequiredCommandValidationOptions struct { + HelpLink *string `json:"helpLink,omitempty"` +} + +func (o *WithRequiredCommandValidationOptions) ToMap() map[string]any { + m := map[string]any{} + if o == nil { return m } + if o.HelpLink != nil { m["helpLink"] = serializeValue(o.HelpLink) } + return m +} + // WithReferenceOptions carries optional parameters for WithReference. type WithReferenceOptions struct { ConnectionName *string `json:"connectionName,omitempty"` @@ -26322,6 +28290,66 @@ func (o *SaveStateJsonOptions) ToMap() map[string]any { return m } +// CreateFileOptions carries optional parameters for CreateFile. +type CreateFileOptions struct { + Contents *string `json:"contents,omitempty"` + SourcePath *string `json:"sourcePath,omitempty"` + Owner *float64 `json:"owner,omitempty"` + Group *float64 `json:"group,omitempty"` + Mode *float64 `json:"mode,omitempty"` + ContinueOnError *bool `json:"continueOnError,omitempty"` +} + +func (o *CreateFileOptions) ToMap() map[string]any { + m := map[string]any{} + if o == nil { return m } + if o.Contents != nil { m["contents"] = serializeValue(o.Contents) } + if o.SourcePath != nil { m["sourcePath"] = serializeValue(o.SourcePath) } + if o.Owner != nil { m["owner"] = serializeValue(o.Owner) } + if o.Group != nil { m["group"] = serializeValue(o.Group) } + if o.Mode != nil { m["mode"] = serializeValue(o.Mode) } + if o.ContinueOnError != nil { m["continueOnError"] = serializeValue(o.ContinueOnError) } + return m +} + +// CreateCertificateFileOptions carries optional parameters for CreateCertificateFile. +type CreateCertificateFileOptions struct { + Contents *string `json:"contents,omitempty"` + SourcePath *string `json:"sourcePath,omitempty"` + Owner *float64 `json:"owner,omitempty"` + Group *float64 `json:"group,omitempty"` + Mode *float64 `json:"mode,omitempty"` + ContinueOnError *bool `json:"continueOnError,omitempty"` +} + +func (o *CreateCertificateFileOptions) ToMap() map[string]any { + m := map[string]any{} + if o == nil { return m } + if o.Contents != nil { m["contents"] = serializeValue(o.Contents) } + if o.SourcePath != nil { m["sourcePath"] = serializeValue(o.SourcePath) } + if o.Owner != nil { m["owner"] = serializeValue(o.Owner) } + if o.Group != nil { m["group"] = serializeValue(o.Group) } + if o.Mode != nil { m["mode"] = serializeValue(o.Mode) } + if o.ContinueOnError != nil { m["continueOnError"] = serializeValue(o.ContinueOnError) } + return m +} + +// CreateDirectoryOptions carries optional parameters for CreateDirectory. +type CreateDirectoryOptions struct { + Owner *float64 `json:"owner,omitempty"` + Group *float64 `json:"group,omitempty"` + Mode *float64 `json:"mode,omitempty"` +} + +func (o *CreateDirectoryOptions) ToMap() map[string]any { + m := map[string]any{} + if o == nil { return m } + if o.Owner != nil { m["owner"] = serializeValue(o.Owner) } + if o.Group != nil { m["group"] = serializeValue(o.Group) } + if o.Mode != nil { m["mode"] = serializeValue(o.Mode) } + return m +} + // GetValueAsyncOptions carries optional parameters for GetValueAsync. type GetValueAsyncOptions struct { CancellationToken *CancellationToken `json:"-"` @@ -26553,6 +28581,15 @@ func registerWrappers(c *client) { c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent", func(h *handle, c *client) any { return newConnectionStringAvailableEventFromHandle(h, c) }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext", func(h *handle, c *client) any { + return newContainerBuildOptionsCallbackContextFromHandle(h, c) + }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext", func(h *handle, c *client) any { + return newContainerFileSystemCallbackContextFromHandle(h, c) + }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemItem", func(h *handle, c *client) any { + return newContainerFileSystemItemFromHandle(h, c) + }) c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptions", func(h *handle, c *client) any { return newContainerImagePushOptionsFromHandle(h, c) }) @@ -26655,6 +28692,12 @@ func registerWrappers(c *client) { c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpCommandPrepareRequestContext", func(h *handle, c *client) any { return newHttpCommandPrepareRequestContextFromHandle(h, c) }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext", func(h *handle, c *client) any { + return newHttpsCertificateConfigurationCallbackAnnotationContextFromHandle(h, c) + }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext", func(h *handle, c *client) any { + return newHttpsEndpointUpdateCallbackContextFromHandle(h, c) + }) c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.IComputeResource", func(h *handle, c *client) any { return newIComputeResourceFromHandle(h, c) }) @@ -26718,6 +28761,12 @@ func registerWrappers(c *client) { c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.Pipelines.IReportingTask", func(h *handle, c *client) any { return newReportingTaskFromHandle(h, c) }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext", func(h *handle, c *client) any { + return newRequiredCommandValidationContextFromHandle(h, c) + }) + c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult", func(h *handle, c *client) any { + return newRequiredCommandValidationResultFromHandle(h, c) + }) c.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceCommandService", func(h *handle, c *client) any { return newResourceCommandServiceFromHandle(h, c) }) diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java index 41aa0b988fe..e78c7cad2fb 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java @@ -1377,6 +1377,9 @@ public class AspireRegistrations { AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.CommandLineArgsCallbackContext", (h, c) -> new CommandLineArgsCallbackContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.CommandLineArgsEditor", (h, c) -> new CommandLineArgsEditor(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent", (h, c) -> new ConnectionStringAvailableEvent(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext", (h, c) -> new ContainerBuildOptionsCallbackContext(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemItem", (h, c) -> new ContainerFileSystemItem(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext", (h, c) -> new ContainerFileSystemCallbackContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptions", (h, c) -> new ContainerImagePushOptions(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptionsCallbackContext", (h, c) -> new ContainerImagePushOptionsCallbackContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.DistributedApplicationModel", (h, c) -> new DistributedApplicationModel(h, c)); @@ -1387,10 +1390,14 @@ public class AspireRegistrations { AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.EnvironmentCallbackContext", (h, c) -> new EnvironmentCallbackContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.EnvironmentEditor", (h, c) -> new EnvironmentEditor(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpCommandPrepareRequestContext", (h, c) -> new HttpCommandPrepareRequestContext(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext", (h, c) -> new HttpsCertificateConfigurationCallbackAnnotationContext(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext", (h, c) -> new HttpsEndpointUpdateCallbackContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.IExpressionValue", (h, c) -> new IExpressionValue(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.InitializeResourceEvent", (h, c) -> new InitializeResourceEvent(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.LogFacade", (h, c) -> new LogFacade(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ReferenceExpressionBuilder", (h, c) -> new ReferenceExpressionBuilder(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext", (h, c) -> new RequiredCommandValidationContext(h, c)); + AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult", (h, c) -> new RequiredCommandValidationResult(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.UpdateCommandStateContext", (h, c) -> new UpdateCommandStateContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ExecuteCommandContext", (h, c) -> new ExecuteCommandContext(h, c)); AspireClient.registerHandleWrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceEndpointsAllocatedEvent", (h, c) -> new ResourceEndpointsAllocatedEvent(h, c)); @@ -1768,6 +1775,29 @@ public CSharpAppResource withRequiredCommand(String command, String helpLink) { return this; } + public CSharpAppResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public CSharpAppResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public CSharpAppResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -2521,6 +2551,38 @@ public CSharpAppResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public CSharpAppResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public CSharpAppResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public CSharpAppResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -2874,6 +2936,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public CSharpAppResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public CSharpAppResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -3800,6 +3878,327 @@ public IServiceProvider services() { } +// ===== ContainerBuildOptionsCallbackContext.java ===== +// ContainerBuildOptionsCallbackContext.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext. */ +public class ContainerBuildOptionsCallbackContext extends HandleWrapperBase { + ContainerBuildOptionsCallbackContext(Handle handle, AspireClient client) { + super(handle, client); + } + + /** Gets the resource being built. */ + public IResource resource() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.resource", reqArgs); + return (IResource) result; + } + + /** Gets the service provider. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.services", reqArgs); + return (IServiceProvider) result; + } + + /** Gets the logger instance. */ + public ILogger logger() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.logger", reqArgs); + return (ILogger) result; + } + + /** Gets the cancellation token. */ + public CancellationToken cancellationToken() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.cancellationToken", reqArgs); + return (CancellationToken) result; + } + + /** Gets the distributed application execution context. */ + public DistributedApplicationExecutionContext executionContext() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.executionContext", reqArgs); + return (DistributedApplicationExecutionContext) result; + } + + /** Gets or sets the destination for the container image. */ + public ContainerImageDestination destination() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.destination", reqArgs); + return result == null ? null : ContainerImageDestination.fromValue((String) result); + } + + /** Sets the Destination property */ + public ContainerBuildOptionsCallbackContext setDestination(ContainerImageDestination value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setDestination", reqArgs); + return (ContainerBuildOptionsCallbackContext) result; + } + + /** Gets or sets the output path for the container archive. */ + public String outputPath() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.outputPath", reqArgs); + return result == null ? null : (String) result; + } + + /** Sets the OutputPath property */ + public ContainerBuildOptionsCallbackContext setOutputPath(String value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setOutputPath", reqArgs); + return (ContainerBuildOptionsCallbackContext) result; + } + + /** Gets or sets the container image format. */ + public ContainerImageFormat imageFormat() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.imageFormat", reqArgs); + return result == null ? null : ContainerImageFormat.fromValue((String) result); + } + + /** Sets the ImageFormat property */ + public ContainerBuildOptionsCallbackContext setImageFormat(ContainerImageFormat value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setImageFormat", reqArgs); + return (ContainerBuildOptionsCallbackContext) result; + } + + /** Gets or sets the target platform for the container. */ + public ContainerTargetPlatform targetPlatform() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.targetPlatform", reqArgs); + return result == null ? null : ContainerTargetPlatform.fromValue((String) result); + } + + /** Sets the TargetPlatform property */ + public ContainerBuildOptionsCallbackContext setTargetPlatform(ContainerTargetPlatform value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setTargetPlatform", reqArgs); + return (ContainerBuildOptionsCallbackContext) result; + } + + /** Gets or sets the local image name for the built container. */ + public String localImageName() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageName", reqArgs); + return result == null ? null : (String) result; + } + + /** Sets the LocalImageName property */ + public ContainerBuildOptionsCallbackContext setLocalImageName(String value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageName", reqArgs); + return (ContainerBuildOptionsCallbackContext) result; + } + + /** Gets or sets the local image tag for the built container. */ + public String localImageTag() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageTag", reqArgs); + return result == null ? null : (String) result; + } + + /** Sets the LocalImageTag property */ + public ContainerBuildOptionsCallbackContext setLocalImageTag(String value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageTag", reqArgs); + return (ContainerBuildOptionsCallbackContext) result; + } + +} + +// ===== ContainerFileSystemCallbackContext.java ===== +// ContainerFileSystemCallbackContext.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext. */ +public class ContainerFileSystemCallbackContext extends HandleWrapperBase { + ContainerFileSystemCallbackContext(Handle handle, AspireClient client) { + super(handle, client); + } + + /** A `IServiceProvider` that can be used to resolve services in the callback. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.services", reqArgs); + return (IServiceProvider) result; + } + + /** The app model resource the callback is associated with. */ + public IResource model() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.model", reqArgs); + return (IResource) result; + } + + /** Creates a container file entry with inline contents or a host source path. */ + public ContainerFileSystemItem createFile(String name, CreateFileOptions options) { + var contents = options == null ? null : options.getContents(); + var sourcePath = options == null ? null : options.getSourcePath(); + var owner = options == null ? null : options.getOwner(); + var group = options == null ? null : options.getGroup(); + var mode = options == null ? null : options.getMode(); + var continueOnError = options == null ? null : options.getContinueOnError(); + return createFileImpl(name, contents, sourcePath, owner, group, mode, continueOnError); + } + + public ContainerFileSystemItem createFile(String name) { + return createFile(name, null); + } + + /** Creates a container file entry with inline contents or a host source path. */ + private ContainerFileSystemItem createFileImpl(String name, String contents, String sourcePath, Double owner, Double group, Double mode, Boolean continueOnError) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("name", AspireClient.serializeValue(name)); + if (contents != null) { + reqArgs.put("contents", AspireClient.serializeValue(contents)); + } + if (sourcePath != null) { + reqArgs.put("sourcePath", AspireClient.serializeValue(sourcePath)); + } + if (owner != null) { + reqArgs.put("owner", AspireClient.serializeValue(owner)); + } + if (group != null) { + reqArgs.put("group", AspireClient.serializeValue(group)); + } + if (mode != null) { + reqArgs.put("mode", AspireClient.serializeValue(mode)); + } + if (continueOnError != null) { + reqArgs.put("continueOnError", AspireClient.serializeValue(continueOnError)); + } + var result = getClient().invokeCapability("Aspire.Hosting/createFile", reqArgs); + return (ContainerFileSystemItem) result; + } + + /** Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. */ + public ContainerFileSystemItem createCertificateFile(String name, CreateCertificateFileOptions options) { + var contents = options == null ? null : options.getContents(); + var sourcePath = options == null ? null : options.getSourcePath(); + var owner = options == null ? null : options.getOwner(); + var group = options == null ? null : options.getGroup(); + var mode = options == null ? null : options.getMode(); + var continueOnError = options == null ? null : options.getContinueOnError(); + return createCertificateFileImpl(name, contents, sourcePath, owner, group, mode, continueOnError); + } + + public ContainerFileSystemItem createCertificateFile(String name) { + return createCertificateFile(name, null); + } + + /** Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. */ + private ContainerFileSystemItem createCertificateFileImpl(String name, String contents, String sourcePath, Double owner, Double group, Double mode, Boolean continueOnError) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("name", AspireClient.serializeValue(name)); + if (contents != null) { + reqArgs.put("contents", AspireClient.serializeValue(contents)); + } + if (sourcePath != null) { + reqArgs.put("sourcePath", AspireClient.serializeValue(sourcePath)); + } + if (owner != null) { + reqArgs.put("owner", AspireClient.serializeValue(owner)); + } + if (group != null) { + reqArgs.put("group", AspireClient.serializeValue(group)); + } + if (mode != null) { + reqArgs.put("mode", AspireClient.serializeValue(mode)); + } + if (continueOnError != null) { + reqArgs.put("continueOnError", AspireClient.serializeValue(continueOnError)); + } + var result = getClient().invokeCapability("Aspire.Hosting/createCertificateFile", reqArgs); + return (ContainerFileSystemItem) result; + } + + /** Creates a container directory entry containing the specified child entries. */ + public ContainerFileSystemItem createDirectory(String name, ContainerFileSystemItem[] entries, CreateDirectoryOptions options) { + var owner = options == null ? null : options.getOwner(); + var group = options == null ? null : options.getGroup(); + var mode = options == null ? null : options.getMode(); + return createDirectoryImpl(name, entries, owner, group, mode); + } + + public ContainerFileSystemItem createDirectory(String name, ContainerFileSystemItem[] entries) { + return createDirectory(name, entries, null); + } + + /** Creates a container directory entry containing the specified child entries. */ + private ContainerFileSystemItem createDirectoryImpl(String name, ContainerFileSystemItem[] entries, Double owner, Double group, Double mode) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("name", AspireClient.serializeValue(name)); + reqArgs.put("entries", AspireClient.serializeValue(entries)); + if (owner != null) { + reqArgs.put("owner", AspireClient.serializeValue(owner)); + } + if (group != null) { + reqArgs.put("group", AspireClient.serializeValue(group)); + } + if (mode != null) { + reqArgs.put("mode", AspireClient.serializeValue(mode)); + } + var result = getClient().invokeCapability("Aspire.Hosting/createDirectory", reqArgs); + return (ContainerFileSystemItem) result; + } + +} + +// ===== ContainerFileSystemItem.java ===== +// ContainerFileSystemItem.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemItem. */ +public class ContainerFileSystemItem extends HandleWrapperBase { + ContainerFileSystemItem(Handle handle, AspireClient client) { + super(handle, client); + } + +} + // ===== ContainerFilesOptions.java ===== // ContainerFilesOptions.java - GENERATED CODE - DO NOT EDIT @@ -3842,6 +4241,64 @@ public Map toMap() { } } +// ===== ContainerImageDestination.java ===== +// ContainerImageDestination.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** ContainerImageDestination enum. */ +public enum ContainerImageDestination implements WireValueEnum { + REGISTRY("Registry"), + ARCHIVE("Archive"); + + private final String value; + + ContainerImageDestination(String value) { + this.value = value; + } + + public String getValue() { return value; } + + public static ContainerImageDestination fromValue(String value) { + for (ContainerImageDestination e : values()) { + if (e.value.equals(value)) return e; + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} + +// ===== ContainerImageFormat.java ===== +// ContainerImageFormat.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** ContainerImageFormat enum. */ +public enum ContainerImageFormat implements WireValueEnum { + DOCKER("Docker"), + OCI("Oci"); + + private final String value; + + ContainerImageFormat(String value) { + this.value = value; + } + + public String getValue() { return value; } + + public static ContainerImageFormat fromValue(String value) { + for (ContainerImageFormat e : values()) { + if (e.value.equals(value)) return e; + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} + // ===== ContainerImagePushOptions.java ===== // ContainerImagePushOptions.java - GENERATED CODE - DO NOT EDIT @@ -4170,6 +4627,29 @@ public ContainerRegistryResource withRequiredCommand(String command, String help return this; } + public ContainerRegistryResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public ContainerRegistryResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public ContainerRegistryResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -4349,6 +4829,22 @@ public ContainerRegistryResource withProcessCommandFactory(String commandName, S return this; } + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public ContainerRegistryResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public ContainerRegistryResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -4584,6 +5080,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public ContainerRegistryResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public ContainerRegistryResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -5153,6 +5665,30 @@ public ContainerResource withContainerFiles(String destinationPath, String sourc return this; } + public ContainerResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback) { + return withContainerFilesCallback(destinationPath, callback, null); + } + + /** Creates or updates files and folders in a container using entries produced by a callback. */ + public ContainerResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("destinationPath", AspireClient.serializeValue(destinationPath)); + var callbackId = getClient().registerCallback(args -> { + var arg1 = (ContainerFileSystemCallbackContext) args[0]; + var arg2 = CancellationToken.fromValue(args[1]); + return AspireClient.awaitValue(callback.invoke(arg1, arg2)); + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + if (options != null) { + reqArgs.put("options", AspireClient.serializeValue(options)); + } + getClient().invokeCapability("Aspire.Hosting/withContainerFilesCallback", reqArgs); + return this; + } + public ContainerResource withDockerfileBuilder(String contextPath, AspireAction1 callback) { return withDockerfileBuilder(contextPath, callback, null); } @@ -5275,6 +5811,29 @@ public ContainerResource withRequiredCommand(String command, String helpLink) { return this; } + public ContainerResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public ContainerResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public ContainerResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -6002,15 +6561,47 @@ public ContainerResource withHttpsDeveloperCertificate(ParameterResource passwor if (password != null) { reqArgs.put("password", AspireClient.serializeValue(password)); } - getClient().invokeCapability("Aspire.Hosting/withParameterHttpsDeveloperCertificate", reqArgs); + getClient().invokeCapability("Aspire.Hosting/withParameterHttpsDeveloperCertificate", reqArgs); + return this; + } + + /** Disable HTTPS/TLS server certificate configuration for the resource. No HTTPS/TLS termination configuration will be applied. */ + public ContainerResource withoutHttpsCertificate() { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + getClient().invokeCapability("Aspire.Hosting/withoutHttpsCertificate", reqArgs); + return this; + } + + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public ContainerResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); return this; } - /** Disable HTTPS/TLS server certificate configuration for the resource. No HTTPS/TLS termination configuration will be applied. */ - public ContainerResource withoutHttpsCertificate() { + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public ContainerResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { Map reqArgs = new HashMap<>(); reqArgs.put("builder", AspireClient.serializeValue(getHandle())); - getClient().invokeCapability("Aspire.Hosting/withoutHttpsCertificate", reqArgs); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); return this; } @@ -6384,6 +6975,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public ContainerResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public ContainerResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -6715,6 +7322,40 @@ public ContainerResource withMergeRouteMiddleware(String path, String method, St } +// ===== ContainerTargetPlatform.java ===== +// ContainerTargetPlatform.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** ContainerTargetPlatform enum. */ +public enum ContainerTargetPlatform implements WireValueEnum { + LINUX_AMD64("LinuxAmd64"), + LINUX_ARM64("LinuxArm64"), + ALL_LINUX("AllLinux"), + LINUX_ARM("LinuxArm"), + LINUX386("Linux386"), + WINDOWS_AMD64("WindowsAmd64"), + WINDOWS_ARM64("WindowsArm64"); + + private final String value; + + ContainerTargetPlatform(String value) { + this.value = value; + } + + public String getValue() { return value; } + + public static ContainerTargetPlatform fromValue(String value) { + for (ContainerTargetPlatform e : values()) { + if (e.value.equals(value)) return e; + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} + // ===== CreateBuilderOptions.java ===== // CreateBuilderOptions.java - GENERATED CODE - DO NOT EDIT @@ -6787,6 +7428,150 @@ public Map toMap() { } } +// ===== CreateCertificateFileOptions.java ===== +// CreateCertificateFileOptions.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Options for CreateCertificateFile. */ +public final class CreateCertificateFileOptions { + private String contents; + private String sourcePath; + private Double owner; + private Double group; + private Double mode; + private Boolean continueOnError; + + public String getContents() { return contents; } + public CreateCertificateFileOptions contents(String value) { + this.contents = value; + return this; + } + + public String getSourcePath() { return sourcePath; } + public CreateCertificateFileOptions sourcePath(String value) { + this.sourcePath = value; + return this; + } + + public Double getOwner() { return owner; } + public CreateCertificateFileOptions owner(Double value) { + this.owner = value; + return this; + } + + public Double getGroup() { return group; } + public CreateCertificateFileOptions group(Double value) { + this.group = value; + return this; + } + + public Double getMode() { return mode; } + public CreateCertificateFileOptions mode(Double value) { + this.mode = value; + return this; + } + + public Boolean getContinueOnError() { return continueOnError; } + public CreateCertificateFileOptions continueOnError(Boolean value) { + this.continueOnError = value; + return this; + } + +} + +// ===== CreateDirectoryOptions.java ===== +// CreateDirectoryOptions.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Options for CreateDirectory. */ +public final class CreateDirectoryOptions { + private Double owner; + private Double group; + private Double mode; + + public Double getOwner() { return owner; } + public CreateDirectoryOptions owner(Double value) { + this.owner = value; + return this; + } + + public Double getGroup() { return group; } + public CreateDirectoryOptions group(Double value) { + this.group = value; + return this; + } + + public Double getMode() { return mode; } + public CreateDirectoryOptions mode(Double value) { + this.mode = value; + return this; + } + +} + +// ===== CreateFileOptions.java ===== +// CreateFileOptions.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Options for CreateFile. */ +public final class CreateFileOptions { + private String contents; + private String sourcePath; + private Double owner; + private Double group; + private Double mode; + private Boolean continueOnError; + + public String getContents() { return contents; } + public CreateFileOptions contents(String value) { + this.contents = value; + return this; + } + + public String getSourcePath() { return sourcePath; } + public CreateFileOptions sourcePath(String value) { + this.sourcePath = value; + return this; + } + + public Double getOwner() { return owner; } + public CreateFileOptions owner(Double value) { + this.owner = value; + return this; + } + + public Double getGroup() { return group; } + public CreateFileOptions group(Double value) { + this.group = value; + return this; + } + + public Double getMode() { return mode; } + public CreateFileOptions mode(Double value) { + this.mode = value; + return this; + } + + public Boolean getContinueOnError() { return continueOnError; } + public CreateFileOptions continueOnError(Boolean value) { + this.continueOnError = value; + return this; + } + +} + // ===== DistributedApplication.java ===== // DistributedApplication.java - GENERATED CODE - DO NOT EDIT @@ -7547,6 +8332,29 @@ public DotnetToolResource withRequiredCommand(String command, String helpLink) { return this; } + public DotnetToolResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public DotnetToolResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public DotnetToolResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -8286,6 +9094,38 @@ public DotnetToolResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public DotnetToolResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public DotnetToolResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public DotnetToolResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -8630,6 +9470,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public DotnetToolResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public DotnetToolResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -9743,6 +10599,29 @@ public ExecutableResource withRequiredCommand(String command, String helpLink) { return this; } + public ExecutableResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public ExecutableResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public ExecutableResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -10482,6 +11361,38 @@ public ExecutableResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public ExecutableResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public ExecutableResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public ExecutableResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -10826,6 +11737,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public ExecutableResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public ExecutableResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -11198,6 +12125,14 @@ public class ExecuteCommandContext extends HandleWrapperBase { super(handle, client); } + /** The service provider. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services", reqArgs); + return (IServiceProvider) result; + } + /** The resource name. */ public String resourceName() { Map reqArgs = new HashMap<>(); @@ -11383,6 +12318,29 @@ public ExternalServiceResource withRequiredCommand(String command, String helpLi return this; } + public ExternalServiceResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public ExternalServiceResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public ExternalServiceResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -11562,6 +12520,22 @@ public ExternalServiceResource withProcessCommandFactory(String commandName, Str return this; } + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public ExternalServiceResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public ExternalServiceResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -11797,6 +12771,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public ExternalServiceResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public ExternalServiceResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -12539,6 +13529,86 @@ public static HttpCommandResultMode fromValue(String value) { } } +// ===== HttpsCertificateConfigurationCallbackAnnotationContext.java ===== +// HttpsCertificateConfigurationCallbackAnnotationContext.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext. */ +public class HttpsCertificateConfigurationCallbackAnnotationContext extends HandleWrapperBase { + HttpsCertificateConfigurationCallbackAnnotationContext(Handle handle, AspireClient client) { + super(handle, client); + } + + /** Gets the `DistributedApplicationExecutionContext` for this session. */ + public DistributedApplicationExecutionContext executionContext() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.executionContext", reqArgs); + return (DistributedApplicationExecutionContext) result; + } + + /** Gets the resource to which the annotation is applied. */ + public IResource resource() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.resource", reqArgs); + return (IResource) result; + } + + /** A value provider that will resolve to a path to the certificate file. */ + public ReferenceExpression certificatePath() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.certificatePath", reqArgs); + return (ReferenceExpression) result; + } + + /** A value provider that will resolve to a path to the private key for the certificate. */ + public ReferenceExpression keyPath() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.keyPath", reqArgs); + return (ReferenceExpression) result; + } + + /** A value provider that will resolve to a path to a PFX file for the key pair. */ + public ReferenceExpression pfxPath() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.pfxPath", reqArgs); + return (ReferenceExpression) result; + } + + /** Gets the `CancellationToken` that can be used to cancel the operation. */ + public CancellationToken cancellationToken() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.cancellationToken", reqArgs); + return (CancellationToken) result; + } + + /** Gets the editor used to manipulate the command-line arguments in polyglot callbacks. */ + public CommandLineArgsEditor arguments() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.arguments", reqArgs); + return (CommandLineArgsEditor) result; + } + + /** Gets the editor used to set environment variables in polyglot callbacks. */ + public EnvironmentEditor environment() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.environment", reqArgs); + return (EnvironmentEditor) result; + } + +} + // ===== HttpsCertificateExecutionConfigurationContext.java ===== // HttpsCertificateExecutionConfigurationContext.java - GENERATED CODE - DO NOT EDIT @@ -12689,6 +13759,54 @@ public Map toMap() { } } +// ===== HttpsEndpointUpdateCallbackContext.java ===== +// HttpsEndpointUpdateCallbackContext.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext. */ +public class HttpsEndpointUpdateCallbackContext extends HandleWrapperBase { + HttpsEndpointUpdateCallbackContext(Handle handle, AspireClient client) { + super(handle, client); + } + + /** Gets the `IServiceProvider` instance from the application. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.services", reqArgs); + return (IServiceProvider) result; + } + + /** Gets the `IResource` that is being configured for HTTPS. */ + public IResource resource() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.resource", reqArgs); + return (IResource) result; + } + + /** Gets the `DistributedApplicationModel` instance. */ + public DistributedApplicationModel model() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.model", reqArgs); + return (DistributedApplicationModel) result; + } + + /** Gets the `CancellationToken` for the operation. */ + public CancellationToken cancellationToken() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.cancellationToken", reqArgs); + return (CancellationToken) result; + } + +} + // ===== IAspireStore.java ===== // IAspireStore.java - GENERATED CODE - DO NOT EDIT @@ -14560,6 +15678,14 @@ public CancellationToken cancellationToken() { return (CancellationToken) result; } + /** Gets the service provider for resolving services during validation. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting/InputsDialogValidationContext.services", reqArgs); + return (IServiceProvider) result; + } + /** Adds a validation error for the input with the specified name. */ public void addValidationError(String inputName, String errorMessage) { Map reqArgs = new HashMap<>(); @@ -14969,6 +16095,29 @@ public ParameterResource withRequiredCommand(String command, String helpLink) { return this; } + public ParameterResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public ParameterResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public ParameterResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -15148,6 +16297,22 @@ public ParameterResource withProcessCommandFactory(String commandName, String di return this; } + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public ParameterResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public ParameterResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -15383,6 +16548,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public ParameterResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public ParameterResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -16452,6 +17633,29 @@ public ProjectResource withRequiredCommand(String command, String helpLink) { return this; } + public ProjectResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public ProjectResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public ProjectResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -17205,6 +18409,38 @@ public ProjectResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public ProjectResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public ProjectResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public ProjectResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -17558,6 +18794,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public ProjectResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public ProjectResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -18219,64 +19471,153 @@ private static Object extractValueProvider(Object value) { import java.util.*; import java.util.function.*; -/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ReferenceExpressionBuilder. */ -public class ReferenceExpressionBuilder extends HandleWrapperBase { - ReferenceExpressionBuilder(Handle handle, AspireClient client) { +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ReferenceExpressionBuilder. */ +public class ReferenceExpressionBuilder extends HandleWrapperBase { + ReferenceExpressionBuilder(Handle handle, AspireClient client) { + super(handle, client); + } + + /** Indicates whether the expression is empty. */ + public boolean isEmpty() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ReferenceExpressionBuilder.isEmpty", reqArgs); + return (Boolean) result; + } + + /** Appends a literal value to the expression. */ + public void appendLiteral(String value) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + getClient().invokeCapability("Aspire.Hosting.ApplicationModel/appendLiteral", reqArgs); + } + + public void appendFormatted(String value) { + appendFormatted(value, null); + } + + /** Appends a formatted value to the expression. */ + public void appendFormatted(String value, String format) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("value", AspireClient.serializeValue(value)); + if (format != null) { + reqArgs.put("format", AspireClient.serializeValue(format)); + } + getClient().invokeCapability("Aspire.Hosting.ApplicationModel/appendFormatted", reqArgs); + } + + public void appendValueProvider(Object valueProvider) { + appendValueProvider(valueProvider, null); + } + + /** Appends a value provider to the reference expression */ + public void appendValueProvider(Object valueProvider, String format) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("valueProvider", AspireClient.serializeValue(valueProvider)); + if (format != null) { + reqArgs.put("format", AspireClient.serializeValue(format)); + } + getClient().invokeCapability("Aspire.Hosting.ApplicationModel/appendValueProvider", reqArgs); + } + + /** Builds the reference expression */ + public ReferenceExpression build() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/build", reqArgs); + return (ReferenceExpression) result; + } + +} + +// ===== RequiredCommandValidationContext.java ===== +// RequiredCommandValidationContext.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext. */ +public class RequiredCommandValidationContext extends HandleWrapperBase { + RequiredCommandValidationContext(Handle handle, AspireClient client) { super(handle, client); } - /** Indicates whether the expression is empty. */ - public boolean isEmpty() { + /** Gets the resolved full path to the command executable. */ + public String resolvedPath() { Map reqArgs = new HashMap<>(); reqArgs.put("context", AspireClient.serializeValue(getHandle())); - var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ReferenceExpressionBuilder.isEmpty", reqArgs); - return (Boolean) result; + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.resolvedPath", reqArgs); + return (String) result; } - /** Appends a literal value to the expression. */ - public void appendLiteral(String value) { + /** Gets the service provider for accessing application services. */ + public IServiceProvider services() { Map reqArgs = new HashMap<>(); reqArgs.put("context", AspireClient.serializeValue(getHandle())); - reqArgs.put("value", AspireClient.serializeValue(value)); - getClient().invokeCapability("Aspire.Hosting.ApplicationModel/appendLiteral", reqArgs); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.services", reqArgs); + return (IServiceProvider) result; } - public void appendFormatted(String value) { - appendFormatted(value, null); + /** Gets a cancellation token that can be used to cancel the validation. */ + public CancellationToken cancellationToken() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.cancellationToken", reqArgs); + return (CancellationToken) result; } - /** Appends a formatted value to the expression. */ - public void appendFormatted(String value, String format) { + /** Creates a successful validation result. */ + public RequiredCommandValidationResult success() { Map reqArgs = new HashMap<>(); reqArgs.put("context", AspireClient.serializeValue(getHandle())); - reqArgs.put("value", AspireClient.serializeValue(value)); - if (format != null) { - reqArgs.put("format", AspireClient.serializeValue(format)); - } - getClient().invokeCapability("Aspire.Hosting.ApplicationModel/appendFormatted", reqArgs); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.success", reqArgs); + return (RequiredCommandValidationResult) result; } - public void appendValueProvider(Object valueProvider) { - appendValueProvider(valueProvider, null); + /** Creates a failed validation result with the specified message. */ + public RequiredCommandValidationResult failure(String validationMessage) { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + reqArgs.put("validationMessage", AspireClient.serializeValue(validationMessage)); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.failure", reqArgs); + return (RequiredCommandValidationResult) result; } - /** Appends a value provider to the reference expression */ - public void appendValueProvider(Object valueProvider, String format) { +} + +// ===== RequiredCommandValidationResult.java ===== +// RequiredCommandValidationResult.java - GENERATED CODE - DO NOT EDIT + +package aspire; + +import java.util.*; +import java.util.function.*; + +/** Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult. */ +public class RequiredCommandValidationResult extends HandleWrapperBase { + RequiredCommandValidationResult(Handle handle, AspireClient client) { + super(handle, client); + } + + /** Gets a value indicating whether the command validation succeeded. */ + public boolean isValid() { Map reqArgs = new HashMap<>(); reqArgs.put("context", AspireClient.serializeValue(getHandle())); - reqArgs.put("valueProvider", AspireClient.serializeValue(valueProvider)); - if (format != null) { - reqArgs.put("format", AspireClient.serializeValue(format)); - } - getClient().invokeCapability("Aspire.Hosting.ApplicationModel/appendValueProvider", reqArgs); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.isValid", reqArgs); + return (Boolean) result; } - /** Builds the reference expression */ - public ReferenceExpression build() { + /** Gets an optional validation message describing why validation failed. */ + public String validationMessage() { Map reqArgs = new HashMap<>(); reqArgs.put("context", AspireClient.serializeValue(getHandle())); - var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/build", reqArgs); - return (ReferenceExpression) result; + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.validationMessage", reqArgs); + return result == null ? null : (String) result; } } @@ -19332,6 +20673,30 @@ public TestDatabaseResource withContainerFiles(String destinationPath, String so return this; } + public TestDatabaseResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback) { + return withContainerFilesCallback(destinationPath, callback, null); + } + + /** Creates or updates files and folders in a container using entries produced by a callback. */ + public TestDatabaseResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("destinationPath", AspireClient.serializeValue(destinationPath)); + var callbackId = getClient().registerCallback(args -> { + var arg1 = (ContainerFileSystemCallbackContext) args[0]; + var arg2 = CancellationToken.fromValue(args[1]); + return AspireClient.awaitValue(callback.invoke(arg1, arg2)); + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + if (options != null) { + reqArgs.put("options", AspireClient.serializeValue(options)); + } + getClient().invokeCapability("Aspire.Hosting/withContainerFilesCallback", reqArgs); + return this; + } + public TestDatabaseResource withDockerfileBuilder(String contextPath, AspireAction1 callback) { return withDockerfileBuilder(contextPath, callback, null); } @@ -19454,6 +20819,29 @@ public TestDatabaseResource withRequiredCommand(String command, String helpLink) return this; } + public TestDatabaseResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public TestDatabaseResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public TestDatabaseResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -20193,6 +21581,38 @@ public TestDatabaseResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public TestDatabaseResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public TestDatabaseResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public TestDatabaseResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -20563,6 +21983,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public TestDatabaseResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public TestDatabaseResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -21390,6 +22826,30 @@ public TestRedisResource withContainerFiles(String destinationPath, String sourc return this; } + public TestRedisResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback) { + return withContainerFilesCallback(destinationPath, callback, null); + } + + /** Creates or updates files and folders in a container using entries produced by a callback. */ + public TestRedisResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("destinationPath", AspireClient.serializeValue(destinationPath)); + var callbackId = getClient().registerCallback(args -> { + var arg1 = (ContainerFileSystemCallbackContext) args[0]; + var arg2 = CancellationToken.fromValue(args[1]); + return AspireClient.awaitValue(callback.invoke(arg1, arg2)); + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + if (options != null) { + reqArgs.put("options", AspireClient.serializeValue(options)); + } + getClient().invokeCapability("Aspire.Hosting/withContainerFilesCallback", reqArgs); + return this; + } + public TestRedisResource withDockerfileBuilder(String contextPath, AspireAction1 callback) { return withDockerfileBuilder(contextPath, callback, null); } @@ -21512,6 +22972,29 @@ public TestRedisResource withRequiredCommand(String command, String helpLink) { return this; } + public TestRedisResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public TestRedisResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public TestRedisResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -22278,6 +23761,38 @@ public TestRedisResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public TestRedisResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public TestRedisResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public TestRedisResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -22664,6 +24179,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public TestRedisResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + public TestDatabaseResource addTestChildDatabase(String name) { return addTestChildDatabase(name, null); } @@ -23520,6 +25051,30 @@ public TestVaultResource withContainerFiles(String destinationPath, String sourc return this; } + public TestVaultResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback) { + return withContainerFilesCallback(destinationPath, callback, null); + } + + /** Creates or updates files and folders in a container using entries produced by a callback. */ + public TestVaultResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("destinationPath", AspireClient.serializeValue(destinationPath)); + var callbackId = getClient().registerCallback(args -> { + var arg1 = (ContainerFileSystemCallbackContext) args[0]; + var arg2 = CancellationToken.fromValue(args[1]); + return AspireClient.awaitValue(callback.invoke(arg1, arg2)); + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + if (options != null) { + reqArgs.put("options", AspireClient.serializeValue(options)); + } + getClient().invokeCapability("Aspire.Hosting/withContainerFilesCallback", reqArgs); + return this; + } + public TestVaultResource withDockerfileBuilder(String contextPath, AspireAction1 callback) { return withDockerfileBuilder(contextPath, callback, null); } @@ -23642,6 +25197,29 @@ public TestVaultResource withRequiredCommand(String command, String helpLink) { return this; } + public TestVaultResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback) { + return withRequiredCommandValidation(command, validationCallback, null); + } + + /** Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. */ + public TestVaultResource withRequiredCommandValidation(String command, AspireFunc1 validationCallback, String helpLink) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + reqArgs.put("command", AspireClient.serializeValue(command)); + var validationCallbackId = getClient().registerCallback(args -> { + var arg = (RequiredCommandValidationContext) args[0]; + return AspireClient.awaitValue(validationCallback.invoke(arg)); + }); + if (validationCallbackId != null) { + reqArgs.put("validationCallback", validationCallbackId); + } + if (helpLink != null) { + reqArgs.put("helpLink", AspireClient.serializeValue(helpLink)); + } + getClient().invokeCapability("Aspire.Hosting/withRequiredCommandValidation", reqArgs); + return this; + } + /** Configures a resource to use a session lifetime. */ public TestVaultResource withSessionLifetime() { Map reqArgs = new HashMap<>(); @@ -24381,6 +25959,38 @@ public TestVaultResource withoutHttpsCertificate() { return this; } + /** Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. */ + public TestVaultResource withHttpsCertificateConfiguration(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (HttpsCertificateConfigurationCallbackAnnotationContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withHttpsCertificateConfiguration", reqArgs); + return this; + } + + /** Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. */ + public TestVaultResource subscribeHttpsEndpointsUpdate(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var obj = (HttpsEndpointUpdateCallbackContext) args[0]; + callback.invoke(obj); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", reqArgs); + return this; + } + /** Adds a relationship to another resource using its builder. */ public TestVaultResource withRelationship(IResource resourceBuilder, String type) { Map reqArgs = new HashMap<>(); @@ -24751,6 +26361,22 @@ public IExecutionConfigurationBuilder createExecutionConfiguration() { return (IExecutionConfigurationBuilder) result; } + /** Configures container build options for a compute resource using an async callback. */ + public TestVaultResource withContainerBuildOptions(AspireAction1 callback) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + var callbackId = getClient().registerCallback(args -> { + var arg = (ContainerBuildOptionsCallbackContext) args[0]; + callback.invoke(arg); + return null; + }); + if (callbackId != null) { + reqArgs.put("callback", callbackId); + } + getClient().invokeCapability("Aspire.Hosting/withContainerBuildOptions", reqArgs); + return this; + } + /** Adds an optional string parameter */ public TestVaultResource withOptionalString(WithOptionalStringOptions options) { var value = options == null ? null : options.getValue(); @@ -25113,6 +26739,14 @@ public UpdateCommandStateResourceSnapshot resourceSnapshot() { return UpdateCommandStateResourceSnapshot.fromMap((Map) result); } + /** The service provider. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services", reqArgs); + return (IServiceProvider) result; + } + } // ===== UpdateCommandStateResourceSnapshot.java ===== @@ -26036,7 +27670,12 @@ public WithVolumeOptions isReadOnly(Boolean value) { .aspire/modules/CompleteTaskMarkdownOptions.java .aspire/modules/CompleteTaskOptions.java .aspire/modules/ConnectionStringAvailableEvent.java +.aspire/modules/ContainerBuildOptionsCallbackContext.java +.aspire/modules/ContainerFileSystemCallbackContext.java +.aspire/modules/ContainerFileSystemItem.java .aspire/modules/ContainerFilesOptions.java +.aspire/modules/ContainerImageDestination.java +.aspire/modules/ContainerImageFormat.java .aspire/modules/ContainerImagePushOptions.java .aspire/modules/ContainerImagePushOptionsCallbackContext.java .aspire/modules/ContainerImageReference.java @@ -26046,7 +27685,11 @@ public WithVolumeOptions isReadOnly(Boolean value) { .aspire/modules/ContainerPortReference.java .aspire/modules/ContainerRegistryResource.java .aspire/modules/ContainerResource.java +.aspire/modules/ContainerTargetPlatform.java .aspire/modules/CreateBuilderOptions.java +.aspire/modules/CreateCertificateFileOptions.java +.aspire/modules/CreateDirectoryOptions.java +.aspire/modules/CreateFileOptions.java .aspire/modules/DistributedApplication.java .aspire/modules/DistributedApplicationEventSubscription.java .aspire/modules/DistributedApplicationExecutionContext.java @@ -26080,9 +27723,11 @@ public WithVolumeOptions isReadOnly(Boolean value) { .aspire/modules/HttpCommandPrepareRequestContext.java .aspire/modules/HttpCommandRequestExportData.java .aspire/modules/HttpCommandResultMode.java +.aspire/modules/HttpsCertificateConfigurationCallbackAnnotationContext.java .aspire/modules/HttpsCertificateExecutionConfigurationContext.java .aspire/modules/HttpsCertificateExecutionConfigurationExportData.java .aspire/modules/HttpsCertificateInfo.java +.aspire/modules/HttpsEndpointUpdateCallbackContext.java .aspire/modules/IAspireStore.java .aspire/modules/IComputeEnvironmentResource.java .aspire/modules/IComputeResource.java @@ -26143,6 +27788,8 @@ public WithVolumeOptions isReadOnly(Boolean value) { .aspire/modules/ReferenceEnvironmentInjectionOptions.java .aspire/modules/ReferenceExpression.java .aspire/modules/ReferenceExpressionBuilder.java +.aspire/modules/RequiredCommandValidationContext.java +.aspire/modules/RequiredCommandValidationResult.java .aspire/modules/ResourceBuilderBase.java .aspire/modules/ResourceCommandService.java .aspire/modules/ResourceCommandState.java diff --git a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py index b3f4b2efaf8..bfb494c1115 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py +++ b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py @@ -1508,10 +1508,16 @@ def _validate_dict_types(args: typing.Any, arg_types: typing.Any) -> bool: CommandResultFormat = typing.Literal["Text", "Json", "Markdown"] +ContainerImageDestination = typing.Literal["Registry", "Archive"] + +ContainerImageFormat = typing.Literal["Docker", "Oci"] + ContainerLifetime = typing.Literal["Session", "Persistent"] ContainerMountType = typing.Literal["BindMount", "Volume"] +ContainerTargetPlatform = typing.Literal["LinuxAmd64", "LinuxArm64", "AllLinux", "LinuxArm", "Linux386", "WindowsAmd64", "WindowsArm64"] + DistributedApplicationOperation = typing.Literal["Run", "Publish"] EndpointProperty = typing.Literal["Url", "Host", "IPV4Host", "Port", "Scheme", "TargetPort", "HostAndPort", "TlsEnabled"] @@ -1555,6 +1561,12 @@ class DockerfileBaseImageParameters(typing.TypedDict, total=False): runtime_image: str +class RequiredCommandParameters(typing.TypedDict, total=False): + command: typing.Required[str] + validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult] + help_link: str + + class CommandParameters(typing.TypedDict, total=False): name: typing.Required[str] display_name: typing.Required[str] @@ -1639,6 +1651,12 @@ class ContainerFilesParameters(typing.TypedDict, total=False): options: ContainerFilesOptions +class ContainerFilesCallbackParameters(typing.TypedDict, total=False): + destination_path: typing.Required[str] + callback: typing.Required[typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]]] + options: ContainerFilesOptions + + class DockerfileBuilderParameters(typing.TypedDict, total=False): context_path: typing.Required[str] callback: typing.Required[typing.Callable[[DockerfileBuilderCallbackContext], None]] @@ -3549,6 +3567,279 @@ def services(self) -> AbstractServiceProvider: return typing.cast(AbstractServiceProvider, result) +class ContainerBuildOptionsCallbackContext: + """Type class for ContainerBuildOptionsCallbackContext.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"ContainerBuildOptionsCallbackContext(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + @_cached_property + def resource(self) -> AbstractResource: + """Gets the resource being built.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.resource', + {'context': self._handle} + ) + return typing.cast(AbstractResource, result) + + @_cached_property + def services(self) -> AbstractServiceProvider: + """Gets the service provider.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + + @_cached_property + def logger(self) -> AbstractLogger: + """Gets the logger instance.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.logger', + {'context': self._handle} + ) + return typing.cast(AbstractLogger, result) + + def cancel(self) -> None: + """Cancel the operation.""" + token: CancellationToken = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.cancellationToken', + {'context': self._handle} + ) + token.cancel() + + @_cached_property + def execution_context(self) -> DistributedApplicationExecutionContext: + """Gets the distributed application execution context.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.executionContext', + {'context': self._handle} + ) + return typing.cast(DistributedApplicationExecutionContext, result) + + @_uncached_property + def destination(self) -> ContainerImageDestination | None: + """Gets or sets the destination for the container image.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.destination', + {'context': self._handle} + ) + return typing.cast(ContainerImageDestination | None, result) + + @destination.setter + def destination(self, value: ContainerImageDestination | None) -> None: + """Sets the Destination property""" + self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setDestination', + {'context': self._handle, 'value': value} + ) + + @_uncached_property + def output_path(self) -> str | None: + """Gets or sets the output path for the container archive.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.outputPath', + {'context': self._handle} + ) + return typing.cast(str | None, result) + + @output_path.setter + def output_path(self, value: str | None) -> None: + """Sets the OutputPath property""" + self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setOutputPath', + {'context': self._handle, 'value': value} + ) + + @_uncached_property + def image_format(self) -> ContainerImageFormat | None: + """Gets or sets the container image format.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.imageFormat', + {'context': self._handle} + ) + return typing.cast(ContainerImageFormat | None, result) + + @image_format.setter + def image_format(self, value: ContainerImageFormat | None) -> None: + """Sets the ImageFormat property""" + self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setImageFormat', + {'context': self._handle, 'value': value} + ) + + @_uncached_property + def target_platform(self) -> ContainerTargetPlatform | None: + """Gets or sets the target platform for the container.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.targetPlatform', + {'context': self._handle} + ) + return typing.cast(ContainerTargetPlatform | None, result) + + @target_platform.setter + def target_platform(self, value: ContainerTargetPlatform | None) -> None: + """Sets the TargetPlatform property""" + self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setTargetPlatform', + {'context': self._handle, 'value': value} + ) + + @_uncached_property + def local_image_name(self) -> str | None: + """Gets or sets the local image name for the built container.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageName', + {'context': self._handle} + ) + return typing.cast(str | None, result) + + @local_image_name.setter + def local_image_name(self, value: str | None) -> None: + """Sets the LocalImageName property""" + self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageName', + {'context': self._handle, 'value': value} + ) + + @_uncached_property + def local_image_tag(self) -> str | None: + """Gets or sets the local image tag for the built container.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageTag', + {'context': self._handle} + ) + return typing.cast(str | None, result) + + @local_image_tag.setter + def local_image_tag(self, value: str | None) -> None: + """Sets the LocalImageTag property""" + self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageTag', + {'context': self._handle, 'value': value} + ) + + +class ContainerFileSystemCallbackContext: + """Type class for ContainerFileSystemCallbackContext.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"ContainerFileSystemCallbackContext(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + @_cached_property + def services(self) -> AbstractServiceProvider: + """A `IServiceProvider` that can be used to resolve services in the callback.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + + @_cached_property + def model(self) -> AbstractResource: + """The app model resource the callback is associated with.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.model', + {'context': self._handle} + ) + return typing.cast(AbstractResource, result) + + def create_file(self, name: str, *, contents: str | None = None, source_path: str | None = None, owner: int | None = None, group: int | None = None, mode: int | None = None, continue_on_error: bool | None = None) -> ContainerFileSystemItem: + """Creates a container file entry with inline contents or a host source path.""" + rpc_args: dict[str, typing.Any] = {'context': self._handle} + rpc_args['name'] = name + if contents is not None: + rpc_args['contents'] = contents + if source_path is not None: + rpc_args['sourcePath'] = source_path + if owner is not None: + rpc_args['owner'] = owner + if group is not None: + rpc_args['group'] = group + if mode is not None: + rpc_args['mode'] = mode + if continue_on_error is not None: + rpc_args['continueOnError'] = continue_on_error + result = self._client.invoke_capability( + 'Aspire.Hosting/createFile', + rpc_args, + ) + return typing.cast(ContainerFileSystemItem, result) + + def create_certificate_file(self, name: str, *, contents: str | None = None, source_path: str | None = None, owner: int | None = None, group: int | None = None, mode: int | None = None, continue_on_error: bool | None = None) -> ContainerFileSystemItem: + """Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink.""" + rpc_args: dict[str, typing.Any] = {'context': self._handle} + rpc_args['name'] = name + if contents is not None: + rpc_args['contents'] = contents + if source_path is not None: + rpc_args['sourcePath'] = source_path + if owner is not None: + rpc_args['owner'] = owner + if group is not None: + rpc_args['group'] = group + if mode is not None: + rpc_args['mode'] = mode + if continue_on_error is not None: + rpc_args['continueOnError'] = continue_on_error + result = self._client.invoke_capability( + 'Aspire.Hosting/createCertificateFile', + rpc_args, + ) + return typing.cast(ContainerFileSystemItem, result) + + def create_dir(self, name: str, entries: typing.Iterable[ContainerFileSystemItem], *, owner: int | None = None, group: int | None = None, mode: int | None = None) -> ContainerFileSystemItem: + """Creates a container directory entry containing the specified child entries.""" + rpc_args: dict[str, typing.Any] = {'context': self._handle} + rpc_args['name'] = name + rpc_args['entries'] = entries + if owner is not None: + rpc_args['owner'] = owner + if group is not None: + rpc_args['group'] = group + if mode is not None: + rpc_args['mode'] = mode + result = self._client.invoke_capability( + 'Aspire.Hosting/createDirectory', + rpc_args, + ) + return typing.cast(ContainerFileSystemItem, result) + + +class ContainerFileSystemItem: + """Type class for ContainerFileSystemItem.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"ContainerFileSystemItem(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + class ContainerImagePushOptions: """Type class for ContainerImagePushOptions.""" @@ -4791,6 +5082,15 @@ def handle(self) -> Handle: """The underlying object reference handle.""" return self._handle + @_cached_property + def services(self) -> AbstractServiceProvider: + """The service provider.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + @_cached_property def resource_name(self) -> str: """The resource name.""" @@ -4878,6 +5178,144 @@ def arguments(self) -> InteractionInputCollection: return typing.cast(InteractionInputCollection, result) +class HttpsCertificateConfigurationCallbackAnnotationContext: + """Type class for HttpsCertificateConfigurationCallbackAnnotationContext.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"HttpsCertificateConfigurationCallbackAnnotationContext(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + @_cached_property + def execution_context(self) -> DistributedApplicationExecutionContext: + """Gets the `DistributedApplicationExecutionContext` for this session.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.executionContext', + {'context': self._handle} + ) + return typing.cast(DistributedApplicationExecutionContext, result) + + @_cached_property + def resource(self) -> AbstractResource: + """Gets the resource to which the annotation is applied.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.resource', + {'context': self._handle} + ) + return typing.cast(AbstractResource, result) + + @_cached_property + def certificate_path(self) -> ReferenceExpression: + """A value provider that will resolve to a path to the certificate file.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.certificatePath', + {'context': self._handle} + ) + return typing.cast(ReferenceExpression, result) + + @_cached_property + def key_path(self) -> ReferenceExpression: + """A value provider that will resolve to a path to the private key for the certificate.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.keyPath', + {'context': self._handle} + ) + return typing.cast(ReferenceExpression, result) + + @_cached_property + def pfx_path(self) -> ReferenceExpression: + """A value provider that will resolve to a path to a PFX file for the key pair.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.pfxPath', + {'context': self._handle} + ) + return typing.cast(ReferenceExpression, result) + + def cancel(self) -> None: + """Cancel the operation.""" + token: CancellationToken = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.cancellationToken', + {'context': self._handle} + ) + token.cancel() + + @_cached_property + def arguments(self) -> CommandLineArgsEditor: + """Gets the editor used to manipulate the command-line arguments in polyglot callbacks.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.arguments', + {'context': self._handle} + ) + return typing.cast(CommandLineArgsEditor, result) + + @_cached_property + def env(self) -> EnvironmentEditor: + """Gets the editor used to set environment variables in polyglot callbacks.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.environment', + {'context': self._handle} + ) + return typing.cast(EnvironmentEditor, result) + + +class HttpsEndpointUpdateCallbackContext: + """Type class for HttpsEndpointUpdateCallbackContext.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"HttpsEndpointUpdateCallbackContext(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + @_cached_property + def services(self) -> AbstractServiceProvider: + """Gets the `IServiceProvider` instance from the application.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + + @_cached_property + def resource(self) -> AbstractResource: + """Gets the `IResource` that is being configured for HTTPS.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.resource', + {'context': self._handle} + ) + return typing.cast(AbstractResource, result) + + @_cached_property + def model(self) -> DistributedApplicationModel: + """Gets the `DistributedApplicationModel` instance.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.model', + {'context': self._handle} + ) + return typing.cast(DistributedApplicationModel, result) + + def cancel(self) -> None: + """Cancel the operation.""" + token: CancellationToken = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.cancellationToken', + {'context': self._handle} + ) + token.cancel() + + class InitializeResourceEvent: """Type class for InitializeResourceEvent.""" @@ -4971,6 +5409,15 @@ def cancel(self) -> None: ) token.cancel() + @_cached_property + def services(self) -> AbstractServiceProvider: + """Gets the service provider for resolving services during validation.""" + result = self._client.invoke_capability( + 'Aspire.Hosting/InputsDialogValidationContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + def add_validation_error(self, input_name: str, error_message: str) -> None: """Adds a validation error for the input with the specified name.""" rpc_args: dict[str, typing.Any] = {'context': self._handle} @@ -5584,6 +6031,101 @@ def build(self) -> ReferenceExpression: return typing.cast(ReferenceExpression, result) +class RequiredCommandValidationContext: + """Type class for RequiredCommandValidationContext.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"RequiredCommandValidationContext(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + @_cached_property + def resolved_path(self) -> str: + """Gets the resolved full path to the command executable.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.resolvedPath', + {'context': self._handle} + ) + return typing.cast(str, result) + + @_cached_property + def services(self) -> AbstractServiceProvider: + """Gets the service provider for accessing application services.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + + def cancel(self) -> None: + """Cancel the operation.""" + token: CancellationToken = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.cancellationToken', + {'context': self._handle} + ) + token.cancel() + + def success(self) -> RequiredCommandValidationResult: + """Creates a successful validation result.""" + rpc_args: dict[str, typing.Any] = {'context': self._handle} + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.success', + rpc_args, + ) + return typing.cast(RequiredCommandValidationResult, result) + + def failure(self, validation_message: str) -> RequiredCommandValidationResult: + """Creates a failed validation result with the specified message.""" + rpc_args: dict[str, typing.Any] = {'context': self._handle} + rpc_args['validationMessage'] = validation_message + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.failure', + rpc_args, + ) + return typing.cast(RequiredCommandValidationResult, result) + + +class RequiredCommandValidationResult: + """Type class for RequiredCommandValidationResult.""" + + def __init__(self, handle: Handle, client: AspireClient) -> None: + self._handle = handle + self._client = client + + def __repr__(self) -> str: + return f"RequiredCommandValidationResult(handle={self._handle.handle_id})" + + @_uncached_property + def handle(self) -> Handle: + """The underlying object reference handle.""" + return self._handle + + @_cached_property + def is_valid(self) -> bool: + """Gets a value indicating whether the command validation succeeded.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.isValid', + {'context': self._handle} + ) + return typing.cast(bool, result) + + @_cached_property + def validation_message(self) -> str | None: + """Gets an optional validation message describing why validation failed.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.validationMessage', + {'context': self._handle} + ) + return typing.cast(str | None, result) + + class ResourceCommandService: """Type class for ResourceCommandService.""" @@ -6251,6 +6793,15 @@ def resource_snapshot(self) -> UpdateCommandStateResourceSnapshot: ) return typing.cast(UpdateCommandStateResourceSnapshot, result) + @_cached_property + def services(self) -> AbstractServiceProvider: + """The service provider.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + # ============================================================================ # Interface Classes @@ -6268,7 +6819,7 @@ def with_dockerfile_base_image(self, *, build_image: str | None = None, runtime_ """Configures custom base images for generated Dockerfiles.""" @abc.abstractmethod - def with_required_command(self, command: str, *, help_link: str | None = None) -> typing.Self: + def with_required_command(self, command: str, *, validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult] | None = None, help_link: str | None = None) -> typing.Self: """Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start.""" @abc.abstractmethod @@ -6323,6 +6874,10 @@ def with_process_command(self, command_name: str, display_name: str, options: Pr def with_process_command_factory(self, command_name: str, display_name: str, create_process_spec: typing.Callable[[ExecuteCommandContext], ProcessCommandSpecExportData], *, options: ProcessCommandResultExportOptions | None = None) -> typing.Self: """Adds a command to the resource that starts a local process created by a callback when invoked.""" + @abc.abstractmethod + def subscribe_https_endpoints_update(self, callback: typing.Callable[[HttpsEndpointUpdateCallbackContext], None]) -> typing.Self: + """Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup.""" + @abc.abstractmethod def with_relationship(self, resource_builder: AbstractResource, type: str) -> typing.Self: """Adds a relationship to another resource using its builder.""" @@ -6383,6 +6938,10 @@ def on_resource_ready(self, callback: typing.Callable[[ResourceReadyEvent], None def create_execution_config(self) -> AbstractExecutionConfigurationBuilder: """Creates an execution configuration builder for the specified resource.""" + @abc.abstractmethod + def with_container_build_options(self, callback: typing.Callable[[ContainerBuildOptionsCallbackContext], None]) -> typing.Self: + """Configures container build options for a compute resource using an async callback.""" + @abc.abstractmethod def with_optional_string(self, *, value: str | None = None, enabled: bool = True) -> typing.Self: """Adds an optional string parameter""" @@ -6639,6 +7198,10 @@ def with_https_developer_certificate(self, *, password: ParameterResource | None def without_https_certificate(self) -> typing.Self: """Disable HTTPS/TLS server certificate configuration for the resource. No HTTPS/TLS termination configuration will be applied.""" + @abc.abstractmethod + def with_https_certificate_config(self, callback: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]) -> typing.Self: + """Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication.""" + @abc.abstractmethod def test_with_env_callback(self, callback: typing.Callable[[TestEnvironmentContext], None]) -> typing.Self: """Configures environment with callback (test version)""" @@ -6689,7 +7252,7 @@ class _BaseResourceKwargs(typing.TypedDict, total=False): container_registry: AbstractResource dockerfile_base_image: DockerfileBaseImageParameters | typing.Literal[True] - required_command: str | tuple[str, str] + required_command: str | RequiredCommandParameters session_lifetime: typing.Literal[True] persistent_lifetime: typing.Literal[True] lifetime_of: AbstractResource @@ -6703,6 +7266,7 @@ class _BaseResourceKwargs(typing.TypedDict, total=False): command: tuple[str, str, typing.Callable[[ExecuteCommandContext], ExecuteCommandResult]] | CommandParameters process_command: tuple[str, str, ProcessCommandExportOptions] process_command_factory: tuple[str, str, typing.Callable[[ExecuteCommandContext], ProcessCommandSpecExportData]] | ProcessCommandFactoryParameters + subscribe_https_endpoints_update: typing.Callable[[HttpsEndpointUpdateCallbackContext], None] relationship: tuple[AbstractResource, str] parent_relationship: AbstractResource child_relationship: AbstractResource @@ -6716,6 +7280,7 @@ class _BaseResourceKwargs(typing.TypedDict, total=False): on_resource_stopped: typing.Callable[[ResourceStoppedEvent], None] on_initialize_resource: typing.Callable[[InitializeResourceEvent], None] on_resource_ready: typing.Callable[[ResourceReadyEvent], None] + container_build_options: typing.Callable[[ContainerBuildOptionsCallbackContext], None] optional_string: OptionalStringParameters | typing.Literal[True] config: TestConfigDto created_at: datetime.datetime @@ -6773,14 +7338,17 @@ def with_dockerfile_base_image(self, *, build_image: str | None = None, runtime_ self._handle = self._wrap_builder(result) return self - def with_required_command(self, command: str, *, help_link: str | None = None) -> typing.Self: + def with_required_command(self, command: str, *, validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult] | None = None, help_link: str | None = None) -> typing.Self: """Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} rpc_args['command'] = command + if validation_callback is not None: + rpc_args['validationCallback'] = self._client.register_callback(validation_callback) if help_link is not None: rpc_args['helpLink'] = help_link + capability_id = 'Aspire.Hosting/withRequiredCommandValidation' if validation_callback is not None else 'Aspire.Hosting/withRequiredCommand' result = self._client.invoke_capability( - 'Aspire.Hosting/withRequiredCommand', + capability_id, rpc_args, ) self._handle = self._wrap_builder(result) @@ -6938,6 +7506,17 @@ def with_process_command_factory(self, command_name: str, display_name: str, cre self._handle = self._wrap_builder(result) return self + def subscribe_https_endpoints_update(self, callback: typing.Callable[[HttpsEndpointUpdateCallbackContext], None]) -> typing.Self: + """Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['callback'] = self._client.register_callback(callback) + result = self._client.invoke_capability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + def with_relationship(self, resource_builder: AbstractResource, type: str) -> typing.Self: """Adds a relationship to another resource using its builder.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} @@ -7112,6 +7691,17 @@ def create_execution_config(self) -> AbstractExecutionConfigurationBuilder: ) return typing.cast(AbstractExecutionConfigurationBuilder, result) + def with_container_build_options(self, callback: typing.Callable[[ContainerBuildOptionsCallbackContext], None]) -> typing.Self: + """Configures container build options for a compute resource using an async callback.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['callback'] = self._client.register_callback(callback) + result = self._client.invoke_capability( + 'Aspire.Hosting/withContainerBuildOptions', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + def with_optional_string(self, *, value: str | None = None, enabled: bool = True) -> typing.Self: """Adds an optional string parameter""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} @@ -7358,13 +7948,15 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack rpc_args: dict[str, typing.Any] = {"builder": handle} rpc_args["command"] = typing.cast(str, _required_command) handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withRequiredCommand', rpc_args)) - elif _validate_tuple_types(_required_command, (str, str)): + elif _validate_dict_types(_required_command, RequiredCommandParameters): rpc_args: dict[str, typing.Any] = {"builder": handle} - rpc_args["command"] = typing.cast(tuple[str, str], _required_command)[0] - rpc_args["helpLink"] = typing.cast(tuple[str, str], _required_command)[1] - handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withRequiredCommand', rpc_args)) + rpc_args["command"] = typing.cast(RequiredCommandParameters, _required_command)["command"] + rpc_args["validationCallback"] = client.register_callback(typing.cast(RequiredCommandParameters, _required_command).get("validation_callback")) + rpc_args["helpLink"] = typing.cast(RequiredCommandParameters, _required_command).get("help_link") + capability_id = 'Aspire.Hosting/withRequiredCommandValidation' if "validation_callback" in _required_command else 'Aspire.Hosting/withRequiredCommand' + handle = self._wrap_builder(client.invoke_capability(capability_id, rpc_args)) else: - raise TypeError("Invalid type for option 'required_command'. Expected: str or (str, str)") + raise TypeError("Invalid type for option 'required_command'. Expected: str or RequiredCommandParameters") if _session_lifetime := kwargs.pop("session_lifetime", None): if _session_lifetime is True: rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -7478,6 +8070,13 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withProcessCommandFactory', rpc_args)) else: raise TypeError("Invalid type for option 'process_command_factory'. Expected: (str, str, Callable[[ExecuteCommandContext], ProcessCommandSpecExportData]) or ProcessCommandFactoryParameters") + if _subscribe_https_endpoints_update := kwargs.pop("subscribe_https_endpoints_update", None): + if _validate_type(_subscribe_https_endpoints_update, typing.Callable[[HttpsEndpointUpdateCallbackContext], None]): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["callback"] = client.register_callback(typing.cast(typing.Callable[[HttpsEndpointUpdateCallbackContext], None], _subscribe_https_endpoints_update)) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/subscribeHttpsEndpointsUpdate', rpc_args)) + else: + raise TypeError("Invalid type for option 'subscribe_https_endpoints_update'. Expected: Callable[[HttpsEndpointUpdateCallbackContext], None]") if _relationship := kwargs.pop("relationship", None): if _validate_tuple_types(_relationship, (AbstractResource, str)): rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -7587,6 +8186,13 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/onResourceReady', rpc_args)) else: raise TypeError("Invalid type for option 'on_resource_ready'. Expected: Callable[[ResourceReadyEvent], None]") + if _container_build_options := kwargs.pop("container_build_options", None): + if _validate_type(_container_build_options, typing.Callable[[ContainerBuildOptionsCallbackContext], None]): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["callback"] = client.register_callback(typing.cast(typing.Callable[[ContainerBuildOptionsCallbackContext], None], _container_build_options)) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withContainerBuildOptions', rpc_args)) + else: + raise TypeError("Invalid type for option 'container_build_options'. Expected: Callable[[ContainerBuildOptionsCallbackContext], None]") if _optional_string := kwargs.pop("optional_string", None): if _validate_dict_types(_optional_string, OptionalStringParameters): rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -7786,6 +8392,7 @@ class ContainerResourceKwargs(_BaseResourceKwargs, total=False): build_secret: tuple[str, ParameterResource] container_certificate_paths: ContainerCertificatePathsParameters | typing.Literal[True] container_files: tuple[str, str] | ContainerFilesParameters + container_files_callback: tuple[str, typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]]] | ContainerFilesCallbackParameters dockerfile_builder: tuple[str, typing.Callable[[DockerfileBuilderCallbackContext], None]] | DockerfileBuilderParameters container_network_alias: str mcp_server: McpServerParameters | typing.Literal[True] @@ -7815,6 +8422,7 @@ class ContainerResourceKwargs(_BaseResourceKwargs, total=False): certificate_trust_scope: CertificateTrustScope https_developer_certificate: ParameterResource | typing.Literal[True] without_https_certificate: typing.Literal[True] + https_certificate_config: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None] compute_env: AbstractComputeEnvironmentResource http_probe: ProbeType | HttpProbeParameters image_push_options: typing.Callable[[ContainerImagePushOptionsCallbackContext], None] @@ -8039,6 +8647,20 @@ def with_container_files(self, destination_path: str, source_path: str, *, optio self._handle = self._wrap_builder(result) return self + def with_container_files_callback(self, destination_path: str, callback: typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]], *, options: ContainerFilesOptions | None = None) -> typing.Self: + """Creates or updates files and folders in a container using entries produced by a callback.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['destinationPath'] = destination_path + rpc_args['callback'] = self._client.register_callback(callback) + if options is not None: + rpc_args['options'] = options + result = self._client.invoke_capability( + 'Aspire.Hosting/withContainerFilesCallback', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + def with_dockerfile_builder(self, context_path: str, callback: typing.Callable[[DockerfileBuilderCallbackContext], None], *, stage: str | None = None) -> typing.Self: """Configures the resource to use a programmatically generated Dockerfile""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} @@ -8437,6 +9059,17 @@ def without_https_certificate(self) -> typing.Self: self._handle = self._wrap_builder(result) return self + def with_https_certificate_config(self, callback: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]) -> typing.Self: + """Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['callback'] = self._client.register_callback(callback) + result = self._client.invoke_capability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + def with_compute_env(self, compute_env_resource: AbstractComputeEnvironmentResource) -> typing.Self: """Configures the compute environment for the compute resource.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} @@ -8712,6 +9345,20 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withContainerFiles', rpc_args)) else: raise TypeError("Invalid type for option 'container_files'. Expected: (str, str) or ContainerFilesParameters") + if _container_files_callback := kwargs.pop("container_files_callback", None): + if _validate_tuple_types(_container_files_callback, (str, typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]])): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["destinationPath"] = typing.cast(tuple[str, typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]]], _container_files_callback)[0] + rpc_args["callback"] = client.register_callback(typing.cast(tuple[str, typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]]], _container_files_callback)[1]) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withContainerFilesCallback', rpc_args)) + elif _validate_dict_types(_container_files_callback, ContainerFilesCallbackParameters): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["destinationPath"] = typing.cast(ContainerFilesCallbackParameters, _container_files_callback)["destination_path"] + rpc_args["callback"] = client.register_callback(typing.cast(ContainerFilesCallbackParameters, _container_files_callback)["callback"]) + rpc_args["options"] = typing.cast(ContainerFilesCallbackParameters, _container_files_callback).get("options") + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withContainerFilesCallback', rpc_args)) + else: + raise TypeError("Invalid type for option 'container_files_callback'. Expected: (str, Callable[[ContainerFileSystemCallbackContext, CancellationToken], Iterable[ContainerFileSystemItem]]) or ContainerFilesCallbackParameters") if _dockerfile_builder := kwargs.pop("dockerfile_builder", None): if _validate_tuple_types(_dockerfile_builder, (str, typing.Callable[[DockerfileBuilderCallbackContext], None])): rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -9006,6 +9653,13 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withoutHttpsCertificate', rpc_args)) else: raise TypeError("Invalid type for option 'without_https_certificate'. Expected: Literal[True]") + if _https_certificate_config := kwargs.pop("https_certificate_config", None): + if _validate_type(_https_certificate_config, typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["callback"] = client.register_callback(typing.cast(typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None], _https_certificate_config)) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withHttpsCertificateConfiguration', rpc_args)) + else: + raise TypeError("Invalid type for option 'https_certificate_config'. Expected: Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]") if _compute_env := kwargs.pop("compute_env", None): if _validate_type(_compute_env, AbstractComputeEnvironmentResource): rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -9122,6 +9776,7 @@ class ProjectResourceKwargs(_BaseResourceKwargs, total=False): certificate_trust_scope: CertificateTrustScope https_developer_certificate: ParameterResource | typing.Literal[True] without_https_certificate: typing.Literal[True] + https_certificate_config: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None] compute_env: AbstractComputeEnvironmentResource http_probe: ProbeType | HttpProbeParameters image_push_options: typing.Callable[[ContainerImagePushOptionsCallbackContext], None] @@ -9546,6 +10201,17 @@ def without_https_certificate(self) -> typing.Self: self._handle = self._wrap_builder(result) return self + def with_https_certificate_config(self, callback: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]) -> typing.Self: + """Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['callback'] = self._client.register_callback(callback) + result = self._client.invoke_capability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + def with_compute_env(self, compute_env_resource: AbstractComputeEnvironmentResource) -> typing.Self: """Configures the compute environment for the compute resource.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} @@ -9958,6 +10624,13 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withoutHttpsCertificate', rpc_args)) else: raise TypeError("Invalid type for option 'without_https_certificate'. Expected: Literal[True]") + if _https_certificate_config := kwargs.pop("https_certificate_config", None): + if _validate_type(_https_certificate_config, typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["callback"] = client.register_callback(typing.cast(typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None], _https_certificate_config)) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withHttpsCertificateConfiguration', rpc_args)) + else: + raise TypeError("Invalid type for option 'https_certificate_config'. Expected: Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]") if _compute_env := kwargs.pop("compute_env", None): if _validate_type(_compute_env, AbstractComputeEnvironmentResource): rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -10081,6 +10754,7 @@ class ExecutableResourceKwargs(_BaseResourceKwargs, total=False): certificate_trust_scope: CertificateTrustScope https_developer_certificate: ParameterResource | typing.Literal[True] without_https_certificate: typing.Literal[True] + https_certificate_config: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None] compute_env: AbstractComputeEnvironmentResource http_probe: ProbeType | HttpProbeParameters image_push_options: typing.Callable[[ContainerImagePushOptionsCallbackContext], None] @@ -10492,6 +11166,17 @@ def without_https_certificate(self) -> typing.Self: self._handle = self._wrap_builder(result) return self + def with_https_certificate_config(self, callback: typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]) -> typing.Self: + """Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['callback'] = self._client.register_callback(callback) + result = self._client.invoke_capability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + def with_compute_env(self, compute_env_resource: AbstractComputeEnvironmentResource) -> typing.Self: """Configures the compute environment for the compute resource.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} @@ -10883,6 +11568,13 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withoutHttpsCertificate', rpc_args)) else: raise TypeError("Invalid type for option 'without_https_certificate'. Expected: Literal[True]") + if _https_certificate_config := kwargs.pop("https_certificate_config", None): + if _validate_type(_https_certificate_config, typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["callback"] = client.register_callback(typing.cast(typing.Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None], _https_certificate_config)) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withHttpsCertificateConfiguration', rpc_args)) + else: + raise TypeError("Invalid type for option 'https_certificate_config'. Expected: Callable[[HttpsCertificateConfigurationCallbackAnnotationContext], None]") if _compute_env := kwargs.pop("compute_env", None): if _validate_type(_compute_env, AbstractComputeEnvironmentResource): rpc_args: dict[str, typing.Any] = {"builder": handle} @@ -11608,6 +12300,9 @@ def create_builder( _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.CommandLineArgsCallbackContext", CommandLineArgsCallbackContext) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.CommandLineArgsEditor", CommandLineArgsEditor) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent", ConnectionStringAvailableEvent) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext", ContainerBuildOptionsCallbackContext) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext", ContainerFileSystemCallbackContext) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemItem", ContainerFileSystemItem) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptions", ContainerImagePushOptions) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptionsCallbackContext", ContainerImagePushOptionsCallbackContext) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImageReference", ContainerImageReference) @@ -11629,6 +12324,8 @@ def create_builder( _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.Ats.EventingSubscriberRegistrationContext", EventingSubscriberRegistrationContext) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ExecuteCommandContext", ExecuteCommandContext) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpCommandPrepareRequestContext", HttpCommandPrepareRequestContext) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext", HttpsCertificateConfigurationCallbackAnnotationContext) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext", HttpsEndpointUpdateCallbackContext) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.InitializeResourceEvent", InitializeResourceEvent) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.InputsDialogValidationContext", InputsDialogValidationContext) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.InteractionInputCollection", InteractionInputCollection) @@ -11642,6 +12339,8 @@ def create_builder( _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.Pipelines.PipelineSummary", PipelineSummary) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ProjectResourceOptions", ProjectResourceOptions) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ReferenceExpressionBuilder", ReferenceExpressionBuilder) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext", RequiredCommandValidationContext) +_register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult", RequiredCommandValidationResult) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceCommandService", ResourceCommandService) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceEndpointsAllocatedEvent", ResourceEndpointsAllocatedEvent) _register_handle_wrapper("Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceLoggerService", ResourceLoggerService) diff --git a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs index 76d64d6e0b2..a899148bb33 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs +++ b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs @@ -297,6 +297,78 @@ impl std::fmt::Display for ProbeType { } } +/// ContainerImageDestination +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ContainerImageDestination { + #[default] + #[serde(rename = "Registry")] + Registry, + #[serde(rename = "Archive")] + Archive, +} + +impl std::fmt::Display for ContainerImageDestination { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Registry => write!(f, "Registry"), + Self::Archive => write!(f, "Archive"), + } + } +} + +/// ContainerImageFormat +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ContainerImageFormat { + #[default] + #[serde(rename = "Docker")] + Docker, + #[serde(rename = "Oci")] + Oci, +} + +impl std::fmt::Display for ContainerImageFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Docker => write!(f, "Docker"), + Self::Oci => write!(f, "Oci"), + } + } +} + +/// ContainerTargetPlatform +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ContainerTargetPlatform { + #[default] + #[serde(rename = "LinuxAmd64")] + LinuxAmd64, + #[serde(rename = "LinuxArm64")] + LinuxArm64, + #[serde(rename = "AllLinux")] + AllLinux, + #[serde(rename = "LinuxArm")] + LinuxArm, + #[serde(rename = "Linux386")] + Linux386, + #[serde(rename = "WindowsAmd64")] + WindowsAmd64, + #[serde(rename = "WindowsArm64")] + WindowsArm64, +} + +impl std::fmt::Display for ContainerTargetPlatform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::LinuxAmd64 => write!(f, "LinuxAmd64"), + Self::LinuxArm64 => write!(f, "LinuxArm64"), + Self::AllLinux => write!(f, "AllLinux"), + Self::LinuxArm => write!(f, "LinuxArm"), + Self::Linux386 => write!(f, "Linux386"), + Self::WindowsAmd64 => write!(f, "WindowsAmd64"), + Self::WindowsArm64 => write!(f, "WindowsArm64"), + } + } +} + /// EndpointProperty #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum EndpointProperty { @@ -1908,6 +1980,21 @@ impl CSharpAppResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -2417,6 +2504,28 @@ impl CSharpAppResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -2683,6 +2792,17 @@ impl CSharpAppResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -3098,6 +3218,332 @@ impl ConnectionStringAvailableEvent { } } +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext +pub struct ContainerBuildOptionsCallbackContext { + handle: Handle, + client: Arc, +} + +impl HasHandle for ContainerBuildOptionsCallbackContext { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl ContainerBuildOptionsCallbackContext { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } + + /// Gets the resource being built. + pub fn resource(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.resource", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + + /// Gets the service provider. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + + /// Gets the logger instance. + pub fn logger(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.logger", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ILogger::new(handle, self.client.clone())) + } + + /// Gets the cancellation token. + pub fn cancellation_token(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.cancellationToken", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(CancellationToken::new(handle, self.client.clone())) + } + + /// Gets the distributed application execution context. + pub fn execution_context(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.executionContext", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(DistributedApplicationExecutionContext::new(handle, self.client.clone())) + } + + /// Gets or sets the destination for the container image. + pub fn destination(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.destination", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Sets the Destination property + pub fn set_destination(&self, value: ContainerImageDestination) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("value".to_string(), serde_json::to_value(&value).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setDestination", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerBuildOptionsCallbackContext::new(handle, self.client.clone())) + } + + /// Gets or sets the output path for the container archive. + pub fn output_path(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.outputPath", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Sets the OutputPath property + pub fn set_output_path(&self, value: &str) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("value".to_string(), serde_json::to_value(&value).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setOutputPath", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerBuildOptionsCallbackContext::new(handle, self.client.clone())) + } + + /// Gets or sets the container image format. + pub fn image_format(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.imageFormat", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Sets the ImageFormat property + pub fn set_image_format(&self, value: ContainerImageFormat) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("value".to_string(), serde_json::to_value(&value).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setImageFormat", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerBuildOptionsCallbackContext::new(handle, self.client.clone())) + } + + /// Gets or sets the target platform for the container. + pub fn target_platform(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.targetPlatform", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Sets the TargetPlatform property + pub fn set_target_platform(&self, value: ContainerTargetPlatform) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("value".to_string(), serde_json::to_value(&value).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setTargetPlatform", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerBuildOptionsCallbackContext::new(handle, self.client.clone())) + } + + /// Gets or sets the local image name for the built container. + pub fn local_image_name(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageName", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Sets the LocalImageName property + pub fn set_local_image_name(&self, value: &str) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("value".to_string(), serde_json::to_value(&value).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageName", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerBuildOptionsCallbackContext::new(handle, self.client.clone())) + } + + /// Gets or sets the local image tag for the built container. + pub fn local_image_tag(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageTag", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Sets the LocalImageTag property + pub fn set_local_image_tag(&self, value: &str) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("value".to_string(), serde_json::to_value(&value).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageTag", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerBuildOptionsCallbackContext::new(handle, self.client.clone())) + } +} + +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext +pub struct ContainerFileSystemCallbackContext { + handle: Handle, + client: Arc, +} + +impl HasHandle for ContainerFileSystemCallbackContext { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl ContainerFileSystemCallbackContext { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } + + /// A `IServiceProvider` that can be used to resolve services in the callback. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + + /// The app model resource the callback is associated with. + pub fn model(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.model", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + + /// Creates a container file entry with inline contents or a host source path. + pub fn create_file(&self, name: &str, contents: Option<&str>, source_path: Option<&str>, owner: Option, group: Option, mode: Option, continue_on_error: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("name".to_string(), serde_json::to_value(&name).unwrap_or(Value::Null)); + if let Some(ref v) = contents { + args.insert("contents".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = source_path { + args.insert("sourcePath".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = owner { + args.insert("owner".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = group { + args.insert("group".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = mode { + args.insert("mode".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = continue_on_error { + args.insert("continueOnError".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/createFile", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerFileSystemItem::new(handle, self.client.clone())) + } + + /// Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. + pub fn create_certificate_file(&self, name: &str, contents: Option<&str>, source_path: Option<&str>, owner: Option, group: Option, mode: Option, continue_on_error: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("name".to_string(), serde_json::to_value(&name).unwrap_or(Value::Null)); + if let Some(ref v) = contents { + args.insert("contents".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = source_path { + args.insert("sourcePath".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = owner { + args.insert("owner".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = group { + args.insert("group".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = mode { + args.insert("mode".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = continue_on_error { + args.insert("continueOnError".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/createCertificateFile", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerFileSystemItem::new(handle, self.client.clone())) + } + + /// Creates a container directory entry containing the specified child entries. + pub fn create_directory(&self, name: &str, entries: Vec, owner: Option, group: Option, mode: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("name".to_string(), serde_json::to_value(&name).unwrap_or(Value::Null)); + let handles: Vec = entries.iter().map(|item| item.handle().to_json()).collect(); + args.insert("entries".to_string(), Value::Array(handles)); + if let Some(ref v) = owner { + args.insert("owner".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = group { + args.insert("group".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = mode { + args.insert("mode".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/createDirectory", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerFileSystemItem::new(handle, self.client.clone())) + } +} + +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemItem +pub struct ContainerFileSystemItem { + handle: Handle, + client: Arc, +} + +impl HasHandle for ContainerFileSystemItem { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl ContainerFileSystemItem { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } +} + /// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptions pub struct ContainerImagePushOptions { handle: Handle, @@ -3420,6 +3866,21 @@ impl ContainerRegistryResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -3566,6 +4027,17 @@ impl ContainerRegistryResource { Ok(IResource::new(handle, self.client.clone())) } + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -3739,6 +4211,17 @@ impl ContainerRegistryResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -4228,6 +4711,21 @@ impl ContainerResource { Ok(ContainerResource::new(handle, self.client.clone())) } + /// Creates or updates files and folders in a container using entries produced by a callback. + pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("destinationPath".to_string(), serde_json::to_value(&destination_path).unwrap_or(Value::Null)); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + if let Some(ref v) = options { + args.insert("options".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withContainerFilesCallback", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerResource::new(handle, self.client.clone())) + } + /// Configures the resource to use a programmatically generated Dockerfile pub fn with_dockerfile_builder(&self, context_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, stage: Option<&str>) -> Result> { let mut args: HashMap = HashMap::new(); @@ -4317,6 +4815,21 @@ impl ContainerResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -4815,6 +5328,28 @@ impl ContainerResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -5087,6 +5622,17 @@ impl ContainerResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -6122,6 +6668,21 @@ impl DotnetToolResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -6603,21 +7164,43 @@ impl DotnetToolResource { pub fn with_https_developer_certificate(&self, password: Option<&ParameterResource>) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); - if let Some(ref v) = password { - args.insert("password".to_string(), v.handle().to_json()); - } - let result = self.client.invoke_capability("Aspire.Hosting/withParameterHttpsDeveloperCertificate", args)?; + if let Some(ref v) = password { + args.insert("password".to_string(), v.handle().to_json()); + } + let result = self.client.invoke_capability("Aspire.Hosting/withParameterHttpsDeveloperCertificate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Disable HTTPS/TLS server certificate configuration for the resource. No HTTPS/TLS termination configuration will be applied. + pub fn without_https_certificate(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting/withoutHttpsCertificate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; let handle: Handle = serde_json::from_value(result)?; Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } - /// Disable HTTPS/TLS server certificate configuration for the resource. No HTTPS/TLS termination configuration will be applied. - pub fn without_https_certificate(&self) -> Result> { + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); - let result = self.client.invoke_capability("Aspire.Hosting/withoutHttpsCertificate", args)?; + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; let handle: Handle = serde_json::from_value(result)?; - Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + Ok(IResource::new(handle, self.client.clone())) } /// Adds a relationship to another resource using its builder. @@ -6876,6 +7459,17 @@ impl DotnetToolResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -7901,6 +8495,21 @@ impl ExecutableResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -8399,6 +9008,28 @@ impl ExecutableResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -8655,6 +9286,17 @@ impl ExecutableResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -8954,6 +9596,15 @@ impl ExecuteCommandContext { &self.client } + /// The service provider. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + /// The resource name. pub fn resource_name(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -9071,6 +9722,21 @@ impl ExternalServiceResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -9217,6 +9883,17 @@ impl ExternalServiceResource { Ok(IResource::new(handle, self.client.clone())) } + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -9390,6 +10067,17 @@ impl ExternalServiceResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -9704,6 +10392,163 @@ impl HttpCommandPrepareRequestContext { } } +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext +pub struct HttpsCertificateConfigurationCallbackAnnotationContext { + handle: Handle, + client: Arc, +} + +impl HasHandle for HttpsCertificateConfigurationCallbackAnnotationContext { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl HttpsCertificateConfigurationCallbackAnnotationContext { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } + + /// Gets the `DistributedApplicationExecutionContext` for this session. + pub fn execution_context(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.executionContext", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(DistributedApplicationExecutionContext::new(handle, self.client.clone())) + } + + /// Gets the resource to which the annotation is applied. + pub fn resource(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.resource", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + + /// A value provider that will resolve to a path to the certificate file. + pub fn certificate_path(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.certificatePath", args)?; + Ok(serde_json::from_value(result)?) + } + + /// A value provider that will resolve to a path to the private key for the certificate. + pub fn key_path(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.keyPath", args)?; + Ok(serde_json::from_value(result)?) + } + + /// A value provider that will resolve to a path to a PFX file for the key pair. + pub fn pfx_path(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.pfxPath", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Gets the `CancellationToken` that can be used to cancel the operation. + pub fn cancellation_token(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.cancellationToken", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(CancellationToken::new(handle, self.client.clone())) + } + + /// Gets the editor used to manipulate the command-line arguments in polyglot callbacks. + pub fn arguments(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.arguments", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(CommandLineArgsEditor::new(handle, self.client.clone())) + } + + /// Gets the editor used to set environment variables in polyglot callbacks. + pub fn environment(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.environment", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(EnvironmentEditor::new(handle, self.client.clone())) + } +} + +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext +pub struct HttpsEndpointUpdateCallbackContext { + handle: Handle, + client: Arc, +} + +impl HasHandle for HttpsEndpointUpdateCallbackContext { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl HttpsEndpointUpdateCallbackContext { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } + + /// Gets the `IServiceProvider` instance from the application. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + + /// Gets the `IResource` that is being configured for HTTPS. + pub fn resource(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.resource", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + + /// Gets the `DistributedApplicationModel` instance. + pub fn model(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.model", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(DistributedApplicationModel::new(handle, self.client.clone())) + } + + /// Gets the `CancellationToken` for the operation. + pub fn cancellation_token(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.cancellationToken", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(CancellationToken::new(handle, self.client.clone())) + } +} + /// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.IAspireStore pub struct IAspireStore { handle: Handle, @@ -11616,6 +12461,15 @@ impl InputsDialogValidationContext { Ok(CancellationToken::new(handle, self.client.clone())) } + /// Gets the service provider for resolving services during validation. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting/InputsDialogValidationContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + /// Adds a validation error for the input with the specified name. pub fn add_validation_error(&self, input_name: &str, error_message: &str) -> Result<(), Box> { let mut args: HashMap = HashMap::new(); @@ -11809,6 +12663,21 @@ impl ParameterResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -11955,6 +12824,17 @@ impl ParameterResource { Ok(IResource::new(handle, self.client.clone())) } + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -12128,6 +13008,17 @@ impl ParameterResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -12952,15 +13843,30 @@ impl ProjectResource { Ok(ProjectResource::new(handle, self.client.clone())) } - /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start. - pub fn with_required_command(&self, command: &str, help_link: Option<&str>) -> Result> { + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start. + pub fn with_required_command(&self, command: &str, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommand", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); if let Some(ref v) = help_link { args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); } - let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommand", args)?; + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; let handle: Handle = serde_json::from_value(result)?; Ok(IResource::new(handle, self.client.clone())) } @@ -13474,6 +14380,28 @@ impl ProjectResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -13740,6 +14668,17 @@ impl ProjectResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -14169,6 +15108,119 @@ impl ReferenceExpressionBuilder { } } +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext +pub struct RequiredCommandValidationContext { + handle: Handle, + client: Arc, +} + +impl HasHandle for RequiredCommandValidationContext { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl RequiredCommandValidationContext { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } + + /// Gets the resolved full path to the command executable. + pub fn resolved_path(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.resolvedPath", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Gets the service provider for accessing application services. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + + /// Gets a cancellation token that can be used to cancel the validation. + pub fn cancellation_token(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.cancellationToken", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(CancellationToken::new(handle, self.client.clone())) + } + + /// Creates a successful validation result. + pub fn success(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.success", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(RequiredCommandValidationResult::new(handle, self.client.clone())) + } + + /// Creates a failed validation result with the specified message. + pub fn failure(&self, validation_message: &str) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + args.insert("validationMessage".to_string(), serde_json::to_value(&validation_message).unwrap_or(Value::Null)); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.failure", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(RequiredCommandValidationResult::new(handle, self.client.clone())) + } +} + +/// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult +pub struct RequiredCommandValidationResult { + handle: Handle, + client: Arc, +} + +impl HasHandle for RequiredCommandValidationResult { + fn handle(&self) -> &Handle { + &self.handle + } +} + +impl RequiredCommandValidationResult { + pub fn new(handle: Handle, client: Arc) -> Self { + Self { handle, client } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn client(&self) -> &Arc { + &self.client + } + + /// Gets a value indicating whether the command validation succeeded. + pub fn is_valid(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.isValid", args)?; + Ok(serde_json::from_value(result)?) + } + + /// Gets an optional validation message describing why validation failed. + pub fn validation_message(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.validationMessage", args)?; + Ok(serde_json::from_value(result)?) + } +} + /// Wrapper for Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceCommandService pub struct ResourceCommandService { handle: Handle, @@ -14966,6 +16018,21 @@ impl TestDatabaseResource { Ok(ContainerResource::new(handle, self.client.clone())) } + /// Creates or updates files and folders in a container using entries produced by a callback. + pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("destinationPath".to_string(), serde_json::to_value(&destination_path).unwrap_or(Value::Null)); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + if let Some(ref v) = options { + args.insert("options".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withContainerFilesCallback", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerResource::new(handle, self.client.clone())) + } + /// Configures the resource to use a programmatically generated Dockerfile pub fn with_dockerfile_builder(&self, context_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, stage: Option<&str>) -> Result> { let mut args: HashMap = HashMap::new(); @@ -15055,6 +16122,21 @@ impl TestDatabaseResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -15553,6 +16635,28 @@ impl TestDatabaseResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -15825,6 +16929,17 @@ impl TestDatabaseResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -16471,6 +17586,21 @@ impl TestRedisResource { Ok(ContainerResource::new(handle, self.client.clone())) } + /// Creates or updates files and folders in a container using entries produced by a callback. + pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("destinationPath".to_string(), serde_json::to_value(&destination_path).unwrap_or(Value::Null)); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + if let Some(ref v) = options { + args.insert("options".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withContainerFilesCallback", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerResource::new(handle, self.client.clone())) + } + /// Configures the resource to use a programmatically generated Dockerfile pub fn with_dockerfile_builder(&self, context_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, stage: Option<&str>) -> Result> { let mut args: HashMap = HashMap::new(); @@ -16560,6 +17690,21 @@ impl TestRedisResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -17078,6 +18223,28 @@ impl TestRedisResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -17361,6 +18528,17 @@ impl TestRedisResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a child database to a test Redis resource pub fn add_test_child_database(&self, name: &str, database_name: Option<&str>) -> Result> { let mut args: HashMap = HashMap::new(); @@ -18082,6 +19260,21 @@ impl TestVaultResource { Ok(ContainerResource::new(handle, self.client.clone())) } + /// Creates or updates files and folders in a container using entries produced by a callback. + pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("destinationPath".to_string(), serde_json::to_value(&destination_path).unwrap_or(Value::Null)); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + if let Some(ref v) = options { + args.insert("options".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withContainerFilesCallback", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(ContainerResource::new(handle, self.client.clone())) + } + /// Configures the resource to use a programmatically generated Dockerfile pub fn with_dockerfile_builder(&self, context_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, stage: Option<&str>) -> Result> { let mut args: HashMap = HashMap::new(); @@ -18171,6 +19364,21 @@ impl TestVaultResource { Ok(IResource::new(handle, self.client.clone())) } + /// Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + pub fn with_required_command_validation(&self, command: &str, validation_callback: impl Fn(Vec) -> Value + Send + Sync + 'static, help_link: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + args.insert("command".to_string(), serde_json::to_value(&command).unwrap_or(Value::Null)); + let callback_id = register_callback(validation_callback); + args.insert("validationCallback".to_string(), Value::String(callback_id)); + if let Some(ref v) = help_link { + args.insert("helpLink".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting/withRequiredCommandValidation", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Configures a resource to use a session lifetime. pub fn with_session_lifetime(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -18669,6 +19877,28 @@ impl TestVaultResource { Ok(IResourceWithEnvironment::new(handle, self.client.clone())) } + /// Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + pub fn with_https_certificate_configuration(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withHttpsCertificateConfiguration", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResourceWithEnvironment::new(handle, self.client.clone())) + } + + /// Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + pub fn subscribe_https_endpoints_update(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/subscribeHttpsEndpointsUpdate", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds a relationship to another resource using its builder. pub fn with_relationship(&self, resource_builder: &IResource, r#type: &str) -> Result> { let mut args: HashMap = HashMap::new(); @@ -18941,6 +20171,17 @@ impl TestVaultResource { Ok(IExecutionConfigurationBuilder::new(handle, self.client.clone())) } + /// Configures container build options for a compute resource using an async callback. + pub fn with_container_build_options(&self, callback: impl Fn(Vec) -> Value + Send + Sync + 'static) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + let callback_id = register_callback(callback); + args.insert("callback".to_string(), Value::String(callback_id)); + let result = self.client.invoke_capability("Aspire.Hosting/withContainerBuildOptions", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IResource::new(handle, self.client.clone())) + } + /// Adds an optional string parameter pub fn with_optional_string(&self, value: Option<&str>, enabled: Option) -> Result> { let mut args: HashMap = HashMap::new(); @@ -19257,6 +20498,15 @@ impl UpdateCommandStateContext { let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.resourceSnapshot", args)?; Ok(serde_json::from_value(result)?) } + + /// The service provider. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } } // ============================================================================ diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts index bb75379ad07..8550367520f 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts @@ -112,6 +112,21 @@ type CommandLineArgsEditorHandle = Handle<'Aspire.Hosting/Aspire.Hosting.Applica /** The {@link ConnectionStringAvailableEvent} is raised when a connection string becomes available for a resource. */ type ConnectionStringAvailableEventHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent'>; +/** Context for configuring container build options via a callback. */ +type ContainerBuildOptionsCallbackContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext'>; + +/** Represents the context for a `ContainerFileSystemCallbackAnnotation` callback. */ +type ContainerFileSystemCallbackContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext'>; + +/** + * Represents a base class for file system entries in a container. + * + * Exported to ATS as an opaque handle type. Polyglot app hosts never construct or inspect these + * directly; they create concrete entries through the factory methods on + * `ContainerFileSystemCallbackContext` and pass the resulting handles back via the callback. + */ +type ContainerFileSystemItemHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemItem'>; + /** * Represents options for pushing container images to a registry. * @@ -191,6 +206,12 @@ type ExecuteCommandContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.Applica /** Provides context for HTTP command prepare-request callbacks in polyglot app hosts. */ type HttpCommandPrepareRequestContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpCommandPrepareRequestContext'>; +/** Context provided to a `HttpsCertificateConfigurationCallbackAnnotation` callback. */ +type HttpsCertificateConfigurationCallbackAnnotationContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext'>; + +/** Context provided to the callback of `SubscribeHttpsEndpointsUpdate``1` when an HTTPS certificate is determined to be available for the resource. */ +type HttpsEndpointUpdateCallbackContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext'>; + /** * Represents a store for managing files in the Aspire hosting environment that can be reused across runs. * @@ -270,6 +291,12 @@ type ReferenceExpressionHandle = Handle<'Aspire.Hosting/Aspire.Hosting.Applicati /** A builder for creating {@link ReferenceExpression} instances. */ type ReferenceExpressionBuilderHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.ReferenceExpressionBuilder'>; +/** Provides context for validating a required command. */ +type RequiredCommandValidationContextHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext'>; + +/** Represents the result of validating a required command. */ +type RequiredCommandValidationResultHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult'>; + /** A service to execute resource commands. */ type ResourceCommandServiceHandle = Handle<'Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceCommandService'>; @@ -441,6 +468,22 @@ export enum CommandResultFormat { Markdown = "Markdown", } +/** Specifies the destination for container images. */ +export enum ContainerImageDestination { + /** Image will be pushed to a container registry. */ + Registry = "Registry", + /** Image will be saved as an archive file. */ + Archive = "Archive", +} + +/** Specifies the format for container images. */ +export enum ContainerImageFormat { + /** Docker format (default). */ + Docker = "Docker", + /** OCI format. */ + Oci = "Oci", +} + /** Lifetime modes for container resources. */ export enum ContainerLifetime { /** Create the resource when the app host process starts and dispose of it when the app host process shuts down. */ @@ -466,6 +509,24 @@ export enum ContainerMountType { Volume = "Volume", } +/** Specifies the target platform for container images. */ +export enum ContainerTargetPlatform { + /** Linux AMD64 (linux/amd64). */ + LinuxAmd64 = "LinuxAmd64", + /** Linux ARM64 (linux/arm64). */ + LinuxArm64 = "LinuxArm64", + /** All Linux platforms (AMD64 and ARM64). */ + AllLinux = "AllLinux", + /** Linux ARM (linux/arm). */ + LinuxArm = "LinuxArm", + /** Linux 386 (linux/386). */ + Linux386 = "Linux386", + /** Windows AMD64 (windows/amd64). */ + WindowsAmd64 = "WindowsAmd64", + /** Windows ARM64 (windows/arm64). */ + WindowsArm64 = "WindowsArm64", +} + /** Describes the context in which the AppHost is being executed. */ export enum DistributedApplicationOperation { /** AppHost is being run for the purpose of debugging locally. */ @@ -1286,6 +1347,45 @@ export interface CopyOptions { chown?: string; } +export interface CreateCertificateFileOptions { + /** The inline PEM-encoded contents of the certificate. Mutually exclusive with `sourcePath`. */ + contents?: string; + /** An absolute path to a PEM file on the host to copy. Mutually exclusive with `contents`. */ + sourcePath?: string; + /** The owner UID, or `null` to inherit. */ + owner?: number; + /** The group GID, or `null` to inherit. */ + group?: number; + /** The Unix file mode as an integer (for example `0o644`), or `null` to inherit. */ + mode?: number; + /** Whether to ignore errors creating this file. */ + continueOnError?: boolean; +} + +export interface CreateDirectoryOptions { + /** The owner UID, or `null` to inherit. */ + owner?: number; + /** The group GID, or `null` to inherit. */ + group?: number; + /** The Unix file mode as an integer (for example `0o755`), or `null` to inherit. */ + mode?: number; +} + +export interface CreateFileOptions { + /** The inline UTF-8 contents of the file. Mutually exclusive with `sourcePath`. */ + contents?: string; + /** An absolute path to a file on the host to copy. Mutually exclusive with `contents`. */ + sourcePath?: string; + /** The owner UID, or `null` to inherit. */ + owner?: number; + /** The group GID, or `null` to inherit. */ + group?: number; + /** The Unix file mode as an integer (for example `0o644`), or `null` to inherit. */ + mode?: number; + /** Whether to ignore errors creating this file. */ + continueOnError?: boolean; +} + export interface CreateMarkdownTaskOptions { cancellationToken?: AbortSignal | CancellationToken; } @@ -1572,6 +1672,11 @@ export interface WithRequiredCommandOptions { helpLink?: string; } +export interface WithRequiredCommandValidationOptions { + /** An optional help link URL to guide users when the command is missing or fails validation. */ + helpLink?: string; +} + export interface WithUrlOptions { displayText?: string; } @@ -2300,6 +2405,481 @@ class ConnectionStringAvailableEventPromiseImpl implements ConnectionStringAvail } +// ============================================================================ +// ContainerBuildOptionsCallbackContext +// ============================================================================ + +/** Context for configuring container build options via a callback. */ +export interface ContainerBuildOptionsCallbackContext { + toJSON(): MarshalledHandle; + /** Gets the resource being built. */ + resource(): ResourcePromise; + /** Gets the service provider. */ + services(): ServiceProviderPromise; + /** Gets the logger instance. */ + logger(): LoggerPromise; + /** Gets the cancellation token. */ + cancellationToken(): Promise; + /** + * Gets the distributed application execution context. + * + * Use `IsPublishMode` or + * `IsRunMode` to vary build options + * (for example `TargetPlatform`) between local run and publish operations. + */ + executionContext(): DistributedApplicationExecutionContextPromise; + /** Gets or sets the destination for the container image. */ + destination: { + get: () => Promise; + set: (value: ContainerImageDestination | null) => Promise; + }; + /** Gets or sets the output path for the container archive. */ + outputPath: { + get: () => Promise; + set: (value: string | null) => Promise; + }; + /** Gets or sets the container image format. */ + imageFormat: { + get: () => Promise; + set: (value: ContainerImageFormat | null) => Promise; + }; + /** Gets or sets the target platform for the container. */ + targetPlatform: { + get: () => Promise; + set: (value: ContainerTargetPlatform | null) => Promise; + }; + /** Gets or sets the local image name for the built container. */ + localImageName: { + get: () => Promise; + set: (value: string | null) => Promise; + }; + /** Gets or sets the local image tag for the built container. */ + localImageTag: { + get: () => Promise; + set: (value: string | null) => Promise; + }; +} + +export interface ContainerBuildOptionsCallbackContextPromise extends PromiseLike { + /** Gets the resource being built. */ + resource(): ResourcePromise; + /** Gets the service provider. */ + services(): ServiceProviderPromise; + /** Gets the logger instance. */ + logger(): LoggerPromise; + /** Gets the cancellation token. */ + cancellationToken(): Promise; + /** + * Gets the distributed application execution context. + * + * Use `IsPublishMode` or + * `IsRunMode` to vary build options + * (for example `TargetPlatform`) between local run and publish operations. + */ + executionContext(): DistributedApplicationExecutionContextPromise; +} + +// ============================================================================ +// ContainerBuildOptionsCallbackContextImpl +// ============================================================================ + +/** Context for configuring container build options via a callback. */ +class ContainerBuildOptionsCallbackContextImpl implements ContainerBuildOptionsCallbackContext { + constructor(private _handle: ContainerBuildOptionsCallbackContextHandle, private _client: AspireClientRpc) {} + + /** Serialize for JSON-RPC transport */ + toJSON(): MarshalledHandle { return this._handle.toJSON(); } + + resource(): ResourcePromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.resource', + { context: this._handle } + ); + return new ResourceImpl(handle, this._client); + })(); + return new ResourcePromiseImpl(promise, this._client, false); + } + + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + + logger(): LoggerPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.logger', + { context: this._handle } + ); + return new LoggerImpl(handle, this._client); + })(); + return new LoggerPromiseImpl(promise, this._client, false); + } + + async cancellationToken(): Promise { + const result = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.cancellationToken', + { context: this._handle } + ); + return CancellationToken.fromValue(result); + } + + executionContext(): DistributedApplicationExecutionContextPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.executionContext', + { context: this._handle } + ); + return new DistributedApplicationExecutionContextImpl(handle, this._client); + })(); + return new DistributedApplicationExecutionContextPromiseImpl(promise, this._client, false); + } + + destination = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.destination', + { context: this._handle } + ); + }, + set: async (value: ContainerImageDestination | null): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setDestination', + { context: this._handle, value } + ); + } + }; + + outputPath = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.outputPath', + { context: this._handle } + ); + }, + set: async (value: string | null): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setOutputPath', + { context: this._handle, value } + ); + } + }; + + imageFormat = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.imageFormat', + { context: this._handle } + ); + }, + set: async (value: ContainerImageFormat | null): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setImageFormat', + { context: this._handle, value } + ); + } + }; + + targetPlatform = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.targetPlatform', + { context: this._handle } + ); + }, + set: async (value: ContainerTargetPlatform | null): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setTargetPlatform', + { context: this._handle, value } + ); + } + }; + + localImageName = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageName', + { context: this._handle } + ); + }, + set: async (value: string | null): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageName', + { context: this._handle, value } + ); + } + }; + + localImageTag = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.localImageTag', + { context: this._handle } + ); + }, + set: async (value: string | null): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerBuildOptionsCallbackContext.setLocalImageTag', + { context: this._handle, value } + ); + } + }; + +} + +/** + * Thenable wrapper for ContainerBuildOptionsCallbackContext that enables fluent chaining. + */ +class ContainerBuildOptionsCallbackContextPromiseImpl implements ContainerBuildOptionsCallbackContextPromise { + constructor(private _promise: Promise, private _client: AspireClientRpc, track = true) { + if (track) { _client.trackPromise(_promise); } + } + + then( + onfulfilled?: ((value: ContainerBuildOptionsCallbackContext) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._promise.then(onfulfilled, onrejected); + } + + resource(): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.resource()), this._client, false); + } + + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + + logger(): LoggerPromise { + return new LoggerPromiseImpl(this._promise.then(obj => obj.logger()), this._client, false); + } + + cancellationToken(): Promise { + return this._promise.then(obj => obj.cancellationToken()); + } + + executionContext(): DistributedApplicationExecutionContextPromise { + return new DistributedApplicationExecutionContextPromiseImpl(this._promise.then(obj => obj.executionContext()), this._client, false); + } + +} + +// ============================================================================ +// ContainerFileSystemCallbackContext +// ============================================================================ + +/** Represents the context for a `ContainerFileSystemCallbackAnnotation` callback. */ +export interface ContainerFileSystemCallbackContext { + toJSON(): MarshalledHandle; + /** A `IServiceProvider` that can be used to resolve services in the callback. */ + services(): ServiceProviderPromise; + /** The app model resource the callback is associated with. */ + model(): ResourcePromise; + /** + * Creates a container file entry with inline contents or a host source path. + * @param name The simple file name (no path separators). + * @param options Additional options. + * @returns The created file entry. + */ + createFile(name: string, options?: CreateFileOptions): Promise; + /** + * Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. + * @param name The simple file name (no path separators). + * @param options Additional options. + * @returns The created certificate file entry. + */ + createCertificateFile(name: string, options?: CreateCertificateFileOptions): Promise; + /** + * Creates a container directory entry containing the specified child entries. + * @param name The simple directory name (no path separators). + * @param entries The child entries (files and/or directories) created via this context. + * @param options Additional options. + * @returns The created directory entry. + */ + createDirectory(name: string, entries: ContainerFileSystemItemHandle[], options?: CreateDirectoryOptions): Promise; +} + +export interface ContainerFileSystemCallbackContextPromise extends PromiseLike { + /** A `IServiceProvider` that can be used to resolve services in the callback. */ + services(): ServiceProviderPromise; + /** The app model resource the callback is associated with. */ + model(): ResourcePromise; + /** + * Creates a container file entry with inline contents or a host source path. + * @param name The simple file name (no path separators). + * @param options Additional options. + * @returns The created file entry. + */ + createFile(name: string, options?: CreateFileOptions): Promise; + /** + * Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. + * @param name The simple file name (no path separators). + * @param options Additional options. + * @returns The created certificate file entry. + */ + createCertificateFile(name: string, options?: CreateCertificateFileOptions): Promise; + /** + * Creates a container directory entry containing the specified child entries. + * @param name The simple directory name (no path separators). + * @param entries The child entries (files and/or directories) created via this context. + * @param options Additional options. + * @returns The created directory entry. + */ + createDirectory(name: string, entries: ContainerFileSystemItemHandle[], options?: CreateDirectoryOptions): Promise; +} + +// ============================================================================ +// ContainerFileSystemCallbackContextImpl +// ============================================================================ + +/** Represents the context for a `ContainerFileSystemCallbackAnnotation` callback. */ +class ContainerFileSystemCallbackContextImpl implements ContainerFileSystemCallbackContext { + constructor(private _handle: ContainerFileSystemCallbackContextHandle, private _client: AspireClientRpc) {} + + /** Serialize for JSON-RPC transport */ + toJSON(): MarshalledHandle { return this._handle.toJSON(); } + + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + + model(): ResourcePromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ContainerFileSystemCallbackContext.model', + { context: this._handle } + ); + return new ResourceImpl(handle, this._client); + })(); + return new ResourcePromiseImpl(promise, this._client, false); + } + + /** + * Creates a container file entry with inline contents or a host source path. + * @param name The simple file name (no path separators). + * @param options Additional options. + * @returns The created file entry. + */ + async createFile(name: string, options?: CreateFileOptions): Promise { + const contents = options?.contents; + const sourcePath = options?.sourcePath; + const owner = options?.owner; + const group = options?.group; + const mode = options?.mode; + const continueOnError = options?.continueOnError; + const rpcArgs: Record = { context: this._handle, name }; + if (contents !== undefined) rpcArgs.contents = contents; + if (sourcePath !== undefined) rpcArgs.sourcePath = sourcePath; + if (owner !== undefined) rpcArgs.owner = owner; + if (group !== undefined) rpcArgs.group = group; + if (mode !== undefined) rpcArgs.mode = mode; + if (continueOnError !== undefined) rpcArgs.continueOnError = continueOnError; + return await this._client.invokeCapability( + 'Aspire.Hosting/createFile', + rpcArgs + ); + } + + /** + * Creates a PEM container certificate file entry with the OpenSSL subject-hash symlink. + * @param name The simple file name (no path separators). + * @param options Additional options. + * @returns The created certificate file entry. + */ + async createCertificateFile(name: string, options?: CreateCertificateFileOptions): Promise { + const contents = options?.contents; + const sourcePath = options?.sourcePath; + const owner = options?.owner; + const group = options?.group; + const mode = options?.mode; + const continueOnError = options?.continueOnError; + const rpcArgs: Record = { context: this._handle, name }; + if (contents !== undefined) rpcArgs.contents = contents; + if (sourcePath !== undefined) rpcArgs.sourcePath = sourcePath; + if (owner !== undefined) rpcArgs.owner = owner; + if (group !== undefined) rpcArgs.group = group; + if (mode !== undefined) rpcArgs.mode = mode; + if (continueOnError !== undefined) rpcArgs.continueOnError = continueOnError; + return await this._client.invokeCapability( + 'Aspire.Hosting/createCertificateFile', + rpcArgs + ); + } + + /** + * Creates a container directory entry containing the specified child entries. + * @param name The simple directory name (no path separators). + * @param entries The child entries (files and/or directories) created via this context. + * @param options Additional options. + * @returns The created directory entry. + */ + async createDirectory(name: string, entries: ContainerFileSystemItemHandle[], options?: CreateDirectoryOptions): Promise { + const owner = options?.owner; + const group = options?.group; + const mode = options?.mode; + const rpcArgs: Record = { context: this._handle, name, entries }; + if (owner !== undefined) rpcArgs.owner = owner; + if (group !== undefined) rpcArgs.group = group; + if (mode !== undefined) rpcArgs.mode = mode; + return await this._client.invokeCapability( + 'Aspire.Hosting/createDirectory', + rpcArgs + ); + } + +} + +/** + * Thenable wrapper for ContainerFileSystemCallbackContext that enables fluent chaining. + */ +class ContainerFileSystemCallbackContextPromiseImpl implements ContainerFileSystemCallbackContextPromise { + constructor(private _promise: Promise, private _client: AspireClientRpc, track = true) { + if (track) { _client.trackPromise(_promise); } + } + + then( + onfulfilled?: ((value: ContainerFileSystemCallbackContext) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._promise.then(onfulfilled, onrejected); + } + + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + + model(): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.model()), this._client, false); + } + + createFile(name: string, options?: CreateFileOptions): Promise { + return this._promise.then(obj => obj.createFile(name, options)); + } + + createCertificateFile(name: string, options?: CreateCertificateFileOptions): Promise { + return this._promise.then(obj => obj.createCertificateFile(name, options)); + } + + createDirectory(name: string, entries: ContainerFileSystemItemHandle[], options?: CreateDirectoryOptions): Promise { + return this._promise.then(obj => obj.createDirectory(name, entries, options)); + } + +} + // ============================================================================ // ContainerImagePushOptions // ============================================================================ @@ -4963,6 +5543,8 @@ class EventingSubscriberRegistrationContextPromiseImpl implements EventingSubscr /** Context for {@link ResourceCommandAnnotation.ExecuteCommand}. */ export interface ExecuteCommandContext { toJSON(): MarshalledHandle; + /** The service provider. */ + services(): ServiceProviderPromise; /** The resource name. */ resourceName(): Promise; /** The cancellation token. */ @@ -4980,6 +5562,8 @@ export interface ExecuteCommandContext { } export interface ExecuteCommandContextPromise extends PromiseLike { + /** The service provider. */ + services(): ServiceProviderPromise; /** The resource name. */ resourceName(): Promise; /** The cancellation token. */ @@ -5007,6 +5591,17 @@ class ExecuteCommandContextImpl implements ExecuteCommandContext { /** Serialize for JSON-RPC transport */ toJSON(): MarshalledHandle { return this._handle.toJSON(); } + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + async resourceName(): Promise { return await this._client.invokeCapability( 'Aspire.Hosting.ApplicationModel/ExecuteCommandContext.resourceName', @@ -5057,6 +5652,10 @@ class ExecuteCommandContextPromiseImpl implements ExecuteCommandContextPromise { return this._promise.then(onfulfilled, onrejected); } + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + resourceName(): Promise { return this._promise.then(obj => obj.resourceName()); } @@ -5182,6 +5781,300 @@ class HttpCommandPrepareRequestContextPromiseImpl implements HttpCommandPrepareR } +// ============================================================================ +// HttpsCertificateConfigurationCallbackAnnotationContext +// ============================================================================ + +/** Context provided to a `HttpsCertificateConfigurationCallbackAnnotation` callback. */ +export interface HttpsCertificateConfigurationCallbackAnnotationContext { + toJSON(): MarshalledHandle; + /** Gets the `DistributedApplicationExecutionContext` for this session. */ + executionContext(): DistributedApplicationExecutionContextPromise; + /** Gets the resource to which the annotation is applied. */ + resource(): ResourcePromise; + /** A value provider that will resolve to a path to the certificate file. */ + certificatePath(): Promise; + /** A value provider that will resolve to a path to the private key for the certificate. */ + keyPath(): Promise; + /** A value provider that will resolve to a path to a PFX file for the key pair. */ + pfxPath(): Promise; + /** Gets the `CancellationToken` that can be used to cancel the operation. */ + cancellationToken(): Promise; + /** Gets the editor used to manipulate the command-line arguments in polyglot callbacks. */ + arguments(): CommandLineArgsEditorPromise; + /** Gets the editor used to set environment variables in polyglot callbacks. */ + environment(): EnvironmentEditorPromise; +} + +export interface HttpsCertificateConfigurationCallbackAnnotationContextPromise extends PromiseLike { + /** Gets the `DistributedApplicationExecutionContext` for this session. */ + executionContext(): DistributedApplicationExecutionContextPromise; + /** Gets the resource to which the annotation is applied. */ + resource(): ResourcePromise; + /** A value provider that will resolve to a path to the certificate file. */ + certificatePath(): Promise; + /** A value provider that will resolve to a path to the private key for the certificate. */ + keyPath(): Promise; + /** A value provider that will resolve to a path to a PFX file for the key pair. */ + pfxPath(): Promise; + /** Gets the `CancellationToken` that can be used to cancel the operation. */ + cancellationToken(): Promise; + /** Gets the editor used to manipulate the command-line arguments in polyglot callbacks. */ + arguments(): CommandLineArgsEditorPromise; + /** Gets the editor used to set environment variables in polyglot callbacks. */ + environment(): EnvironmentEditorPromise; +} + +// ============================================================================ +// HttpsCertificateConfigurationCallbackAnnotationContextImpl +// ============================================================================ + +/** Context provided to a `HttpsCertificateConfigurationCallbackAnnotation` callback. */ +class HttpsCertificateConfigurationCallbackAnnotationContextImpl implements HttpsCertificateConfigurationCallbackAnnotationContext { + constructor(private _handle: HttpsCertificateConfigurationCallbackAnnotationContextHandle, private _client: AspireClientRpc) {} + + /** Serialize for JSON-RPC transport */ + toJSON(): MarshalledHandle { return this._handle.toJSON(); } + + executionContext(): DistributedApplicationExecutionContextPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.executionContext', + { context: this._handle } + ); + return new DistributedApplicationExecutionContextImpl(handle, this._client); + })(); + return new DistributedApplicationExecutionContextPromiseImpl(promise, this._client, false); + } + + resource(): ResourcePromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.resource', + { context: this._handle } + ); + return new ResourceImpl(handle, this._client); + })(); + return new ResourcePromiseImpl(promise, this._client, false); + } + + async certificatePath(): Promise { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.certificatePath', + { context: this._handle } + ); + } + + async keyPath(): Promise { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.keyPath', + { context: this._handle } + ); + } + + async pfxPath(): Promise { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.pfxPath', + { context: this._handle } + ); + } + + async cancellationToken(): Promise { + const result = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.cancellationToken', + { context: this._handle } + ); + return CancellationToken.fromValue(result); + } + + arguments(): CommandLineArgsEditorPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.arguments', + { context: this._handle } + ); + return new CommandLineArgsEditorImpl(handle, this._client); + })(); + return new CommandLineArgsEditorPromiseImpl(promise, this._client, false); + } + + environment(): EnvironmentEditorPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsCertificateConfigurationCallbackAnnotationContext.environment', + { context: this._handle } + ); + return new EnvironmentEditorImpl(handle, this._client); + })(); + return new EnvironmentEditorPromiseImpl(promise, this._client, false); + } + +} + +/** + * Thenable wrapper for HttpsCertificateConfigurationCallbackAnnotationContext that enables fluent chaining. + */ +class HttpsCertificateConfigurationCallbackAnnotationContextPromiseImpl implements HttpsCertificateConfigurationCallbackAnnotationContextPromise { + constructor(private _promise: Promise, private _client: AspireClientRpc, track = true) { + if (track) { _client.trackPromise(_promise); } + } + + then( + onfulfilled?: ((value: HttpsCertificateConfigurationCallbackAnnotationContext) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._promise.then(onfulfilled, onrejected); + } + + executionContext(): DistributedApplicationExecutionContextPromise { + return new DistributedApplicationExecutionContextPromiseImpl(this._promise.then(obj => obj.executionContext()), this._client, false); + } + + resource(): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.resource()), this._client, false); + } + + certificatePath(): Promise { + return this._promise.then(obj => obj.certificatePath()); + } + + keyPath(): Promise { + return this._promise.then(obj => obj.keyPath()); + } + + pfxPath(): Promise { + return this._promise.then(obj => obj.pfxPath()); + } + + cancellationToken(): Promise { + return this._promise.then(obj => obj.cancellationToken()); + } + + arguments(): CommandLineArgsEditorPromise { + return new CommandLineArgsEditorPromiseImpl(this._promise.then(obj => obj.arguments()), this._client, false); + } + + environment(): EnvironmentEditorPromise { + return new EnvironmentEditorPromiseImpl(this._promise.then(obj => obj.environment()), this._client, false); + } + +} + +// ============================================================================ +// HttpsEndpointUpdateCallbackContext +// ============================================================================ + +/** Context provided to the callback of `SubscribeHttpsEndpointsUpdate``1` when an HTTPS certificate is determined to be available for the resource. */ +export interface HttpsEndpointUpdateCallbackContext { + toJSON(): MarshalledHandle; + /** Gets the `IServiceProvider` instance from the application. */ + services(): ServiceProviderPromise; + /** Gets the `IResource` that is being configured for HTTPS. */ + resource(): ResourcePromise; + /** Gets the `DistributedApplicationModel` instance. */ + model(): DistributedApplicationModelPromise; + /** Gets the `CancellationToken` for the operation. */ + cancellationToken(): Promise; +} + +export interface HttpsEndpointUpdateCallbackContextPromise extends PromiseLike { + /** Gets the `IServiceProvider` instance from the application. */ + services(): ServiceProviderPromise; + /** Gets the `IResource` that is being configured for HTTPS. */ + resource(): ResourcePromise; + /** Gets the `DistributedApplicationModel` instance. */ + model(): DistributedApplicationModelPromise; + /** Gets the `CancellationToken` for the operation. */ + cancellationToken(): Promise; +} + +// ============================================================================ +// HttpsEndpointUpdateCallbackContextImpl +// ============================================================================ + +/** Context provided to the callback of `SubscribeHttpsEndpointsUpdate``1` when an HTTPS certificate is determined to be available for the resource. */ +class HttpsEndpointUpdateCallbackContextImpl implements HttpsEndpointUpdateCallbackContext { + constructor(private _handle: HttpsEndpointUpdateCallbackContextHandle, private _client: AspireClientRpc) {} + + /** Serialize for JSON-RPC transport */ + toJSON(): MarshalledHandle { return this._handle.toJSON(); } + + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + + resource(): ResourcePromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.resource', + { context: this._handle } + ); + return new ResourceImpl(handle, this._client); + })(); + return new ResourcePromiseImpl(promise, this._client, false); + } + + model(): DistributedApplicationModelPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.model', + { context: this._handle } + ); + return new DistributedApplicationModelImpl(handle, this._client); + })(); + return new DistributedApplicationModelPromiseImpl(promise, this._client, false); + } + + async cancellationToken(): Promise { + const result = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/HttpsEndpointUpdateCallbackContext.cancellationToken', + { context: this._handle } + ); + return CancellationToken.fromValue(result); + } + +} + +/** + * Thenable wrapper for HttpsEndpointUpdateCallbackContext that enables fluent chaining. + */ +class HttpsEndpointUpdateCallbackContextPromiseImpl implements HttpsEndpointUpdateCallbackContextPromise { + constructor(private _promise: Promise, private _client: AspireClientRpc, track = true) { + if (track) { _client.trackPromise(_promise); } + } + + then( + onfulfilled?: ((value: HttpsEndpointUpdateCallbackContext) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._promise.then(onfulfilled, onrejected); + } + + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + + resource(): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.resource()), this._client, false); + } + + model(): DistributedApplicationModelPromise { + return new DistributedApplicationModelPromiseImpl(this._promise.then(obj => obj.model()), this._client, false); + } + + cancellationToken(): Promise { + return this._promise.then(obj => obj.cancellationToken()); + } + +} + // ============================================================================ // InitializeResourceEvent // ============================================================================ @@ -5340,6 +6233,8 @@ export interface InputsDialogValidationContext { inputs(): Promise; /** Gets the cancellation token for the validation operation. */ cancellationToken(): Promise; + /** Gets the service provider for resolving services during validation. */ + services(): ServiceProviderPromise; /** * Adds a validation error for the input with the specified name. * @param inputName The name of the input to add a validation error for. @@ -5353,6 +6248,8 @@ export interface InputsDialogValidationContextPromise extends PromiseLike; /** Gets the cancellation token for the validation operation. */ cancellationToken(): Promise; + /** Gets the service provider for resolving services during validation. */ + services(): ServiceProviderPromise; /** * Adds a validation error for the input with the specified name. * @param inputName The name of the input to add a validation error for. @@ -5387,6 +6284,17 @@ class InputsDialogValidationContextImpl implements InputsDialogValidationContext return CancellationToken.fromValue(result); } + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting/InputsDialogValidationContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + /** @internal */ async _addValidationErrorInternal(inputName: string, errorMessage: string): Promise { const rpcArgs: Record = { context: this._handle, inputName, errorMessage }; @@ -5431,6 +6339,10 @@ class InputsDialogValidationContextPromiseImpl implements InputsDialogValidation return this._promise.then(obj => obj.cancellationToken()); } + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + addValidationError(inputName: string, errorMessage: string): InputsDialogValidationContextPromise { return new InputsDialogValidationContextPromiseImpl(this._promise.then(obj => obj.addValidationError(inputName, errorMessage)), this._client); } @@ -6909,6 +7821,237 @@ class ReferenceExpressionBuilderPromiseImpl implements ReferenceExpressionBuilde } +// ============================================================================ +// RequiredCommandValidationContext +// ============================================================================ + +/** Provides context for validating a required command. */ +export interface RequiredCommandValidationContext { + toJSON(): MarshalledHandle; + /** Gets the resolved full path to the command executable. */ + resolvedPath(): Promise; + /** Gets the service provider for accessing application services. */ + services(): ServiceProviderPromise; + /** Gets a cancellation token that can be used to cancel the validation. */ + cancellationToken(): Promise; + /** + * Creates a successful validation result. + * @returns A `RequiredCommandValidationResult` indicating the command is valid. + */ + success(): RequiredCommandValidationResultPromise; + /** + * Creates a failed validation result with the specified message. + * @param validationMessage A message describing why validation failed. + * @returns A `RequiredCommandValidationResult` indicating the command is invalid. + */ + failure(validationMessage: string): RequiredCommandValidationResultPromise; +} + +export interface RequiredCommandValidationContextPromise extends PromiseLike { + /** Gets the resolved full path to the command executable. */ + resolvedPath(): Promise; + /** Gets the service provider for accessing application services. */ + services(): ServiceProviderPromise; + /** Gets a cancellation token that can be used to cancel the validation. */ + cancellationToken(): Promise; + /** + * Creates a successful validation result. + * @returns A `RequiredCommandValidationResult` indicating the command is valid. + */ + success(): RequiredCommandValidationResultPromise; + /** + * Creates a failed validation result with the specified message. + * @param validationMessage A message describing why validation failed. + * @returns A `RequiredCommandValidationResult` indicating the command is invalid. + */ + failure(validationMessage: string): RequiredCommandValidationResultPromise; +} + +// ============================================================================ +// RequiredCommandValidationContextImpl +// ============================================================================ + +/** Provides context for validating a required command. */ +class RequiredCommandValidationContextImpl implements RequiredCommandValidationContext { + constructor(private _handle: RequiredCommandValidationContextHandle, private _client: AspireClientRpc) {} + + /** Serialize for JSON-RPC transport */ + toJSON(): MarshalledHandle { return this._handle.toJSON(); } + + async resolvedPath(): Promise { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.resolvedPath', + { context: this._handle } + ); + } + + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + + async cancellationToken(): Promise { + const result = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.cancellationToken', + { context: this._handle } + ); + return CancellationToken.fromValue(result); + } + + /** @internal */ + async _successInternal(): Promise { + const rpcArgs: Record = { context: this._handle }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.success', + rpcArgs + ); + return new RequiredCommandValidationResultImpl(result, this._client); + } + + /** + * Creates a successful validation result. + * @returns A `RequiredCommandValidationResult` indicating the command is valid. + */ + success(): RequiredCommandValidationResultPromise { + return new RequiredCommandValidationResultPromiseImpl(this._successInternal(), this._client); + } + + /** @internal */ + async _failureInternal(validationMessage: string): Promise { + const rpcArgs: Record = { context: this._handle, validationMessage }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationContext.failure', + rpcArgs + ); + return new RequiredCommandValidationResultImpl(result, this._client); + } + + /** + * Creates a failed validation result with the specified message. + * @param validationMessage A message describing why validation failed. + * @returns A `RequiredCommandValidationResult` indicating the command is invalid. + */ + failure(validationMessage: string): RequiredCommandValidationResultPromise { + return new RequiredCommandValidationResultPromiseImpl(this._failureInternal(validationMessage), this._client); + } + +} + +/** + * Thenable wrapper for RequiredCommandValidationContext that enables fluent chaining. + */ +class RequiredCommandValidationContextPromiseImpl implements RequiredCommandValidationContextPromise { + constructor(private _promise: Promise, private _client: AspireClientRpc, track = true) { + if (track) { _client.trackPromise(_promise); } + } + + then( + onfulfilled?: ((value: RequiredCommandValidationContext) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._promise.then(onfulfilled, onrejected); + } + + resolvedPath(): Promise { + return this._promise.then(obj => obj.resolvedPath()); + } + + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + + cancellationToken(): Promise { + return this._promise.then(obj => obj.cancellationToken()); + } + + success(): RequiredCommandValidationResultPromise { + return new RequiredCommandValidationResultPromiseImpl(this._promise.then(obj => obj.success()), this._client); + } + + failure(validationMessage: string): RequiredCommandValidationResultPromise { + return new RequiredCommandValidationResultPromiseImpl(this._promise.then(obj => obj.failure(validationMessage)), this._client); + } + +} + +// ============================================================================ +// RequiredCommandValidationResult +// ============================================================================ + +/** Represents the result of validating a required command. */ +export interface RequiredCommandValidationResult { + toJSON(): MarshalledHandle; + /** Gets a value indicating whether the command validation succeeded. */ + isValid(): Promise; + /** Gets an optional validation message describing why validation failed. */ + validationMessage(): Promise; +} + +export interface RequiredCommandValidationResultPromise extends PromiseLike { + /** Gets a value indicating whether the command validation succeeded. */ + isValid(): Promise; + /** Gets an optional validation message describing why validation failed. */ + validationMessage(): Promise; +} + +// ============================================================================ +// RequiredCommandValidationResultImpl +// ============================================================================ + +/** Represents the result of validating a required command. */ +class RequiredCommandValidationResultImpl implements RequiredCommandValidationResult { + constructor(private _handle: RequiredCommandValidationResultHandle, private _client: AspireClientRpc) {} + + /** Serialize for JSON-RPC transport */ + toJSON(): MarshalledHandle { return this._handle.toJSON(); } + + async isValid(): Promise { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.isValid', + { context: this._handle } + ); + } + + async validationMessage(): Promise { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RequiredCommandValidationResult.validationMessage', + { context: this._handle } + ); + } + +} + +/** + * Thenable wrapper for RequiredCommandValidationResult that enables fluent chaining. + */ +class RequiredCommandValidationResultPromiseImpl implements RequiredCommandValidationResultPromise { + constructor(private _promise: Promise, private _client: AspireClientRpc, track = true) { + if (track) { _client.trackPromise(_promise); } + } + + then( + onfulfilled?: ((value: RequiredCommandValidationResult) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._promise.then(onfulfilled, onrejected); + } + + isValid(): Promise { + return this._promise.then(obj => obj.isValid()); + } + + validationMessage(): Promise { + return this._promise.then(obj => obj.validationMessage()); + } + +} + // ============================================================================ // ResourceCommandService // ============================================================================ @@ -8269,11 +9412,15 @@ export interface UpdateCommandStateContext { toJSON(): MarshalledHandle; /** Gets the resource snapshot data available to polyglot command state callbacks. */ resourceSnapshot(): Promise; + /** The service provider. */ + services(): ServiceProviderPromise; } export interface UpdateCommandStateContextPromise extends PromiseLike { /** Gets the resource snapshot data available to polyglot command state callbacks. */ resourceSnapshot(): Promise; + /** The service provider. */ + services(): ServiceProviderPromise; } // ============================================================================ @@ -8294,6 +9441,17 @@ class UpdateCommandStateContextImpl implements UpdateCommandStateContext { ); } + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + } /** @@ -8315,6 +9473,10 @@ class UpdateCommandStateContextPromiseImpl implements UpdateCommandStateContextP return this._promise.then(obj => obj.resourceSnapshot()); } + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + } // ============================================================================ @@ -11917,6 +13079,19 @@ export interface ContainerRegistryResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ContainerRegistryResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerRegistryResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -11997,6 +13172,338 @@ export interface ContainerRegistryResource { * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. */ withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ContainerRegistryResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerRegistryResourcePromise; + /** + * Adds a relationship to another resource using its builder. + * @param resourceBuilder The resource builder that the relationship is to. + * @param type The relationship type. + * @returns A resource builder. + */ + withRelationship(resourceBuilder: Awaitable, type: string): ContainerRegistryResourcePromise; + /** + * Sets the parent relationship + * @param parent The parent of `builder`. + * @returns A resource builder. + */ + withParentRelationship(parent: Awaitable): ContainerRegistryResourcePromise; + /** + * Sets a child relationship + * @param child The child of `builder`. + * @returns A resource builder. + */ + withChildRelationship(child: Awaitable): ContainerRegistryResourcePromise; + /** + * Specifies the icon to use when displaying the resource in the dashboard. + * @param iconName The name of the FluentUI icon to use. See https://aka.ms/fluentui-system-icons for available icons. + * @param options Additional options. + * @returns The resource builder. + */ + withIconName(iconName: string, options?: WithIconNameOptions): ContainerRegistryResourcePromise; + /** + * Exclude the resource from MCP operations using the Aspire MCP server. The resource is excluded from results that return resources, console logs and telemetry. + * @returns The resource builder. + */ + excludeFromMcp(): ContainerRegistryResourcePromise; + /** + * Hides the resource from default resource lists + * + * Use this method to hide resources that are implementation details and should never be displayed by default. + * Hidden resources can still be accessed directly by their name, by using `Show hidden resources` toggle in the dashboard or by using `aspire describe --include-hidden` from the CLI. + * @returns The resource builder. + */ + withHidden(): ContainerRegistryResourcePromise; + /** + * Hides the resource from default resource lists after successful completion + * + * This method is useful for one-off resources such as setup scripts, migrations, or build steps that should remain visible while running + * and then be hidden after successful completion. + * Hidden resources can still be accessed directly by their name, by using `Show hidden resources` toggle in the dashboard or by using `aspire describe --include-hidden` from the CLI. + * @param options Additional options. + * @returns The resource builder. + */ + withHiddenOnCompletion(options?: WithHiddenOnCompletionOptions): ContainerRegistryResourcePromise; + /** + * Adds a pipeline step to the resource that will be executed during deployment. + * @param stepName The unique name of the pipeline step. + * @param callback The callback to execute when the step runs. + * @param options Additional options. + * @returns The resource builder for chaining. + */ + withPipelineStepFactory(stepName: string, callback: (arg: PipelineStepContext) => Promise, options?: WithPipelineStepFactoryOptions): ContainerRegistryResourcePromise; + /** + * Registers a callback to be executed during the pipeline configuration phase, allowing modification of step dependencies and relationships. + * @param callback The callback function to execute during the configuration phase. + * @returns The resource builder for chaining. + */ + withPipelineConfiguration(callback: (obj: PipelineConfigurationContext) => Promise): ContainerRegistryResourcePromise; + /** + * Gets the name of the resource from a builder. + * + * Why this wrapper exists: This capability accesses a nested property + * (`resource.Resource.Name`) which requires a wrapper method. There is no single + * .NET method that returns just the resource name that could be annotated directly. + * @returns The resource name. + */ + getResourceName(): Promise; + /** + * Subscribes to the BeforeResourceStarted event. + * @param callback The callback to invoke when the event fires. + * @returns The resource builder. + */ + onBeforeResourceStarted(callback: (arg: BeforeResourceStartedEvent) => Promise): ContainerRegistryResourcePromise; + /** + * Subscribes to the ResourceStopped event. + * @param callback The callback to invoke when the event fires. + * @returns The resource builder. + */ + onResourceStopped(callback: (arg: ResourceStoppedEvent) => Promise): ContainerRegistryResourcePromise; + /** + * Subscribes to the InitializeResource event. + * @param callback The callback to invoke when the event fires. + * @returns The resource builder. + */ + onInitializeResource(callback: (arg: InitializeResourceEvent) => Promise): ContainerRegistryResourcePromise; + /** + * Subscribes to the ResourceReady event. + * @param callback The callback to invoke when the event fires. + * @returns The resource builder. + */ + onResourceReady(callback: (arg: ResourceReadyEvent) => Promise): ContainerRegistryResourcePromise; + /** + * Creates an execution configuration builder for the specified resource. + * @returns The execution configuration builder. + */ + createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerRegistryResourcePromise; + /** + * Adds an optional string parameter + * @param options Additional options. + */ + withOptionalString(options?: WithOptionalStringOptions): ContainerRegistryResourcePromise; + /** Configures the resource with a DTO */ + withConfig(config: TestConfigDto): ContainerRegistryResourcePromise; + /** Sets the created timestamp */ + withCreatedAt(createdAt: string): ContainerRegistryResourcePromise; + /** Sets the modified timestamp */ + withModifiedAt(modifiedAt: string): ContainerRegistryResourcePromise; + /** Sets the correlation ID */ + withCorrelationId(correlationId: string): ContainerRegistryResourcePromise; + /** + * Configures with optional callback + * @param options Additional options. + */ + withOptionalCallback(options?: WithOptionalCallbackOptions): ContainerRegistryResourcePromise; + /** Sets the resource status */ + withStatus(status: TestResourceStatus): ContainerRegistryResourcePromise; + /** Configures with nested DTO */ + withNestedConfig(config: TestNestedDto): ContainerRegistryResourcePromise; + /** Adds validation callback */ + withValidator(validator: (arg: TestResourceContext) => Promise): ContainerRegistryResourcePromise; + /** Waits for another resource (test version) */ + testWaitFor(dependency: Awaitable): ContainerRegistryResourcePromise; + /** Adds a dependency on another resource */ + withDependency(dependency: Awaitable): ContainerRegistryResourcePromise; + /** Adds a dependency from a string or another resource */ + withUnionDependency(dependency: string | ResourceWithConnectionString | TestRedisResource | Awaitable): ContainerRegistryResourcePromise; + /** Sets the endpoints */ + withEndpoints(endpoints: string[]): ContainerRegistryResourcePromise; + /** Performs a cancellable operation */ + withCancellableOperation(operation: (arg: CancellationToken) => Promise): ContainerRegistryResourcePromise; + /** Adds a label to the resource */ + withMergeLabel(label: string): ContainerRegistryResourcePromise; + /** Adds a categorized label to the resource */ + withMergeLabelCategorized(label: string, category: string): ContainerRegistryResourcePromise; + /** Configures a named endpoint */ + withMergeEndpoint(endpointName: string, port: number): ContainerRegistryResourcePromise; + /** Configures a named endpoint with scheme */ + withMergeEndpointScheme(endpointName: string, port: number, scheme: string): ContainerRegistryResourcePromise; + /** + * Configures resource logging + * @param options Additional options. + */ + withMergeLogging(logLevel: string, options?: WithMergeLoggingOptions): ContainerRegistryResourcePromise; + /** + * Configures resource logging with file path + * @param options Additional options. + */ + withMergeLoggingPath(logLevel: string, logPath: string, options?: WithMergeLoggingPathOptions): ContainerRegistryResourcePromise; + /** Configures a route */ + withMergeRoute(path: string, method: string, handler: string, priority: number): ContainerRegistryResourcePromise; + /** Configures a route with middleware */ + withMergeRouteMiddleware(path: string, method: string, handler: string, priority: number, middleware: string): ContainerRegistryResourcePromise; +} + +export interface ContainerRegistryResourcePromise extends PromiseLike { + /** + * Configures the resource to use the specified container registry for container image operations. + * + * This method adds a `ContainerRegistryReferenceAnnotation` to the resource, + * indicating that the resource should use the specified container registry for container image operations. + * @param registry The container registry resource builder. + * @returns The resource builder for chaining. + */ + withContainerRegistry(registry: Awaitable): ContainerRegistryResourcePromise; + /** + * Configures custom base images for generated Dockerfiles. + * + * This extension method allows customization of the base images used in generated Dockerfiles. + * For multi-stage Dockerfiles (e.g., Python with UV), you can specify separate build and runtime images. + * Specify custom base images for a Python application: + * ``` + * var builder = DistributedApplication.CreateBuilder(args); + * builder.AddPythonApp("myapp", "path/to/app", "main.py") + * .WithDockerfileBaseImage( + * buildImage: "ghcr.io/astral-sh/uv:python3.12-bookworm-slim", + * runtimeImage: "python:3.12-slim-bookworm"); + * builder.Build().Run(); + * ``` + * @param options Additional options. + * @returns The resource builder. + */ + withDockerfileBaseImage(options?: WithDockerfileBaseImageOptions): ContainerRegistryResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start. + * + * The command is considered valid if either: + * 1. It is an absolute or relative path (contains a directory separator) that points to an existing file, or + * 2. It is discoverable on the current process PATH (respecting PATHEXT on Windows). + * If the command is not found, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ContainerRegistryResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerRegistryResourcePromise; + /** + * Configures a resource to use a session lifetime. + * @returns The resource builder. + */ + withSessionLifetime(): ContainerRegistryResourcePromise; + /** + * Configures a resource to use a persistent lifetime. + * @returns The resource builder. + */ + withPersistentLifetime(): ContainerRegistryResourcePromise; + /** + * Configures a resource to match the lifetime of another resource. + * + * The resource lifetime is evaluated from `sourceBuilder` when the application model is prepared, so later lifetime + * changes to the source resource are reflected by this resource. + * @param sourceBuilder The resource builder whose lifetime should be used. + * @returns The resource builder. + */ + withLifetimeOf(sourceBuilder: Awaitable): ContainerRegistryResourcePromise; + /** + * Configures a resource to use a persistent lifetime that ends when a parent process exits. + * @param parentProcessId The ID of the parent process to monitor. + * @returns The resource builder. + */ + withParentProcessLifetime(parentProcessId: number): ContainerRegistryResourcePromise; + /** + * Registers a callback to customize the URLs displayed for the resource. + * @param callback The callback that will customize URLs for the resource. + * @returns The resource builder. + */ + withUrls(callback: (obj: ResourceUrlsCallbackContext) => Promise): ContainerRegistryResourcePromise; + /** + * Adds or modifies displayed URLs + * @param options Additional options. + */ + withUrl(url: string | ReferenceExpression, options?: WithUrlOptions): ContainerRegistryResourcePromise; + /** + * Registers a callback to update the URL displayed for the endpoint with the specified name. + * @param endpointName The name of the endpoint to customize the URL for. + * @param callback The callback that will customize the URL. + * @returns The resource builder. + */ + withUrlForEndpoint(endpointName: string, callback: (obj: ResourceUrlAnnotation) => Promise): ContainerRegistryResourcePromise; + /** + * Excludes a resource from being published to the manifest. + * @returns The resource builder. + */ + excludeFromManifest(): ContainerRegistryResourcePromise; + /** + * Prevents resource from starting automatically + * @returns The resource builder. + */ + withExplicitStart(): ContainerRegistryResourcePromise; + /** + * Adds a health check by key + * @param key The key for the health check. + * @returns The resource builder. + */ + withHealthCheck(key: string): ContainerRegistryResourcePromise; + /** + * Adds a resource command + * + * The `WithCommand` method is used to add commands to the resource. Commands are displayed in the dashboard + * and can be executed by a user using the dashboard UI. + * When a command is executed, the `executeCommand` callback is called and is run inside the Aspire host. + * @param name The name of the command. The name uniquely identifies the command. + * @param displayName The display name visible in UI. + * @param executeCommand A callback that is executed when the command is executed. The callback is run inside the Aspire host. The callback result is used to indicate success or failure in the UI. + * @param options Additional options. + * @returns The resource builder. + */ + withCommand(name: string, displayName: string, executeCommand: (arg: ExecuteCommandContext) => Promise, options?: WithCommandOptions): ContainerRegistryResourcePromise; + /** Adds a command to the resource that starts a local process when invoked. */ + withProcessCommand(commandName: string, displayName: string, options: ProcessCommandExportOptions): ContainerRegistryResourcePromise; + /** + * Adds a command to the resource that starts a local process created by a callback when invoked. + * @param options Additional options. + * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. + */ + withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ContainerRegistryResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerRegistryResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -12099,284 +13606,11 @@ export interface ContainerRegistryResource { */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; /** - * Adds an optional string parameter - * @param options Additional options. - */ - withOptionalString(options?: WithOptionalStringOptions): ContainerRegistryResourcePromise; - /** Configures the resource with a DTO */ - withConfig(config: TestConfigDto): ContainerRegistryResourcePromise; - /** Sets the created timestamp */ - withCreatedAt(createdAt: string): ContainerRegistryResourcePromise; - /** Sets the modified timestamp */ - withModifiedAt(modifiedAt: string): ContainerRegistryResourcePromise; - /** Sets the correlation ID */ - withCorrelationId(correlationId: string): ContainerRegistryResourcePromise; - /** - * Configures with optional callback - * @param options Additional options. - */ - withOptionalCallback(options?: WithOptionalCallbackOptions): ContainerRegistryResourcePromise; - /** Sets the resource status */ - withStatus(status: TestResourceStatus): ContainerRegistryResourcePromise; - /** Configures with nested DTO */ - withNestedConfig(config: TestNestedDto): ContainerRegistryResourcePromise; - /** Adds validation callback */ - withValidator(validator: (arg: TestResourceContext) => Promise): ContainerRegistryResourcePromise; - /** Waits for another resource (test version) */ - testWaitFor(dependency: Awaitable): ContainerRegistryResourcePromise; - /** Adds a dependency on another resource */ - withDependency(dependency: Awaitable): ContainerRegistryResourcePromise; - /** Adds a dependency from a string or another resource */ - withUnionDependency(dependency: string | ResourceWithConnectionString | TestRedisResource | Awaitable): ContainerRegistryResourcePromise; - /** Sets the endpoints */ - withEndpoints(endpoints: string[]): ContainerRegistryResourcePromise; - /** Performs a cancellable operation */ - withCancellableOperation(operation: (arg: CancellationToken) => Promise): ContainerRegistryResourcePromise; - /** Adds a label to the resource */ - withMergeLabel(label: string): ContainerRegistryResourcePromise; - /** Adds a categorized label to the resource */ - withMergeLabelCategorized(label: string, category: string): ContainerRegistryResourcePromise; - /** Configures a named endpoint */ - withMergeEndpoint(endpointName: string, port: number): ContainerRegistryResourcePromise; - /** Configures a named endpoint with scheme */ - withMergeEndpointScheme(endpointName: string, port: number, scheme: string): ContainerRegistryResourcePromise; - /** - * Configures resource logging - * @param options Additional options. - */ - withMergeLogging(logLevel: string, options?: WithMergeLoggingOptions): ContainerRegistryResourcePromise; - /** - * Configures resource logging with file path - * @param options Additional options. - */ - withMergeLoggingPath(logLevel: string, logPath: string, options?: WithMergeLoggingPathOptions): ContainerRegistryResourcePromise; - /** Configures a route */ - withMergeRoute(path: string, method: string, handler: string, priority: number): ContainerRegistryResourcePromise; - /** Configures a route with middleware */ - withMergeRouteMiddleware(path: string, method: string, handler: string, priority: number, middleware: string): ContainerRegistryResourcePromise; -} - -export interface ContainerRegistryResourcePromise extends PromiseLike { - /** - * Configures the resource to use the specified container registry for container image operations. - * - * This method adds a `ContainerRegistryReferenceAnnotation` to the resource, - * indicating that the resource should use the specified container registry for container image operations. - * @param registry The container registry resource builder. - * @returns The resource builder for chaining. - */ - withContainerRegistry(registry: Awaitable): ContainerRegistryResourcePromise; - /** - * Configures custom base images for generated Dockerfiles. - * - * This extension method allows customization of the base images used in generated Dockerfiles. - * For multi-stage Dockerfiles (e.g., Python with UV), you can specify separate build and runtime images. - * Specify custom base images for a Python application: - * ``` - * var builder = DistributedApplication.CreateBuilder(args); - * builder.AddPythonApp("myapp", "path/to/app", "main.py") - * .WithDockerfileBaseImage( - * buildImage: "ghcr.io/astral-sh/uv:python3.12-bookworm-slim", - * runtimeImage: "python:3.12-slim-bookworm"); - * builder.Build().Run(); - * ``` - * @param options Additional options. - * @returns The resource builder. - */ - withDockerfileBaseImage(options?: WithDockerfileBaseImageOptions): ContainerRegistryResourcePromise; - /** - * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start. - * - * The command is considered valid if either: - * 1. It is an absolute or relative path (contains a directory separator) that points to an existing file, or - * 2. It is discoverable on the current process PATH (respecting PATHEXT on Windows). - * If the command is not found, a warning message will be logged but the resource will be allowed to attempt to start. - * @param command The command string (file name or path) that should be validated. - * @param options Additional options. - * @returns The resource builder. - */ - withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ContainerRegistryResourcePromise; - /** - * Configures a resource to use a session lifetime. - * @returns The resource builder. - */ - withSessionLifetime(): ContainerRegistryResourcePromise; - /** - * Configures a resource to use a persistent lifetime. - * @returns The resource builder. - */ - withPersistentLifetime(): ContainerRegistryResourcePromise; - /** - * Configures a resource to match the lifetime of another resource. - * - * The resource lifetime is evaluated from `sourceBuilder` when the application model is prepared, so later lifetime - * changes to the source resource are reflected by this resource. - * @param sourceBuilder The resource builder whose lifetime should be used. - * @returns The resource builder. - */ - withLifetimeOf(sourceBuilder: Awaitable): ContainerRegistryResourcePromise; - /** - * Configures a resource to use a persistent lifetime that ends when a parent process exits. - * @param parentProcessId The ID of the parent process to monitor. - * @returns The resource builder. - */ - withParentProcessLifetime(parentProcessId: number): ContainerRegistryResourcePromise; - /** - * Registers a callback to customize the URLs displayed for the resource. - * @param callback The callback that will customize URLs for the resource. - * @returns The resource builder. - */ - withUrls(callback: (obj: ResourceUrlsCallbackContext) => Promise): ContainerRegistryResourcePromise; - /** - * Adds or modifies displayed URLs - * @param options Additional options. - */ - withUrl(url: string | ReferenceExpression, options?: WithUrlOptions): ContainerRegistryResourcePromise; - /** - * Registers a callback to update the URL displayed for the endpoint with the specified name. - * @param endpointName The name of the endpoint to customize the URL for. - * @param callback The callback that will customize the URL. - * @returns The resource builder. - */ - withUrlForEndpoint(endpointName: string, callback: (obj: ResourceUrlAnnotation) => Promise): ContainerRegistryResourcePromise; - /** - * Excludes a resource from being published to the manifest. - * @returns The resource builder. - */ - excludeFromManifest(): ContainerRegistryResourcePromise; - /** - * Prevents resource from starting automatically - * @returns The resource builder. - */ - withExplicitStart(): ContainerRegistryResourcePromise; - /** - * Adds a health check by key - * @param key The key for the health check. - * @returns The resource builder. - */ - withHealthCheck(key: string): ContainerRegistryResourcePromise; - /** - * Adds a resource command - * - * The `WithCommand` method is used to add commands to the resource. Commands are displayed in the dashboard - * and can be executed by a user using the dashboard UI. - * When a command is executed, the `executeCommand` callback is called and is run inside the Aspire host. - * @param name The name of the command. The name uniquely identifies the command. - * @param displayName The display name visible in UI. - * @param executeCommand A callback that is executed when the command is executed. The callback is run inside the Aspire host. The callback result is used to indicate success or failure in the UI. - * @param options Additional options. - * @returns The resource builder. - */ - withCommand(name: string, displayName: string, executeCommand: (arg: ExecuteCommandContext) => Promise, options?: WithCommandOptions): ContainerRegistryResourcePromise; - /** Adds a command to the resource that starts a local process when invoked. */ - withProcessCommand(commandName: string, displayName: string, options: ProcessCommandExportOptions): ContainerRegistryResourcePromise; - /** - * Adds a command to the resource that starts a local process created by a callback when invoked. - * @param options Additional options. - * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. - */ - withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ContainerRegistryResourcePromise; - /** - * Adds a relationship to another resource using its builder. - * @param resourceBuilder The resource builder that the relationship is to. - * @param type The relationship type. - * @returns A resource builder. - */ - withRelationship(resourceBuilder: Awaitable, type: string): ContainerRegistryResourcePromise; - /** - * Sets the parent relationship - * @param parent The parent of `builder`. - * @returns A resource builder. - */ - withParentRelationship(parent: Awaitable): ContainerRegistryResourcePromise; - /** - * Sets a child relationship - * @param child The child of `builder`. - * @returns A resource builder. - */ - withChildRelationship(child: Awaitable): ContainerRegistryResourcePromise; - /** - * Specifies the icon to use when displaying the resource in the dashboard. - * @param iconName The name of the FluentUI icon to use. See https://aka.ms/fluentui-system-icons for available icons. - * @param options Additional options. - * @returns The resource builder. - */ - withIconName(iconName: string, options?: WithIconNameOptions): ContainerRegistryResourcePromise; - /** - * Exclude the resource from MCP operations using the Aspire MCP server. The resource is excluded from results that return resources, console logs and telemetry. - * @returns The resource builder. - */ - excludeFromMcp(): ContainerRegistryResourcePromise; - /** - * Hides the resource from default resource lists - * - * Use this method to hide resources that are implementation details and should never be displayed by default. - * Hidden resources can still be accessed directly by their name, by using `Show hidden resources` toggle in the dashboard or by using `aspire describe --include-hidden` from the CLI. - * @returns The resource builder. - */ - withHidden(): ContainerRegistryResourcePromise; - /** - * Hides the resource from default resource lists after successful completion - * - * This method is useful for one-off resources such as setup scripts, migrations, or build steps that should remain visible while running - * and then be hidden after successful completion. - * Hidden resources can still be accessed directly by their name, by using `Show hidden resources` toggle in the dashboard or by using `aspire describe --include-hidden` from the CLI. - * @param options Additional options. - * @returns The resource builder. - */ - withHiddenOnCompletion(options?: WithHiddenOnCompletionOptions): ContainerRegistryResourcePromise; - /** - * Adds a pipeline step to the resource that will be executed during deployment. - * @param stepName The unique name of the pipeline step. - * @param callback The callback to execute when the step runs. - * @param options Additional options. - * @returns The resource builder for chaining. - */ - withPipelineStepFactory(stepName: string, callback: (arg: PipelineStepContext) => Promise, options?: WithPipelineStepFactoryOptions): ContainerRegistryResourcePromise; - /** - * Registers a callback to be executed during the pipeline configuration phase, allowing modification of step dependencies and relationships. - * @param callback The callback function to execute during the configuration phase. - * @returns The resource builder for chaining. + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. */ - withPipelineConfiguration(callback: (obj: PipelineConfigurationContext) => Promise): ContainerRegistryResourcePromise; - /** - * Gets the name of the resource from a builder. - * - * Why this wrapper exists: This capability accesses a nested property - * (`resource.Resource.Name`) which requires a wrapper method. There is no single - * .NET method that returns just the resource name that could be annotated directly. - * @returns The resource name. - */ - getResourceName(): Promise; - /** - * Subscribes to the BeforeResourceStarted event. - * @param callback The callback to invoke when the event fires. - * @returns The resource builder. - */ - onBeforeResourceStarted(callback: (arg: BeforeResourceStartedEvent) => Promise): ContainerRegistryResourcePromise; - /** - * Subscribes to the ResourceStopped event. - * @param callback The callback to invoke when the event fires. - * @returns The resource builder. - */ - onResourceStopped(callback: (arg: ResourceStoppedEvent) => Promise): ContainerRegistryResourcePromise; - /** - * Subscribes to the InitializeResource event. - * @param callback The callback to invoke when the event fires. - * @returns The resource builder. - */ - onInitializeResource(callback: (arg: InitializeResourceEvent) => Promise): ContainerRegistryResourcePromise; - /** - * Subscribes to the ResourceReady event. - * @param callback The callback to invoke when the event fires. - * @returns The resource builder. - */ - onResourceReady(callback: (arg: ResourceReadyEvent) => Promise): ContainerRegistryResourcePromise; - /** - * Creates an execution configuration builder for the specified resource. - * @returns The execution configuration builder. - */ - createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerRegistryResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -12529,6 +13763,39 @@ class ContainerRegistryResourceImpl extends ResourceBuilderBase Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ContainerRegistryResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerRegistryResourcePromise { + const helpLink = options?.helpLink; + return new ContainerRegistryResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -12890,6 +14157,41 @@ class ContainerRegistryResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ContainerRegistryResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerRegistryResourcePromise { + return new ContainerRegistryResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -13225,6 +14527,30 @@ class ContainerRegistryResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ContainerRegistryResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerRegistryResourcePromise { + return new ContainerRegistryResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -13629,6 +14955,10 @@ class ContainerRegistryResourcePromiseImpl implements ContainerRegistryResourceP return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerRegistryResourcePromise { + return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ContainerRegistryResourcePromise { return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -13681,6 +15011,10 @@ class ContainerRegistryResourcePromiseImpl implements ContainerRegistryResourceP return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withProcessCommandFactory(commandName, displayName, createProcessSpec, options)), this._client); } + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerRegistryResourcePromise { + return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ContainerRegistryResourcePromise { return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -13741,6 +15075,10 @@ class ContainerRegistryResourcePromiseImpl implements ContainerRegistryResourceP return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerRegistryResourcePromise { + return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ContainerRegistryResourcePromise { return new ContainerRegistryResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -13986,14 +15324,24 @@ export interface ContainerResource { * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): ContainerResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): ContainerResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -14085,6 +15433,19 @@ export interface ContainerResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ContainerResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -14357,6 +15718,40 @@ export interface ContainerResource { * @returns The resource builder. */ withoutHttpsCertificate(): ContainerResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ContainerResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -14519,6 +15914,12 @@ export interface ContainerResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -14730,14 +16131,24 @@ export interface ContainerResourcePromise extends PromiseLike * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): ContainerResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): ContainerResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -14829,6 +16240,19 @@ export interface ContainerResourcePromise extends PromiseLike * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ContainerResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -15101,6 +16525,40 @@ export interface ContainerResourcePromise extends PromiseLike * @returns The resource builder. */ withoutHttpsCertificate(): ContainerResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ContainerResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -15263,6 +16721,12 @@ export interface ContainerResourcePromise extends PromiseLike * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -15739,8 +17203,10 @@ class ContainerResourceImpl extends ResourceBuilderBase * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. @@ -15750,6 +17216,34 @@ class ContainerResourceImpl extends ResourceBuilderBase return new ContainerResourcePromiseImpl(this._withContainerFilesInternal(destinationPath, sourcePath, options), this._client); } + /** @internal */ + private async _withContainerFilesCallbackInternal(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): Promise { + const callbackId = registerCallback(async (arg1Data: unknown, arg2Data: unknown) => { + const arg1Handle = wrapIfHandle(arg1Data) as ContainerFileSystemCallbackContextHandle; + const arg1 = new ContainerFileSystemCallbackContextImpl(arg1Handle, this._client); + const arg2 = CancellationToken.fromValue(arg2Data); + return await callback(arg1, arg2); + }); + const rpcArgs: Record = { builder: this._handle, destinationPath, callback: callbackId }; + if (options !== undefined) rpcArgs.options = options; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerFilesCallback', + rpcArgs + ); + return new ContainerResourceImpl(result, this._client); + } + + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._withContainerFilesCallbackInternal(destinationPath, callback, options), this._client); + } + /** @internal */ private async _withDockerfileBuilderInternal(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, stage?: string): Promise { const callbackId = registerCallback(async (argData: unknown) => { @@ -15951,6 +17445,39 @@ class ContainerResourceImpl extends ResourceBuilderBase return new ContainerResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ContainerResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerResourcePromise { + const helpLink = options?.helpLink; + return new ContainerResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -16962,6 +18489,76 @@ class ContainerResourceImpl extends ResourceBuilderBase return new ContainerResourcePromiseImpl(this._withoutHttpsCertificateInternal(), this._client); } + /** @internal */ + private async _withHttpsCertificateConfigurationInternal(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new ContainerResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ContainerResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -17478,6 +19075,30 @@ class ContainerResourceImpl extends ResourceBuilderBase return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ContainerResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -17977,6 +19598,10 @@ class ContainerResourcePromiseImpl implements ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withContainerFiles(destinationPath, sourcePath, options)), this._client); } + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withContainerFilesCallback(destinationPath, callback, options)), this._client); + } + withDockerfileBuilder(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, options?: WithDockerfileBuilderOptions): ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withDockerfileBuilder(contextPath, callback, options)), this._client); } @@ -18005,6 +19630,10 @@ class ContainerResourcePromiseImpl implements ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -18157,6 +19786,14 @@ class ContainerResourcePromiseImpl implements ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -18245,6 +19882,10 @@ class ContainerResourcePromiseImpl implements ContainerResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ContainerResourcePromise { + return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ContainerResourcePromise { return new ContainerResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -18423,6 +20064,19 @@ export interface CSharpAppResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): CSharpAppResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): CSharpAppResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -18701,6 +20355,40 @@ export interface CSharpAppResource { * @returns The resource builder. */ withoutHttpsCertificate(): CSharpAppResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): CSharpAppResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): CSharpAppResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -18855,6 +20543,12 @@ export interface CSharpAppResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): CSharpAppResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -18992,6 +20686,19 @@ export interface CSharpAppResourcePromise extends PromiseLike * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): CSharpAppResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): CSharpAppResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -19270,6 +20977,40 @@ export interface CSharpAppResourcePromise extends PromiseLike * @returns The resource builder. */ withoutHttpsCertificate(): CSharpAppResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): CSharpAppResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): CSharpAppResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -19424,6 +21165,12 @@ export interface CSharpAppResourcePromise extends PromiseLike * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): CSharpAppResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -19693,6 +21440,39 @@ class CSharpAppResourceImpl extends ResourceBuilderBase return new CSharpAppResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new CSharpAppResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): CSharpAppResourcePromise { + const helpLink = options?.helpLink; + return new CSharpAppResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -20724,6 +22504,76 @@ class CSharpAppResourceImpl extends ResourceBuilderBase return new CSharpAppResourcePromiseImpl(this._withoutHttpsCertificateInternal(), this._client); } + /** @internal */ + private async _withHttpsCertificateConfigurationInternal(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new CSharpAppResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new CSharpAppResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -21228,6 +23078,30 @@ class CSharpAppResourceImpl extends ResourceBuilderBase return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new CSharpAppResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -21687,6 +23561,10 @@ class CSharpAppResourcePromiseImpl implements CSharpAppResourcePromise { return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): CSharpAppResourcePromise { return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -21843,6 +23721,14 @@ class CSharpAppResourcePromiseImpl implements CSharpAppResourcePromise { return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): CSharpAppResourcePromise { return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -21931,6 +23817,10 @@ class CSharpAppResourcePromiseImpl implements CSharpAppResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): CSharpAppResourcePromise { + return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): CSharpAppResourcePromise { return new CSharpAppResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -22143,6 +24033,19 @@ export interface DotnetToolResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): DotnetToolResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): DotnetToolResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -22415,6 +24318,40 @@ export interface DotnetToolResource { * @returns The resource builder. */ withoutHttpsCertificate(): DotnetToolResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): DotnetToolResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): DotnetToolResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -22563,6 +24500,12 @@ export interface DotnetToolResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): DotnetToolResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -22734,6 +24677,19 @@ export interface DotnetToolResourcePromise extends PromiseLike Promise, options?: WithRequiredCommandValidationOptions): DotnetToolResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -23006,6 +24962,40 @@ export interface DotnetToolResourcePromise extends PromiseLike + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): DotnetToolResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): DotnetToolResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -23154,6 +25144,12 @@ export interface DotnetToolResourcePromise extends PromiseLike Promise): DotnetToolResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -23533,6 +25529,39 @@ class DotnetToolResourceImpl extends ResourceBuilderBase Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new DotnetToolResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): DotnetToolResourcePromise { + const helpLink = options?.helpLink; + return new DotnetToolResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -24544,6 +26573,76 @@ class DotnetToolResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new DotnetToolResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new DotnetToolResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -25029,6 +27128,30 @@ class DotnetToolResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new DotnetToolResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -25512,6 +27635,10 @@ class DotnetToolResourcePromiseImpl implements DotnetToolResourcePromise { return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): DotnetToolResourcePromise { return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -25664,6 +27791,14 @@ class DotnetToolResourcePromiseImpl implements DotnetToolResourcePromise { return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): DotnetToolResourcePromise { return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -25748,6 +27883,10 @@ class DotnetToolResourcePromiseImpl implements DotnetToolResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): DotnetToolResourcePromise { + return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): DotnetToolResourcePromise { return new DotnetToolResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -25934,6 +28073,19 @@ export interface ExecutableResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ExecutableResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ExecutableResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -26206,6 +28358,40 @@ export interface ExecutableResource { * @returns The resource builder. */ withoutHttpsCertificate(): ExecutableResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ExecutableResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExecutableResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -26354,6 +28540,12 @@ export interface ExecutableResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ExecutableResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -26492,6 +28684,19 @@ export interface ExecutableResourcePromise extends PromiseLike Promise, options?: WithRequiredCommandValidationOptions): ExecutableResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -26764,6 +28969,40 @@ export interface ExecutableResourcePromise extends PromiseLike + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ExecutableResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExecutableResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -26912,6 +29151,12 @@ export interface ExecutableResourcePromise extends PromiseLike Promise): ExecutableResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -27187,6 +29432,39 @@ class ExecutableResourceImpl extends ResourceBuilderBase Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ExecutableResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ExecutableResourcePromise { + const helpLink = options?.helpLink; + return new ExecutableResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -28198,6 +30476,76 @@ class ExecutableResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new ExecutableResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ExecutableResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -28683,6 +31031,30 @@ class ExecutableResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ExecutableResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -29142,6 +31514,10 @@ class ExecutableResourcePromiseImpl implements ExecutableResourcePromise { return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ExecutableResourcePromise { return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -29294,6 +31670,14 @@ class ExecutableResourcePromiseImpl implements ExecutableResourcePromise { return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ExecutableResourcePromise { return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -29378,6 +31762,10 @@ class ExecutableResourcePromiseImpl implements ExecutableResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ExecutableResourcePromise { + return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ExecutableResourcePromise { return new ExecutableResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -29526,6 +31914,19 @@ export interface ExternalServiceResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ExternalServiceResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ExternalServiceResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -29606,6 +32007,23 @@ export interface ExternalServiceResource { * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. */ withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ExternalServiceResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExternalServiceResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -29707,6 +32125,12 @@ export interface ExternalServiceResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ExternalServiceResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -29810,6 +32234,19 @@ export interface ExternalServiceResourcePromise extends PromiseLike Promise, options?: WithRequiredCommandValidationOptions): ExternalServiceResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -29890,6 +32327,23 @@ export interface ExternalServiceResourcePromise extends PromiseLike Promise, options?: ProcessCommandResultExportOptions): ExternalServiceResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExternalServiceResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -29991,6 +32445,12 @@ export interface ExternalServiceResourcePromise extends PromiseLike Promise): ExternalServiceResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -30167,6 +32627,39 @@ class ExternalServiceResourceImpl extends ResourceBuilderBase Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ExternalServiceResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ExternalServiceResourcePromise { + const helpLink = options?.helpLink; + return new ExternalServiceResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -30528,6 +33021,41 @@ class ExternalServiceResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ExternalServiceResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExternalServiceResourcePromise { + return new ExternalServiceResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -30863,6 +33391,30 @@ class ExternalServiceResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ExternalServiceResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ExternalServiceResourcePromise { + return new ExternalServiceResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -31271,6 +33823,10 @@ class ExternalServiceResourcePromiseImpl implements ExternalServiceResourcePromi return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ExternalServiceResourcePromise { + return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ExternalServiceResourcePromise { return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -31323,6 +33879,10 @@ class ExternalServiceResourcePromiseImpl implements ExternalServiceResourcePromi return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withProcessCommandFactory(commandName, displayName, createProcessSpec, options)), this._client); } + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ExternalServiceResourcePromise { + return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ExternalServiceResourcePromise { return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -31383,6 +33943,10 @@ class ExternalServiceResourcePromiseImpl implements ExternalServiceResourcePromi return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ExternalServiceResourcePromise { + return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ExternalServiceResourcePromise { return new ExternalServiceResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -31532,6 +34096,19 @@ export interface ParameterResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ParameterResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ParameterResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -31612,6 +34189,23 @@ export interface ParameterResource { * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. */ withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ParameterResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ParameterResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -31713,6 +34307,12 @@ export interface ParameterResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ParameterResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -31824,6 +34424,19 @@ export interface ParameterResourcePromise extends PromiseLike * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ParameterResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ParameterResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -31904,6 +34517,23 @@ export interface ParameterResourcePromise extends PromiseLike * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. */ withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ParameterResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ParameterResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -32005,6 +34635,12 @@ export interface ParameterResourcePromise extends PromiseLike * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ParameterResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -32199,6 +34835,39 @@ class ParameterResourceImpl extends ResourceBuilderBase return new ParameterResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ParameterResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ParameterResourcePromise { + const helpLink = options?.helpLink; + return new ParameterResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -32560,6 +35229,41 @@ class ParameterResourceImpl extends ResourceBuilderBase return new ParameterResourcePromiseImpl(this._withProcessCommandFactoryInternal(commandName, displayName, createProcessSpec, options), this._client); } + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ParameterResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ParameterResourcePromise { + return new ParameterResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -32895,6 +35599,30 @@ class ParameterResourceImpl extends ResourceBuilderBase return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ParameterResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ParameterResourcePromise { + return new ParameterResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -33307,6 +36035,10 @@ class ParameterResourcePromiseImpl implements ParameterResourcePromise { return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ParameterResourcePromise { + return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ParameterResourcePromise { return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -33359,6 +36091,10 @@ class ParameterResourcePromiseImpl implements ParameterResourcePromise { return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withProcessCommandFactory(commandName, displayName, createProcessSpec, options)), this._client); } + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ParameterResourcePromise { + return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ParameterResourcePromise { return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -33419,6 +36155,10 @@ class ParameterResourcePromiseImpl implements ParameterResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ParameterResourcePromise { + return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ParameterResourcePromise { return new ParameterResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -33590,6 +36330,19 @@ export interface ProjectResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ProjectResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ProjectResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -33868,6 +36621,40 @@ export interface ProjectResource { * @returns The resource builder. */ withoutHttpsCertificate(): ProjectResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ProjectResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ProjectResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -34022,6 +36809,12 @@ export interface ProjectResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ProjectResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -34159,6 +36952,19 @@ export interface ProjectResourcePromise extends PromiseLike { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ProjectResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ProjectResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -34437,6 +37243,40 @@ export interface ProjectResourcePromise extends PromiseLike { * @returns The resource builder. */ withoutHttpsCertificate(): ProjectResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ProjectResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ProjectResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -34591,6 +37431,12 @@ export interface ProjectResourcePromise extends PromiseLike { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ProjectResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -34861,6 +37707,39 @@ class ProjectResourceImpl extends ResourceBuilderBase imp return new ProjectResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ProjectResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ProjectResourcePromise { + const helpLink = options?.helpLink; + return new ProjectResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -35892,6 +38771,76 @@ class ProjectResourceImpl extends ResourceBuilderBase imp return new ProjectResourcePromiseImpl(this._withoutHttpsCertificateInternal(), this._client); } + /** @internal */ + private async _withHttpsCertificateConfigurationInternal(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new ProjectResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ProjectResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -36396,6 +39345,30 @@ class ProjectResourceImpl extends ResourceBuilderBase imp return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ProjectResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -36855,6 +39828,10 @@ class ProjectResourcePromiseImpl implements ProjectResourcePromise { return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ProjectResourcePromise { return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -37011,6 +39988,14 @@ class ProjectResourcePromiseImpl implements ProjectResourcePromise { return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ProjectResourcePromise { return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -37099,6 +40084,10 @@ class ProjectResourcePromiseImpl implements ProjectResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ProjectResourcePromise { + return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ProjectResourcePromise { return new ProjectResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -37351,14 +40340,24 @@ export interface TestDatabaseResource { * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestDatabaseResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestDatabaseResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -37450,6 +40449,19 @@ export interface TestDatabaseResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): TestDatabaseResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestDatabaseResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -37722,6 +40734,40 @@ export interface TestDatabaseResource { * @returns The resource builder. */ withoutHttpsCertificate(): TestDatabaseResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestDatabaseResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestDatabaseResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -37884,6 +40930,12 @@ export interface TestDatabaseResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestDatabaseResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -38095,14 +41147,24 @@ export interface TestDatabaseResourcePromise extends PromiseLike Promise, options?: ContainerFilesOptions): TestDatabaseResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -38194,6 +41256,19 @@ export interface TestDatabaseResourcePromise extends PromiseLike Promise, options?: WithRequiredCommandValidationOptions): TestDatabaseResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -38466,6 +41541,40 @@ export interface TestDatabaseResourcePromise extends PromiseLike + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestDatabaseResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestDatabaseResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -38628,6 +41737,12 @@ export interface TestDatabaseResourcePromise extends PromiseLike Promise): TestDatabaseResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -39103,8 +42218,10 @@ class TestDatabaseResourceImpl extends ResourceBuilderBase Promise, options?: ContainerFilesOptions): Promise { + const callbackId = registerCallback(async (arg1Data: unknown, arg2Data: unknown) => { + const arg1Handle = wrapIfHandle(arg1Data) as ContainerFileSystemCallbackContextHandle; + const arg1 = new ContainerFileSystemCallbackContextImpl(arg1Handle, this._client); + const arg2 = CancellationToken.fromValue(arg2Data); + return await callback(arg1, arg2); + }); + const rpcArgs: Record = { builder: this._handle, destinationPath, callback: callbackId }; + if (options !== undefined) rpcArgs.options = options; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerFilesCallback', + rpcArgs + ); + return new TestDatabaseResourceImpl(result, this._client); + } + + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._withContainerFilesCallbackInternal(destinationPath, callback, options), this._client); + } + /** @internal */ private async _withDockerfileBuilderInternal(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, stage?: string): Promise { const callbackId = registerCallback(async (argData: unknown) => { @@ -39315,6 +42460,39 @@ class TestDatabaseResourceImpl extends ResourceBuilderBase Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new TestDatabaseResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestDatabaseResourcePromise { + const helpLink = options?.helpLink; + return new TestDatabaseResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -40326,6 +43504,76 @@ class TestDatabaseResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new TestDatabaseResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new TestDatabaseResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -40842,6 +44090,30 @@ class TestDatabaseResourceImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new TestDatabaseResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -41341,6 +44613,10 @@ class TestDatabaseResourcePromiseImpl implements TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withContainerFiles(destinationPath, sourcePath, options)), this._client); } + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withContainerFilesCallback(destinationPath, callback, options)), this._client); + } + withDockerfileBuilder(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, options?: WithDockerfileBuilderOptions): TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withDockerfileBuilder(contextPath, callback, options)), this._client); } @@ -41369,6 +44645,10 @@ class TestDatabaseResourcePromiseImpl implements TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -41521,6 +44801,14 @@ class TestDatabaseResourcePromiseImpl implements TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -41609,6 +44897,10 @@ class TestDatabaseResourcePromiseImpl implements TestDatabaseResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -41861,14 +45153,24 @@ export interface TestRedisResource { * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestRedisResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestRedisResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -41960,6 +45262,19 @@ export interface TestRedisResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): TestRedisResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestRedisResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -42248,6 +45563,40 @@ export interface TestRedisResource { * @returns The resource builder. */ withoutHttpsCertificate(): TestRedisResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestRedisResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestRedisResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -42416,6 +45765,12 @@ export interface TestRedisResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestRedisResourcePromise; /** * Adds a child database to a test Redis resource * @@ -42669,14 +46024,24 @@ export interface TestRedisResourcePromise extends PromiseLike * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestRedisResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestRedisResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -42768,6 +46133,19 @@ export interface TestRedisResourcePromise extends PromiseLike * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): TestRedisResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestRedisResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -43056,6 +46434,40 @@ export interface TestRedisResourcePromise extends PromiseLike * @returns The resource builder. */ withoutHttpsCertificate(): TestRedisResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestRedisResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestRedisResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -43224,6 +46636,12 @@ export interface TestRedisResourcePromise extends PromiseLike * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestRedisResourcePromise; /** * Adds a child database to a test Redis resource * @@ -43741,8 +47159,10 @@ class TestRedisResourceImpl extends ResourceBuilderBase * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. @@ -43752,6 +47172,34 @@ class TestRedisResourceImpl extends ResourceBuilderBase return new TestRedisResourcePromiseImpl(this._withContainerFilesInternal(destinationPath, sourcePath, options), this._client); } + /** @internal */ + private async _withContainerFilesCallbackInternal(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): Promise { + const callbackId = registerCallback(async (arg1Data: unknown, arg2Data: unknown) => { + const arg1Handle = wrapIfHandle(arg1Data) as ContainerFileSystemCallbackContextHandle; + const arg1 = new ContainerFileSystemCallbackContextImpl(arg1Handle, this._client); + const arg2 = CancellationToken.fromValue(arg2Data); + return await callback(arg1, arg2); + }); + const rpcArgs: Record = { builder: this._handle, destinationPath, callback: callbackId }; + if (options !== undefined) rpcArgs.options = options; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerFilesCallback', + rpcArgs + ); + return new TestRedisResourceImpl(result, this._client); + } + + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._withContainerFilesCallbackInternal(destinationPath, callback, options), this._client); + } + /** @internal */ private async _withDockerfileBuilderInternal(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, stage?: string): Promise { const callbackId = registerCallback(async (argData: unknown) => { @@ -43953,6 +47401,39 @@ class TestRedisResourceImpl extends ResourceBuilderBase return new TestRedisResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new TestRedisResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestRedisResourcePromise { + const helpLink = options?.helpLink; + return new TestRedisResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -45000,6 +48481,76 @@ class TestRedisResourceImpl extends ResourceBuilderBase return new TestRedisResourcePromiseImpl(this._withoutHttpsCertificateInternal(), this._client); } + /** @internal */ + private async _withHttpsCertificateConfigurationInternal(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new TestRedisResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new TestRedisResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -45540,6 +49091,30 @@ class TestRedisResourceImpl extends ResourceBuilderBase return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new TestRedisResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _addTestChildDatabaseInternal(name: string, databaseName?: string): Promise { const rpcArgs: Record = { builder: this._handle, name }; @@ -46226,6 +49801,10 @@ class TestRedisResourcePromiseImpl implements TestRedisResourcePromise { return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withContainerFiles(destinationPath, sourcePath, options)), this._client); } + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withContainerFilesCallback(destinationPath, callback, options)), this._client); + } + withDockerfileBuilder(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, options?: WithDockerfileBuilderOptions): TestRedisResourcePromise { return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withDockerfileBuilder(contextPath, callback, options)), this._client); } @@ -46254,6 +49833,10 @@ class TestRedisResourcePromiseImpl implements TestRedisResourcePromise { return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): TestRedisResourcePromise { return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -46414,6 +49997,14 @@ class TestRedisResourcePromiseImpl implements TestRedisResourcePromise { return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): TestRedisResourcePromise { return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -46506,6 +50097,10 @@ class TestRedisResourcePromiseImpl implements TestRedisResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestRedisResourcePromise { + return new TestRedisResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + addTestChildDatabase(name: string, options?: AddTestChildDatabaseOptions): TestDatabaseResourcePromise { return new TestDatabaseResourcePromiseImpl(this._promise.then(obj => obj.addTestChildDatabase(name, options)), this._client); } @@ -46806,14 +50401,24 @@ export interface TestVaultResource { * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestVaultResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestVaultResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -46905,6 +50510,19 @@ export interface TestVaultResource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): TestVaultResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestVaultResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -47177,6 +50795,40 @@ export interface TestVaultResource { * @returns The resource builder. */ withoutHttpsCertificate(): TestVaultResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestVaultResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestVaultResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -47339,6 +50991,12 @@ export interface TestVaultResource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestVaultResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -47552,14 +51210,24 @@ export interface TestVaultResourcePromise extends PromiseLike * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. * @returns The resource builder. */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestVaultResourcePromise; + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestVaultResourcePromise; /** * Configures the resource to use a programmatically generated Dockerfile * @@ -47651,6 +51319,19 @@ export interface TestVaultResourcePromise extends PromiseLike * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): TestVaultResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestVaultResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -47923,6 +51604,40 @@ export interface TestVaultResourcePromise extends PromiseLike * @returns The resource builder. */ withoutHttpsCertificate(): TestVaultResourcePromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestVaultResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestVaultResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -48085,6 +51800,12 @@ export interface TestVaultResourcePromise extends PromiseLike * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestVaultResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -48562,8 +52283,10 @@ class TestVaultResourceImpl extends ResourceBuilderBase * * In run mode, Aspire copies the files into the container and applies owner, group, and umask options. * In publish mode, Aspire creates a read-only bind mount and ignores those options. - * Inline file entries and callbacks are only available in .NET apphosts because ATS does not support the recursive, - * polymorphic `ContainerFileSystemItem` hierarchy or callbacks that use .NET services. + * To produce entries dynamically (including inline file contents and OpenSSL certificate files), polyglot app hosts + * use the `withContainerFilesCallback` overload and build the entries via the factory methods on + * `ContainerFileSystemCallbackContext`. Passing a pre-built `ContainerFileSystemItem` collection + * remains .NET-only. * @param destinationPath The destination absolute path in the container. * @param sourcePath The source path on the host to copy files from. * @param options Additional options. @@ -48573,6 +52296,34 @@ class TestVaultResourceImpl extends ResourceBuilderBase return new TestVaultResourcePromiseImpl(this._withContainerFilesInternal(destinationPath, sourcePath, options), this._client); } + /** @internal */ + private async _withContainerFilesCallbackInternal(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): Promise { + const callbackId = registerCallback(async (arg1Data: unknown, arg2Data: unknown) => { + const arg1Handle = wrapIfHandle(arg1Data) as ContainerFileSystemCallbackContextHandle; + const arg1 = new ContainerFileSystemCallbackContextImpl(arg1Handle, this._client); + const arg2 = CancellationToken.fromValue(arg2Data); + return await callback(arg1, arg2); + }); + const rpcArgs: Record = { builder: this._handle, destinationPath, callback: callbackId }; + if (options !== undefined) rpcArgs.options = options; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerFilesCallback', + rpcArgs + ); + return new TestVaultResourceImpl(result, this._client); + } + + /** + * Creates or updates files and folders in a container using entries produced by a callback. + * @param destinationPath The destination absolute path in the container. + * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. + * @param options Additional options. + * @returns The resource builder. + */ + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._withContainerFilesCallbackInternal(destinationPath, callback, options), this._client); + } + /** @internal */ private async _withDockerfileBuilderInternal(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, stage?: string): Promise { const callbackId = registerCallback(async (argData: unknown) => { @@ -48774,6 +52525,39 @@ class TestVaultResourceImpl extends ResourceBuilderBase return new TestVaultResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new TestVaultResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestVaultResourcePromise { + const helpLink = options?.helpLink; + return new TestVaultResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -49785,6 +53569,76 @@ class TestVaultResourceImpl extends ResourceBuilderBase return new TestVaultResourcePromiseImpl(this._withoutHttpsCertificateInternal(), this._client); } + /** @internal */ + private async _withHttpsCertificateConfigurationInternal(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new TestVaultResourceImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new TestVaultResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -50301,6 +54155,30 @@ class TestVaultResourceImpl extends ResourceBuilderBase return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new TestVaultResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -50815,6 +54693,10 @@ class TestVaultResourcePromiseImpl implements TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withContainerFiles(destinationPath, sourcePath, options)), this._client); } + withContainerFilesCallback(destinationPath: string, callback: (arg1: ContainerFileSystemCallbackContext, arg2: CancellationToken) => Promise, options?: ContainerFilesOptions): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withContainerFilesCallback(destinationPath, callback, options)), this._client); + } + withDockerfileBuilder(contextPath: string, callback: (arg: DockerfileBuilderCallbackContext) => Promise, options?: WithDockerfileBuilderOptions): TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withDockerfileBuilder(contextPath, callback, options)), this._client); } @@ -50843,6 +54725,10 @@ class TestVaultResourcePromiseImpl implements TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -50995,6 +54881,14 @@ class TestVaultResourcePromiseImpl implements TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -51083,6 +54977,10 @@ class TestVaultResourcePromiseImpl implements TestVaultResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): TestVaultResourcePromise { + return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): TestVaultResourcePromise { return new TestVaultResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -51550,6 +55448,19 @@ export interface Resource { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -51630,6 +55541,23 @@ export interface Resource { * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. */ withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -51731,6 +55659,12 @@ export interface Resource { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -51829,6 +55763,19 @@ export interface ResourcePromise extends PromiseLike { * @returns The resource builder. */ withRequiredCommand(command: string, options?: WithRequiredCommandOptions): ResourcePromise; + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ResourcePromise; /** * Configures a resource to use a session lifetime. * @returns The resource builder. @@ -51909,6 +55856,23 @@ export interface ResourcePromise extends PromiseLike { * @deprecated Use withProcessCommand with createProcessSpec in the options object instead. */ withProcessCommandFactory(commandName: string, displayName: string, createProcessSpec: (arg: ExecuteCommandContext) => Promise, options?: ProcessCommandResultExportOptions): ResourcePromise; + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ResourcePromise; /** * Adds a relationship to another resource using its builder. * @param resourceBuilder The resource builder that the relationship is to. @@ -52010,6 +55974,12 @@ export interface ResourcePromise extends PromiseLike { * @returns The execution configuration builder. */ createExecutionConfiguration(): ExecutionConfigurationBuilderPromise; + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ResourcePromise; /** * Adds an optional string parameter * @param options Additional options. @@ -52163,6 +56133,39 @@ class ResourceImpl extends ResourceBuilderBase implements Resou return new ResourcePromiseImpl(this._withRequiredCommandInternal(command, helpLink), this._client); } + /** @internal */ + private async _withRequiredCommandValidationInternal(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, helpLink?: string): Promise { + const validationCallbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as RequiredCommandValidationContextHandle; + const arg = new RequiredCommandValidationContextImpl(argHandle, this._client); + return await validationCallback(arg); + }); + const rpcArgs: Record = { builder: this._handle, command, validationCallback: validationCallbackId }; + if (helpLink !== undefined) rpcArgs.helpLink = helpLink; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withRequiredCommandValidation', + rpcArgs + ); + return new ResourceImpl(result, this._client); + } + + /** + * Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic. + * + * The command is first resolved to a full path. If found, the validation callback is invoked with the context containing the resolved path and service provider. + * The callback should return a `RequiredCommandValidationResult` indicating whether the command is valid, + * which can be created via `Success` or `Failure`. + * If the command is not found or fails validation, a warning message will be logged but the resource will be allowed to attempt to start. + * @param command The command string (file name or path) that should be validated. + * @param validationCallback A callback that validates the resolved command path. Receives a `RequiredCommandValidationContext` and returns a `RequiredCommandValidationResult`. + * @param options Additional options. + * @returns The resource builder. + */ + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ResourcePromise { + const helpLink = options?.helpLink; + return new ResourcePromiseImpl(this._withRequiredCommandValidationInternal(command, validationCallback, helpLink), this._client); + } + /** @internal */ private async _withSessionLifetimeInternal(): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -52524,6 +56527,41 @@ class ResourceImpl extends ResourceBuilderBase implements Resou return new ResourcePromiseImpl(this._withProcessCommandFactoryInternal(commandName, displayName, createProcessSpec, options), this._client); } + /** @internal */ + private async _subscribeHttpsEndpointsUpdateInternal(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (objData: unknown) => { + const objHandle = wrapIfHandle(objData) as HttpsEndpointUpdateCallbackContextHandle; + const obj = new HttpsEndpointUpdateCallbackContextImpl(objHandle, this._client); + await callback(obj); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/subscribeHttpsEndpointsUpdate', + rpcArgs + ); + return new ResourceImpl(result, this._client); + } + + /** + * Subscribes to the `BeforeStartEvent` and invokes the specified callback when an HTTPS certificate is determined to be available for the resource. This is used to conditionally update endpoint URI schemes or perform other HTTPS-related configuration at startup. + * + * The callback is invoked when either: + * - + * - + * Switch an endpoint to HTTPS when a certificate is available: + * ``` + * builder.SubscribeHttpsEndpointsUpdate(ctx => + * { + * builder.WithEndpoint("http", ep => ep.UriScheme = "https"); + * }); + * ``` + * @param callback The callback to invoke when HTTPS is enabled. Receives an `HttpsEndpointUpdateCallbackContext` providing access to the service provider, resource, and application model. + * @returns The updated resource builder. + */ + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ResourcePromise { + return new ResourcePromiseImpl(this._subscribeHttpsEndpointsUpdateInternal(callback), this._client); + } + /** @internal */ private async _withRelationshipInternal(resourceBuilder: Awaitable, type: string): Promise { resourceBuilder = isPromiseLike(resourceBuilder) ? await resourceBuilder : resourceBuilder; @@ -52859,6 +56897,30 @@ class ResourceImpl extends ResourceBuilderBase implements Resou return new ExecutionConfigurationBuilderPromiseImpl(promise, this._client); } + /** @internal */ + private async _withContainerBuildOptionsInternal(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as ContainerBuildOptionsCallbackContextHandle; + const arg = new ContainerBuildOptionsCallbackContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withContainerBuildOptions', + rpcArgs + ); + return new ResourceImpl(result, this._client); + } + + /** + * Configures container build options for a compute resource using an async callback. + * @param callback An async callback to configure container build options. + * @returns A reference to the `IResourceBuilder`1`. + */ + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ResourcePromise { + return new ResourcePromiseImpl(this._withContainerBuildOptionsInternal(callback), this._client); + } + /** @internal */ private async _withOptionalStringInternal(value?: string, enabled?: boolean): Promise { const rpcArgs: Record = { builder: this._handle }; @@ -53263,6 +57325,10 @@ class ResourcePromiseImpl implements ResourcePromise { return new ResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommand(command, options)), this._client); } + withRequiredCommandValidation(command: string, validationCallback: (arg: RequiredCommandValidationContext) => Promise, options?: WithRequiredCommandValidationOptions): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.withRequiredCommandValidation(command, validationCallback, options)), this._client); + } + withSessionLifetime(): ResourcePromise { return new ResourcePromiseImpl(this._promise.then(obj => obj.withSessionLifetime()), this._client); } @@ -53315,6 +57381,10 @@ class ResourcePromiseImpl implements ResourcePromise { return new ResourcePromiseImpl(this._promise.then(obj => obj.withProcessCommandFactory(commandName, displayName, createProcessSpec, options)), this._client); } + subscribeHttpsEndpointsUpdate(callback: (obj: HttpsEndpointUpdateCallbackContext) => Promise): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.subscribeHttpsEndpointsUpdate(callback)), this._client); + } + withRelationship(resourceBuilder: Awaitable, type: string): ResourcePromise { return new ResourcePromiseImpl(this._promise.then(obj => obj.withRelationship(resourceBuilder, type)), this._client); } @@ -53375,6 +57445,10 @@ class ResourcePromiseImpl implements ResourcePromise { return new ExecutionConfigurationBuilderPromiseImpl(this._promise.then(obj => obj.createExecutionConfiguration()), this._client); } + withContainerBuildOptions(callback: (arg: ContainerBuildOptionsCallbackContext) => Promise): ResourcePromise { + return new ResourcePromiseImpl(this._promise.then(obj => obj.withContainerBuildOptions(callback)), this._client); + } + withOptionalString(options?: WithOptionalStringOptions): ResourcePromise { return new ResourcePromiseImpl(this._promise.then(obj => obj.withOptionalString(options)), this._client); } @@ -54709,6 +58783,23 @@ export interface ResourceWithEnvironment { * @returns The resource builder. */ withoutHttpsCertificate(): ResourceWithEnvironmentPromise; + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ResourceWithEnvironmentPromise; /** Configures environment with callback (test version) */ testWithEnvironmentCallback(callback: (arg: TestEnvironmentContext) => Promise): ResourceWithEnvironmentPromise; /** Sets environment variables */ @@ -54807,6 +58898,23 @@ export interface ResourceWithEnvironmentPromise extends PromiseLike + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ResourceWithEnvironmentPromise; /** Configures environment with callback (test version) */ testWithEnvironmentCallback(callback: (arg: TestEnvironmentContext) => Promise): ResourceWithEnvironmentPromise; /** Sets environment variables */ @@ -55049,6 +59157,41 @@ class ResourceWithEnvironmentImpl extends ResourceBuilderBase Promise): Promise { + const callbackId = registerCallback(async (argData: unknown) => { + const argHandle = wrapIfHandle(argData) as HttpsCertificateConfigurationCallbackAnnotationContextHandle; + const arg = new HttpsCertificateConfigurationCallbackAnnotationContextImpl(argHandle, this._client); + await callback(arg); + }); + const rpcArgs: Record = { builder: this._handle, callback: callbackId }; + const result = await this._client.invokeCapability( + 'Aspire.Hosting/withHttpsCertificateConfiguration', + rpcArgs + ); + return new ResourceWithEnvironmentImpl(result, this._client); + } + + /** + * Adds a callback that allows configuring the resource to use a specific HTTPS/TLS certificate key pair for server authentication. + * + * Pass the path to the PFX certificate file to the container arguments. + * ``` + * builder.AddContainer("my-service", "my-image") + * .WithHttpsCertificateConfiguration(ctx => + * { + * ctx.Arguments.Add("--https-certificate-path"); + * ctx.Arguments.Add(ctx.PfxPath); + * return Task.CompletedTask; + * }); + * ``` + * @param callback The callback to configure the resource to use a certificate key pair. + * @returns The updated resource builder. + */ + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ResourceWithEnvironmentPromise { + return new ResourceWithEnvironmentPromiseImpl(this._withHttpsCertificateConfigurationInternal(callback), this._client); + } + /** @internal */ private async _testWithEnvironmentCallbackInternal(callback: (arg: TestEnvironmentContext) => Promise): Promise { const callbackId = registerCallback(async (argData: unknown) => { @@ -55139,6 +59282,10 @@ class ResourceWithEnvironmentPromiseImpl implements ResourceWithEnvironmentPromi return new ResourceWithEnvironmentPromiseImpl(this._promise.then(obj => obj.withoutHttpsCertificate()), this._client); } + withHttpsCertificateConfiguration(callback: (arg: HttpsCertificateConfigurationCallbackAnnotationContext) => Promise): ResourceWithEnvironmentPromise { + return new ResourceWithEnvironmentPromiseImpl(this._promise.then(obj => obj.withHttpsCertificateConfiguration(callback)), this._client); + } + testWithEnvironmentCallback(callback: (arg: TestEnvironmentContext) => Promise): ResourceWithEnvironmentPromise { return new ResourceWithEnvironmentPromiseImpl(this._promise.then(obj => obj.testWithEnvironmentCallback(callback)), this._client); } @@ -55440,6 +59587,8 @@ registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.BeforeStar registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.CommandLineArgsCallbackContext', (handle, client) => new CommandLineArgsCallbackContextImpl(handle as CommandLineArgsCallbackContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.CommandLineArgsEditor', (handle, client) => new CommandLineArgsEditorImpl(handle as CommandLineArgsEditorHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent', (handle, client) => new ConnectionStringAvailableEventImpl(handle as ConnectionStringAvailableEventHandle, client)); +registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerBuildOptionsCallbackContext', (handle, client) => new ContainerBuildOptionsCallbackContextImpl(handle as ContainerBuildOptionsCallbackContextHandle, client)); +registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerFileSystemCallbackContext', (handle, client) => new ContainerFileSystemCallbackContextImpl(handle as ContainerFileSystemCallbackContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptions', (handle, client) => new ContainerImagePushOptionsImpl(handle as ContainerImagePushOptionsHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImagePushOptionsCallbackContext', (handle, client) => new ContainerImagePushOptionsCallbackContextImpl(handle as ContainerImagePushOptionsCallbackContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ContainerImageReference', (handle, client) => new ContainerImageReferenceImpl(handle as ContainerImageReferenceHandle, client)); @@ -55460,6 +59609,8 @@ registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.Environmen registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.Ats.EventingSubscriberRegistrationContext', (handle, client) => new EventingSubscriberRegistrationContextImpl(handle as EventingSubscriberRegistrationContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ExecuteCommandContext', (handle, client) => new ExecuteCommandContextImpl(handle as ExecuteCommandContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpCommandPrepareRequestContext', (handle, client) => new HttpCommandPrepareRequestContextImpl(handle as HttpCommandPrepareRequestContextHandle, client)); +registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsCertificateConfigurationCallbackAnnotationContext', (handle, client) => new HttpsCertificateConfigurationCallbackAnnotationContextImpl(handle as HttpsCertificateConfigurationCallbackAnnotationContextHandle, client)); +registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.HttpsEndpointUpdateCallbackContext', (handle, client) => new HttpsEndpointUpdateCallbackContextImpl(handle as HttpsEndpointUpdateCallbackContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.InitializeResourceEvent', (handle, client) => new InitializeResourceEventImpl(handle as InitializeResourceEventHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.InputsDialogValidationContext', (handle, client) => new InputsDialogValidationContextImpl(handle as InputsDialogValidationContextHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.LogFacade', (handle, client) => new LogFacadeImpl(handle as LogFacadeHandle, client)); @@ -55472,6 +59623,8 @@ registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.Pipelines.PipelineStepFacto registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.Pipelines.PipelineSummary', (handle, client) => new PipelineSummaryImpl(handle as PipelineSummaryHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ProjectResourceOptions', (handle, client) => new ProjectResourceOptionsImpl(handle as ProjectResourceOptionsHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ReferenceExpressionBuilder', (handle, client) => new ReferenceExpressionBuilderImpl(handle as ReferenceExpressionBuilderHandle, client)); +registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationContext', (handle, client) => new RequiredCommandValidationContextImpl(handle as RequiredCommandValidationContextHandle, client)); +registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.RequiredCommandValidationResult', (handle, client) => new RequiredCommandValidationResultImpl(handle as RequiredCommandValidationResultHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceCommandService', (handle, client) => new ResourceCommandServiceImpl(handle as ResourceCommandServiceHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceEndpointsAllocatedEvent', (handle, client) => new ResourceEndpointsAllocatedEventImpl(handle as ResourceEndpointsAllocatedEventHandle, client)); registerHandleWrapper('Aspire.Hosting/Aspire.Hosting.ApplicationModel.ResourceLoggerService', (handle, client) => new ResourceLoggerServiceImpl(handle as ResourceLoggerServiceHandle, client)); diff --git a/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs b/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs new file mode 100644 index 00000000000..bd9578f9a73 --- /dev/null +++ b/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Hosting.ApplicationModel; +using Microsoft.Extensions.DependencyInjection; + +namespace Aspire.Hosting.Containers.Tests; + +public class ContainerFileSystemCallbackContextTests +{ + private static ContainerFileSystemCallbackContext CreateContext() + { + return new ContainerFileSystemCallbackContext + { + Model = new ContainerResource("test"), + ServiceProvider = new ServiceCollection().BuildServiceProvider(), + }; + } + + [Fact] + public void CreateFileProducesContainerFileWithInlineContents() + { + var context = CreateContext(); + + var item = context.CreateFile("app.conf", contents: "key=value", mode: 420 /* 0o644 */, owner: 1000, group: 1000, continueOnError: true); + + var file = Assert.IsType(item); + Assert.Equal("app.conf", file.Name); + Assert.Equal("key=value", file.Contents); + Assert.Null(file.SourcePath); + Assert.Equal((UnixFileMode)420 /* 0o644 */, file.Mode); + Assert.Equal(1000, file.Owner); + Assert.Equal(1000, file.Group); + Assert.True(file.ContinueOnError); + } + + [Fact] + public void CreateFileProducesContainerFileWithSourcePath() + { + var context = CreateContext(); + + var item = context.CreateFile("app.conf", sourcePath: "/host/app.conf"); + + var file = Assert.IsType(item); + Assert.Equal("/host/app.conf", file.SourcePath); + Assert.Null(file.Contents); + // A null mode is converted to UnixFileMode 0, which means "inherit". + Assert.Equal((UnixFileMode)0, file.Mode); + } + + [Fact] + public void CreateCertificateFileProducesContainerOpenSSLCertificateFile() + { + var context = CreateContext(); + + var item = context.CreateCertificateFile("server.pem", contents: "-----BEGIN CERTIFICATE-----"); + + var cert = Assert.IsType(item); + Assert.Equal("server.pem", cert.Name); + Assert.Equal("-----BEGIN CERTIFICATE-----", cert.Contents); + } + + [Fact] + public void CreateDirectoryProducesContainerDirectoryWithEntries() + { + var context = CreateContext(); + + var child = context.CreateFile("nested.conf", contents: "nested=true"); + var item = context.CreateDirectory("conf.d", [child], mode: 493 /* 0o755 */); + + var directory = Assert.IsType(item); + Assert.Equal("conf.d", directory.Name); + Assert.Equal((UnixFileMode)493 /* 0o755 */, directory.Mode); + var entry = Assert.Single(directory.Entries); + Assert.Same(child, entry); + } + + [Fact] + public void CreateDirectoryMaterializesLazyEntries() + { + var context = CreateContext(); + + var evaluationCount = 0; + IEnumerable LazyEntries() + { + evaluationCount++; + yield return context.CreateFile("nested.conf", contents: "nested=true"); + } + + var item = context.CreateDirectory("conf.d", LazyEntries()); + + // The directory factory eagerly materializes the sequence so a lazily-resolved + // handle sequence is captured at call time rather than on later enumeration. + Assert.Equal(1, evaluationCount); + var directory = Assert.IsType(item); + Assert.Single(directory.Entries); + } + + [Theory] + [InlineData(-1)] + [InlineData(0x1000)] + public void CreateFileWithOutOfRangeModeThrows(int mode) + { + var context = CreateContext(); + + Assert.Throws(() => context.CreateFile("app.conf", mode: mode)); + } + + [Fact] + public void CreateFileWithMaximumModeSucceeds() + { + var context = CreateContext(); + + var item = context.CreateFile("app.conf", mode: 4095 /* 0o7777 */); + + var file = Assert.IsType(item); + Assert.Equal((UnixFileMode)4095 /* 0o7777 */, file.Mode); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + public void CreateFileWithInvalidNameThrows(string name) + { + var context = CreateContext(); + + Assert.Throws(() => context.CreateFile(name)); + } + + [Fact] + public void CreateDirectoryWithNullEntriesThrows() + { + var context = CreateContext(); + + Assert.Throws(() => context.CreateDirectory("conf.d", entries: null!)); + } +} diff --git a/tests/Aspire.Hosting.Tests/RequiredCommandAnnotationTests.cs b/tests/Aspire.Hosting.Tests/RequiredCommandAnnotationTests.cs index 579fd54e401..05647675eed 100644 --- a/tests/Aspire.Hosting.Tests/RequiredCommandAnnotationTests.cs +++ b/tests/Aspire.Hosting.Tests/RequiredCommandAnnotationTests.cs @@ -468,6 +468,30 @@ public async Task RequiredCommandValidationEventingSubscriber_CoalescesInteracti Assert.False(testInteractionService.Interactions.Reader.TryRead(out _)); } + [Fact] + public void RequiredCommandValidationContext_Success_ReturnsValidResult() + { + using var services = new ServiceCollection().BuildServiceProvider(); + var context = new RequiredCommandValidationContext("/usr/bin/test", services, CancellationToken.None); + + var result = context.Success(); + + Assert.True(result.IsValid); + Assert.Null(result.ValidationMessage); + } + + [Fact] + public void RequiredCommandValidationContext_Failure_ReturnsInvalidResultWithMessage() + { + using var services = new ServiceCollection().BuildServiceProvider(); + var context = new RequiredCommandValidationContext("/usr/bin/test", services, CancellationToken.None); + + var result = context.Failure("command is too old"); + + Assert.False(result.IsValid); + Assert.Equal("command is too old", result.ValidationMessage); + } + /// /// Helper method to subscribe all eventing subscribers (including RequiredCommandValidationEventingSubscriber) /// to the eventing system. This simulates what happens during app.StartAsync(). diff --git a/tests/PolyglotAppHosts/Aspire.Hosting/Go/apphost.go b/tests/PolyglotAppHosts/Aspire.Hosting/Go/apphost.go index e8c3a4dca3f..314d27636cf 100644 --- a/tests/PolyglotAppHosts/Aspire.Hosting/Go/apphost.go +++ b/tests/PolyglotAppHosts/Aspire.Hosting/Go/apphost.go @@ -168,6 +168,26 @@ ENTRYPOINT ["dotnet", "App.dll"] }, }) + // WithContainerFilesCallback — build entries dynamically via the context factory methods + container.WithContainerFilesCallback("/usr/lib/aspire/container-files", func(filesCtx aspire.ContainerFileSystemCallbackContext, _ *aspire.CancellationToken) []aspire.ContainerFileSystemItem { + filesServices := filesCtx.Services() + filesLoggerFactory := filesServices.GetLoggerFactory() + filesLogger := filesLoggerFactory.CreateLogger("ValidationAppHost.ContainerFilesCallback") + _ = filesLogger.LogInformation("ContainerFilesCallback services") + + appConfig := filesCtx.CreateFile("app.conf", &aspire.CreateFileOptions{Contents: aspire.StringPtr("key=value"), Mode: aspire.Float64Ptr(0o644)}) + nestedConfig := filesCtx.CreateFile("nested.conf", &aspire.CreateFileOptions{Contents: aspire.StringPtr("nested=true")}) + confDir := filesCtx.CreateDirectory("conf.d", []aspire.ContainerFileSystemItem{nestedConfig}, &aspire.CreateDirectoryOptions{Mode: aspire.Float64Ptr(0o755)}) + cert := filesCtx.CreateCertificateFile("server.pem", &aspire.CreateCertificateFileOptions{Contents: aspire.StringPtr("-----BEGIN CERTIFICATE-----")}) + return []aspire.ContainerFileSystemItem{appConfig, confDir, cert} + }, &aspire.WithContainerFilesCallbackOptions{ + Options: &aspire.ContainerFilesOptions{ + DefaultOwner: aspire.Float64Ptr(1000), + DefaultGroup: aspire.Float64Ptr(1000), + Umask: aspire.Float64Ptr(0o022), + }, + }) + // WithImageRegistry container.WithImageRegistry("docker.io") @@ -282,6 +302,15 @@ ENTRYPOINT ["dotnet", "App.dll"] // WithRequiredCommand container.WithRequiredCommand("docker") + // WithRequiredCommandValidation + container.WithRequiredCommandValidation("docker", func(validationCtx aspire.RequiredCommandValidationContext) aspire.RequiredCommandValidationResult { + validationServices := validationCtx.Services() + validationLoggerFactory := validationServices.GetLoggerFactory() + validationLogger := validationLoggerFactory.CreateLogger("ValidationAppHost.RequiredCommandValidation") + _ = validationLogger.LogInformation("RequiredCommandValidation services") + return validationCtx.Success() + }) + // =================================================================== // DotnetToolResourceExtensions — all With-tool methods are fluent // =================================================================== @@ -550,6 +579,10 @@ ENTRYPOINT ["dotnet", "App.dll"] if !ok { return aspire.ResourceCommandStateDisabled } + updateStateServices := ctx.Services() + updateStateLoggerFactory := updateStateServices.GetLoggerFactory() + updateStateLogger := updateStateLoggerFactory.CreateLogger("ValidationAppHost.UpdateCommandState") + _ = updateStateLogger.LogInformation("UpdateCommandState services") snapshot, err := ctx.ResourceSnapshot() if err != nil || snapshot.HealthStatus == nil { return aspire.ResourceCommandStateDisabled @@ -566,7 +599,25 @@ ENTRYPOINT ["dotnet", "App.dll"] UpdateState: updateCommandState, }, }) + validateCommandArguments := func(args ...any) any { + if len(args) == 0 { + return nil + } + ctx, ok := args[0].(aspire.InputsDialogValidationContext) + if !ok { + return nil + } + validationServices := ctx.Services() + validationLoggerFactory := validationServices.GetLoggerFactory() + validationLogger := validationLoggerFactory.CreateLogger("ValidationAppHost.ValidateCommandArguments") + _ = validationLogger.LogInformation("Validate command arguments services") + return nil + } _ = container.WithCommand("echo", "Echo", func(ctx aspire.ExecuteCommandContext) *aspire.ExecuteCommandResult { + echoServices := ctx.Services() + echoLoggerFactory := echoServices.GetLoggerFactory() + echoLogger := echoLoggerFactory.CreateLogger("ValidationAppHost.EchoCommand") + _ = echoLogger.LogInformation("Echo command services") commandArguments, err := ctx.Arguments().ToArray() if err != nil { return &aspire.ExecuteCommandResult{Success: false, ErrorMessage: aspire.StringPtr(aspire.FormatError(err))} @@ -581,6 +632,7 @@ ENTRYPOINT ["dotnet", "App.dll"] Required: aspire.BoolPtr(true), }, }, + ValidateArguments: validateCommandArguments, }, }) _ = container.WithCommand("restart", "Restart", func(ctx aspire.ExecuteCommandContext) *aspire.ExecuteCommandResult { @@ -596,6 +648,39 @@ ENTRYPOINT ["dotnet", "App.dll"] return result }) + container.WithHttpsCertificateConfiguration(func(certCtx aspire.HttpsCertificateConfigurationCallbackAnnotationContext) { + certificatePath := certCtx.CertificatePath() + keyPath := certCtx.KeyPath() + certArgs := certCtx.Arguments() + _ = certArgs.Add("--certificate") + _ = certArgs.Add(certificatePath) + _ = certArgs.Add("--key") + _ = certArgs.Add(keyPath) + certEnv := certCtx.Environment() + _ = certEnv.Set("Kestrel__Certificates__Path", certificatePath) + _ = certEnv.Set("Kestrel__Certificates__KeyPath", keyPath) + }) + + _ = container.SubscribeHttpsEndpointsUpdate(func(httpsCtx aspire.HttpsEndpointUpdateCallbackContext) { + httpsServices := httpsCtx.Services() + httpsLoggerFactory := httpsServices.GetLoggerFactory() + httpsLogger := httpsLoggerFactory.CreateLogger("ValidationAppHost.HttpsEndpointsUpdate") + _ = httpsLogger.LogInformation("HttpsEndpointsUpdate services") + }) + + _ = container.WithContainerBuildOptions(func(buildCtx aspire.ContainerBuildOptionsCallbackContext) { + buildCtx.SetDestination(aspire.ContainerImageDestinationRegistry). + SetImageFormat(aspire.ContainerImageFormatOci). + SetTargetPlatform(aspire.ContainerTargetPlatformLinuxAmd64). + SetOutputPath("./artifacts/container-image"). + SetLocalImageName("validation-image"). + SetLocalImageTag("latest") + buildServices := buildCtx.Services() + buildLoggerFactory := buildServices.GetLoggerFactory() + buildLogger := buildLoggerFactory.CreateLogger("ValidationAppHost.ContainerBuildOptions") + _ = buildLogger.LogInformation("ContainerBuildOptions services") + }) + app, err := builder.Build() if err != nil { log.Fatalf(aspire.FormatError(err)) diff --git a/tests/PolyglotAppHosts/Aspire.Hosting/Java/AppHost.java b/tests/PolyglotAppHosts/Aspire.Hosting/Java/AppHost.java index 92061bc4492..91cf4df3c2b 100644 --- a/tests/PolyglotAppHosts/Aspire.Hosting/Java/AppHost.java +++ b/tests/PolyglotAppHosts/Aspire.Hosting/Java/AppHost.java @@ -73,6 +73,21 @@ void main() throws Exception { containerFilesOptions.setDefaultGroup(1000.0); containerFilesOptions.setUmask(18.0); container.withContainerFiles("/usr/lib/aspire/container-files", ".", containerFilesOptions); + var callbackContainerFilesOptions = new ContainerFilesOptions(); + callbackContainerFilesOptions.setDefaultOwner(1000.0); + callbackContainerFilesOptions.setDefaultGroup(1000.0); + callbackContainerFilesOptions.setUmask(18.0); + container.withContainerFilesCallback("/usr/lib/aspire/container-files", (filesCtx, filesCancellationToken) -> { + var filesServices = filesCtx.services(); + var filesLoggerFactory = filesServices.getLoggerFactory(); + var filesLogger = filesLoggerFactory.createLogger("ValidationAppHost.ContainerFilesCallback"); + filesLogger.logInformation("ContainerFilesCallback services"); + var appConfig = filesCtx.createFile("app.conf", new CreateFileOptions().contents("key=value").mode(420.0)); + var nestedConfig = filesCtx.createFile("nested.conf", new CreateFileOptions().contents("nested=true")); + var confDir = filesCtx.createDirectory("conf.d", new ContainerFileSystemItem[] { nestedConfig }, new CreateDirectoryOptions().mode(493.0)); + var cert = filesCtx.createCertificateFile("server.pem", new CreateCertificateFileOptions().contents("-----BEGIN CERTIFICATE-----")); + return new ContainerFileSystemItem[] { appConfig, confDir, cert }; + }, callbackContainerFilesOptions); container.withImageRegistry("docker.io"); dockerContainer.withHttpEndpoint(new WithHttpEndpointOptions().name("http").targetPort(80.0)); dockerContainer.withHttpEndpointCallback((updateContext) -> { updateContext.setPort(8080.0); updateContext.setIsProxied(false); }, new WithHttpEndpointCallbackOptions().name("http").createIfNotExists(false)); @@ -129,6 +144,14 @@ void main() throws Exception { }); container.withMcpServer(new WithMcpServerOptions().path("/mcp")); container.withRequiredCommand("docker"); + container.withRequiredCommandValidation("docker", (validationCtx) -> { + var _validationResolvedPath = validationCtx.resolvedPath(); + var validationServices = validationCtx.services(); + var validationLoggerFactory = validationServices.getLoggerFactory(); + var validationLogger = validationLoggerFactory.createLogger("ValidationAppHost.RequiredCommandValidation"); + validationLogger.logInformation("RequiredCommandValidation services"); + return validationCtx.success(); + }); tool.withToolIgnoreExistingFeeds(); tool.withToolIgnoreFailedSources(); tool.withToolPackage("dotnet-ef"); @@ -248,6 +271,10 @@ void main() throws Exception { var commandOptions = new CommandOptions(); commandOptions.setUpdateState((Function) (ctx) -> { var snapshot = ctx.resourceSnapshot(); + var updateStateServices = ctx.services(); + var updateStateLoggerFactory = updateStateServices.getLoggerFactory(); + var updateStateLogger = updateStateLoggerFactory.createLogger("ValidationAppHost.UpdateCommandState"); + updateStateLogger.logInformation("UpdateCommandState services"); return snapshot.getHealthStatus() == HealthStatus.HEALTHY ? ResourceCommandState.ENABLED : ResourceCommandState.DISABLED; }); container.withCommand("noop", "Noop", (_ctx) -> { @@ -261,8 +288,19 @@ void main() throws Exception { messageArgument.setInputType(InputType.TEXT); messageArgument.setRequired(true); echoCommandOptions.setArguments(new InteractionInput[] { messageArgument }); + echoCommandOptions.setValidateArguments((Function) (ctx) -> { + var validationServices = ctx.services(); + var validationLoggerFactory = validationServices.getLoggerFactory(); + var validationLogger = validationLoggerFactory.createLogger("ValidationAppHost.ValidateCommandArguments"); + validationLogger.logInformation("Validate command arguments services"); + return null; + }); container.withCommand("echo", "Echo", (ctx) -> { var commandArguments = ctx.arguments().toArray(); + var echoServices = ctx.services(); + var echoLoggerFactory = echoServices.getLoggerFactory(); + var echoLogger = echoLoggerFactory.createLogger("ValidationAppHost.EchoCommand"); + echoLogger.logInformation("Echo command services"); var result = new ExecuteCommandResult(); result.setSuccess("hello".equals(commandArguments[0].getValue())); return result; @@ -277,6 +315,42 @@ void main() throws Exception { .cancellationToken(cancellationToken)); }); container.withHealthCheck("custom_check"); + container.withHttpsCertificateConfiguration((certCtx) -> { + var _certResource = certCtx.resource(); + var _certIsRunMode = certCtx.executionContext().isRunMode(); + var certificatePath = certCtx.certificatePath(); + var keyPath = certCtx.keyPath(); + var certArgs = certCtx.arguments(); + certArgs.add("--certificate"); + certArgs.add(certificatePath); + certArgs.add("--key"); + certArgs.add(keyPath); + var certEnv = certCtx.environment(); + certEnv.set("Kestrel__Certificates__Path", certificatePath); + certEnv.set("Kestrel__Certificates__KeyPath", keyPath); + }); + container.subscribeHttpsEndpointsUpdate((httpsCtx) -> { + var _httpsResource = httpsCtx.resource(); + var _httpsModel = httpsCtx.model(); + var httpsServices = httpsCtx.services(); + var httpsLoggerFactory = httpsServices.getLoggerFactory(); + var httpsLogger = httpsLoggerFactory.createLogger("ValidationAppHost.HttpsEndpointsUpdate"); + httpsLogger.logInformation("HttpsEndpointsUpdate services"); + }); + container.withContainerBuildOptions((buildCtx) -> { + buildCtx.setDestination(ContainerImageDestination.REGISTRY); + buildCtx.setImageFormat(ContainerImageFormat.OCI); + buildCtx.setTargetPlatform(ContainerTargetPlatform.LINUX_AMD64); + buildCtx.setOutputPath("./artifacts/container-image"); + buildCtx.setLocalImageName("validation-image"); + buildCtx.setLocalImageTag("latest"); + var _buildResource = buildCtx.resource(); + var _buildExecutionContext = buildCtx.executionContext(); + var buildServices = buildCtx.services(); + var buildLoggerFactory = buildServices.getLoggerFactory(); + var buildLogger = buildLoggerFactory.createLogger("ValidationAppHost.ContainerBuildOptions"); + buildLogger.logInformation("ContainerBuildOptions services"); + }); container.withHttpCommand("/health", "Health Check"); var httpCmdOptions = new HttpCommandExportOptions(); httpCmdOptions.setMethodName("POST"); diff --git a/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py b/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py index 89386509a1b..16477a220b3 100644 --- a/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py +++ b/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py @@ -150,6 +150,28 @@ def custom_health_check(): "Umask": 0o022, }, ) + + # withContainerFilesCallback — build entries dynamically via the context factory methods + def container_files_callback(files_ctx, files_cancellation_token): + files_services = files_ctx.services + files_logger_factory = files_services.get_logger_factory() + files_logger = files_logger_factory.create_logger("ValidationAppHost.ContainerFilesCallback") + files_logger.log_information("ContainerFilesCallback services") + app_config = files_ctx.create_file("app.conf", contents="key=value", mode=0o644) + nested_config = files_ctx.create_file("nested.conf", contents="nested=true") + conf_dir = files_ctx.create_dir("conf.d", [nested_config], mode=0o755) + cert = files_ctx.create_certificate_file("server.pem", contents="-----BEGIN CERTIFICATE-----") + return [app_config, conf_dir, cert] + + container.with_container_files_callback( + "/usr/lib/aspire/container-files", + container_files_callback, + options={ + "DefaultOwner": 1000, + "DefaultGroup": 1000, + "Umask": 0o022, + }, + ) # withImageRegistry container.with_image_registry("docker.io") # =================================================================== @@ -269,6 +291,16 @@ def configure_image_push_options(context): container.with_mcp_server() # withRequiredCommand container.with_required_command("docker") + + def required_command_validation(validation_ctx): + _validation_resolved_path = validation_ctx.resolved_path + validation_services = validation_ctx.services + validation_logger_factory = validation_services.get_logger_factory() + validation_logger = validation_logger_factory.create_logger("ValidationAppHost.RequiredCommandValidation") + validation_logger.log_information("RequiredCommandValidation services") + return validation_ctx.success() + + container.with_required_command("docker", validation_callback=required_command_validation) # withToolIgnoreExistingFeeds tool.with_tool_ignore_existing_feeds() # withToolIgnoreFailedSources @@ -426,12 +458,27 @@ def configure_https_certificate(certificate_info): # withCommand def update_command_state(ctx): snapshot = ctx.resource_snapshot + update_state_services = ctx.services + update_state_logger_factory = update_state_services.get_logger_factory() + update_state_logger = update_state_logger_factory.create_logger("ValidationAppHost.UpdateCommandState") + update_state_logger.log_information("UpdateCommandState services") return "Enabled" if snapshot.get("HealthStatus") == "Healthy" else "Disabled" def echo_command(ctx): command_arguments = list(ctx.arguments.to_array()) + echo_services = ctx.services + echo_logger_factory = echo_services.get_logger_factory() + echo_logger = echo_logger_factory.create_logger("ValidationAppHost.EchoCommand") + echo_logger.log_information("Echo command services") return {"success": command_arguments[0]["Value"] == "hello"} + def validate_command_arguments(ctx): + validation_services = ctx.services + validation_logger_factory = validation_services.get_logger_factory() + validation_logger = validation_logger_factory.create_logger("ValidationAppHost.ValidateCommandArguments") + validation_logger.log_information("Validate command arguments services") + return None + container.with_command( "noop", "Noop", @@ -446,9 +493,48 @@ def restart_command(_ctx): "echo", "Echo", echo_command, - command_options={"Arguments": [{"Name": "message", "InputType": "Text", "Required": True}]} + command_options={"Arguments": [{"Name": "message", "InputType": "Text", "Required": True}], "ValidateArguments": validate_command_arguments} ) container.with_command("restart", "Restart", restart_command) + + def https_endpoints_update(https_ctx): + _https_resource = https_ctx.resource + _https_model = https_ctx.model + https_services = https_ctx.services + https_logger_factory = https_services.get_logger_factory() + https_logger = https_logger_factory.create_logger("ValidationAppHost.HttpsEndpointsUpdate") + https_logger.log_information("HttpsEndpointsUpdate services") + + def https_certificate_configuration(cert_ctx): + _cert_resource = cert_ctx.resource + certificate_path = cert_ctx.certificate_path + key_path = cert_ctx.key_path + cert_ctx.arguments.add("--certificate") + cert_ctx.arguments.add(certificate_path) + cert_ctx.arguments.add("--key") + cert_ctx.arguments.add(key_path) + cert_ctx.env.set("Kestrel__Certificates__Path", certificate_path) + cert_ctx.env.set("Kestrel__Certificates__KeyPath", key_path) + + container.with_https_certificate_config(https_certificate_configuration) + + container.subscribe_https_endpoints_update(https_endpoints_update) + + def container_build_options(build_ctx): + build_ctx.destination = "Registry" + build_ctx.image_format = "Oci" + build_ctx.target_platform = "LinuxAmd64" + build_ctx.output_path = "./artifacts/container-image" + build_ctx.local_image_name = "validation-image" + build_ctx.local_image_tag = "latest" + _build_resource = build_ctx.resource + _build_execution_context = build_ctx.execution_context + build_services = build_ctx.services + build_logger_factory = build_services.get_logger_factory() + build_logger = build_logger_factory.create_logger("ValidationAppHost.ContainerBuildOptions") + build_logger.log_information("ContainerBuildOptions services") + + container.with_container_build_options(container_build_options) # withHttpCommand container.with_http_command("/health", "Health Check") container.with_http_command("/api/reset", "Reset", options={"MethodName": "POST", "ConfirmationMessage": "Are you sure?"}) diff --git a/tests/PolyglotAppHosts/Aspire.Hosting/TypeScript/apphost.mts b/tests/PolyglotAppHosts/Aspire.Hosting/TypeScript/apphost.mts index c2fedbbd8a4..6d49f3dd9dc 100644 --- a/tests/PolyglotAppHosts/Aspire.Hosting/TypeScript/apphost.mts +++ b/tests/PolyglotAppHosts/Aspire.Hosting/TypeScript/apphost.mts @@ -5,6 +5,9 @@ import { WellKnownPipelineTags, createBuilder, CertificateTrustScope, + ContainerImageDestination, + ContainerImageFormat, + ContainerTargetPlatform, EndpointProperty, HealthStatus, IconVariant, @@ -161,6 +164,20 @@ await container.withContainerFiles("/usr/lib/aspire/container-files", ".", { umask: 0o022, }); +// withContainerFilesCallback — build entries dynamically via the context factory methods +await container.withContainerFilesCallback("/usr/lib/aspire/container-files", async (filesCtx) => { + const filesServices = await filesCtx.services(); + const filesLoggerFactory = await filesServices.getLoggerFactory(); + const filesLogger = await filesLoggerFactory.createLogger("ValidationAppHost.ContainerFilesCallback"); + await filesLogger.logInformation("ContainerFilesCallback services"); + + const appConfig = await filesCtx.createFile("app.conf", { contents: "key=value", mode: 0o644 }); + const nestedConfig = await filesCtx.createFile("nested.conf", { contents: "nested=true" }); + const confDir = await filesCtx.createDirectory("conf.d", [nestedConfig], { mode: 0o755 }); + const cert = await filesCtx.createCertificateFile("server.pem", { contents: "-----BEGIN CERTIFICATE-----" }); + return [appConfig, confDir, cert]; +}, { defaultOwner: 1000, defaultGroup: 1000, umask: 0o022 }); + // withImageRegistry await container.withImageRegistry("docker.io"); @@ -336,6 +353,16 @@ await container.withMcpServer({ path: "/mcp" }); // withRequiredCommand await container.withRequiredCommand("docker"); +// withRequiredCommandValidation +await container.withRequiredCommandValidation("docker", async (validationCtx) => { + const _validationResolvedPath = await validationCtx.resolvedPath(); + const validationServices = await validationCtx.services(); + const validationLoggerFactory = await validationServices.getLoggerFactory(); + const validationLogger = await validationLoggerFactory.createLogger("ValidationAppHost.RequiredCommandValidation"); + await validationLogger.logInformation("RequiredCommandValidation services"); + return await validationCtx.success(); +}); + // =================================================================== // DotnetToolResourceExtensions.cs — NEW exports // =================================================================== @@ -728,6 +755,10 @@ await container.withCommand("noop", "Noop", async () => { commandOptions: { updateState: async (ctx) => { const snapshot = await ctx.resourceSnapshot(); + const updateStateServices = await ctx.services(); + const updateStateLoggerFactory = await updateStateServices.getLoggerFactory(); + const updateStateLogger = await updateStateLoggerFactory.createLogger("ValidationAppHost.UpdateCommandState"); + await updateStateLogger.logInformation("UpdateCommandState services"); return snapshot.healthStatus === HealthStatus.Healthy ? ResourceCommandState.Enabled @@ -738,6 +769,10 @@ await container.withCommand("noop", "Noop", async () => { await container.withCommand("echo", "Echo", async (ctx) => { const commandInputs = await ctx.arguments(); const commandArguments = await commandInputs.toArray(); + const echoServices = await ctx.services(); + const echoLoggerFactory = await echoServices.getLoggerFactory(); + const echoLogger = await echoLoggerFactory.createLogger("ValidationAppHost.EchoCommand"); + await echoLogger.logInformation("Echo command services"); return { success: commandArguments[0]?.value === "hello" }; }, { @@ -748,7 +783,13 @@ await container.withCommand("echo", "Echo", async (ctx) => { inputType: InputType.Text, required: true } - ] + ], + validateArguments: async (ctx) => { + const validationServices = await ctx.services(); + const validationLoggerFactory = await validationServices.getLoggerFactory(); + const validationLogger = await validationLoggerFactory.createLogger("ValidationAppHost.ValidateCommandArguments"); + await validationLogger.logInformation("Validate command arguments services"); + } } }); await container.withCommand("restart", "Restart", async (ctx) => { @@ -760,6 +801,48 @@ await container.withCommand("restart", "Restart", async (ctx) => { }); }); +// withHttpsCertificateConfiguration +await container.withHttpsCertificateConfiguration(async (certCtx) => { + const _certResource = await certCtx.resource(); + const _certIsRunMode: boolean = await certCtx.executionContext().isRunMode(); + const certificatePath = await certCtx.certificatePath(); + const keyPath = await certCtx.keyPath(); + const certArgs = await certCtx.arguments(); + await certArgs.add("--certificate"); + await certArgs.add(certificatePath); + await certArgs.add("--key"); + await certArgs.add(keyPath); + const certEnv = await certCtx.environment(); + await certEnv.set("Kestrel__Certificates__Path", certificatePath); + await certEnv.set("Kestrel__Certificates__KeyPath", keyPath); +}); + +// subscribeHttpsEndpointsUpdate +await container.subscribeHttpsEndpointsUpdate(async (httpsCtx) => { + const _httpsResource = await httpsCtx.resource(); + const _httpsModel = await httpsCtx.model(); + const httpsServices = await httpsCtx.services(); + const httpsLoggerFactory = await httpsServices.getLoggerFactory(); + const httpsLogger = await httpsLoggerFactory.createLogger("ValidationAppHost.HttpsEndpointsUpdate"); + await httpsLogger.logInformation("HttpsEndpointsUpdate services"); +}); + +// withContainerBuildOptions +await container.withContainerBuildOptions(async (buildCtx) => { + await buildCtx.destination.set(ContainerImageDestination.Registry); + await buildCtx.imageFormat.set(ContainerImageFormat.Oci); + await buildCtx.targetPlatform.set(ContainerTargetPlatform.LinuxAmd64); + await buildCtx.outputPath.set("./artifacts/container-image"); + await buildCtx.localImageName.set("validation-image"); + await buildCtx.localImageTag.set("latest"); + const _buildResource = await buildCtx.resource(); + const _buildExecutionContext = await buildCtx.executionContext(); + const buildServices = await buildCtx.services(); + const buildLoggerFactory = await buildServices.getLoggerFactory(); + const buildLogger = await buildLoggerFactory.createLogger("ValidationAppHost.ContainerBuildOptions"); + await buildLogger.logInformation("ContainerBuildOptions services"); +}); + // withProcessCommand await container.withProcessCommand("dotnet-version", "Show .NET version", { executablePath: "dotnet", From d90a8b77dd2ed1713c7f228322db6265841c6322 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 9 Jun 2026 11:59:33 -0700 Subject: [PATCH 2/6] Address PR review: validate file factory inputs and fix Python required_command codegen Resolves copilot-pull-request-reviewer feedback on the ATS export PR: - CreateFile/CreateCertificateFile now throw ArgumentException up front when both `contents` and `sourcePath` are provided, since they are mutually exclusive. Added unit tests covering both factories. - Python generator: stop merging callback-differentiated overloads in MergeCapabilitiesBySourceLocation (added IsCallback to the merge guard). This restores `required_command: str | tuple[str, str]` (no breaking change to the published tuple shorthand) and emits a separate `with_required_command_validation` method/`required_command_validation` option instead of forcing a TypedDict and a buggy conditional dispatch that always registered a null validationCallback. - Regenerated the Python snapshot and updated the Python polyglot apphost to call the new `with_required_command_validation` method. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AtsPythonCodeGenerator.cs | 10 +++- .../ContainerFileSystemCallbackAnnotation.cs | 13 ++++ ...TwoPassScanningGeneratedAspire.verified.py | 60 ++++++++++++++----- ...ContainerFileSystemCallbackContextTests.cs | 16 +++++ .../Aspire.Hosting/Python/apphost.py | 2 +- 5 files changed, 82 insertions(+), 19 deletions(-) diff --git a/src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs b/src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs index 48d1447bbbf..923ac53d810 100644 --- a/src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs +++ b/src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs @@ -334,8 +334,14 @@ private List MergeCapabilitiesBySourceLocation(List c.Parameters.Any(p => string.Equals(p.Name, extraParamName, StringComparison.Ordinal))); var extraParam = capWithExtra.Parameters.First(p => string.Equals(p.Name, extraParamName, StringComparison.Ordinal)); - // Only merge if the extra param is required (not already optional) - if (extraParam.IsOptional || extraParam.IsNullable) + // Only merge if the extra param is required (not already optional). + // Never merge when the differing parameter is a callback: a callback cannot be represented as a + // positional tuple element, so merging would (a) change the option shape from the published + // `str | tuple[...]` shorthand to a TypedDict (a breaking change for the non-callback overload) + // and (b) require conditional capability dispatch that always registers the callback argument even + // when routing to the non-callback capability. Keeping the callback overload as its own separate + // option avoids both problems. + if (extraParam.IsOptional || extraParam.IsNullable || extraParam.IsCallback) { result.AddRange(items); continue; diff --git a/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs index 5caa2927570..5e20ba80ded 100644 --- a/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs +++ b/src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs @@ -325,6 +325,7 @@ internal static ContainerFileSystemItem CreateFile(this ContainerFileSystemCallb { ArgumentNullException.ThrowIfNull(context); ArgumentException.ThrowIfNullOrWhiteSpace(name); + ThrowIfContentsAndSourcePathBothProvided(contents, sourcePath); return new ContainerFile { @@ -358,6 +359,7 @@ internal static ContainerFileSystemItem CreateCertificateFile(this ContainerFile { ArgumentNullException.ThrowIfNull(context); ArgumentException.ThrowIfNullOrWhiteSpace(name); + ThrowIfContentsAndSourcePathBothProvided(contents, sourcePath); return new ContainerOpenSSLCertificateFile { @@ -418,6 +420,17 @@ private static UnixFileMode ConvertMode(int? mode) return (UnixFileMode)mode.Value; } + + // contents and sourcePath are mutually exclusive: a file entry is sourced either from inline contents or + // from a host path, never both. Validate here so polyglot callers get a clear error at construction time + // instead of a harder-to-diagnose failure later during DCP conversion. + private static void ThrowIfContentsAndSourcePathBothProvided(string? contents, string? sourcePath) + { + if (contents is not null && sourcePath is not null) + { + throw new ArgumentException($"Only one of '{nameof(contents)}' or '{nameof(sourcePath)}' can be specified, not both."); + } + } } /// diff --git a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py index bfb494c1115..7da1ff151ab 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py +++ b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py @@ -1561,9 +1561,9 @@ class DockerfileBaseImageParameters(typing.TypedDict, total=False): runtime_image: str -class RequiredCommandParameters(typing.TypedDict, total=False): +class RequiredCommandValidationParameters(typing.TypedDict, total=False): command: typing.Required[str] - validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult] + validation_callback: typing.Required[typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult]] help_link: str @@ -6819,9 +6819,13 @@ def with_dockerfile_base_image(self, *, build_image: str | None = None, runtime_ """Configures custom base images for generated Dockerfiles.""" @abc.abstractmethod - def with_required_command(self, command: str, *, validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult] | None = None, help_link: str | None = None) -> typing.Self: + def with_required_command(self, command: str, *, help_link: str | None = None) -> typing.Self: """Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start.""" + @abc.abstractmethod + def with_required_command_validation(self, command: str, validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult], *, help_link: str | None = None) -> typing.Self: + """Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic.""" + @abc.abstractmethod def with_session_lifetime(self) -> typing.Self: """Configures a resource to use a session lifetime.""" @@ -7252,7 +7256,8 @@ class _BaseResourceKwargs(typing.TypedDict, total=False): container_registry: AbstractResource dockerfile_base_image: DockerfileBaseImageParameters | typing.Literal[True] - required_command: str | RequiredCommandParameters + required_command: str | tuple[str, str] + required_command_validation: tuple[str, typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult]] | RequiredCommandValidationParameters session_lifetime: typing.Literal[True] persistent_lifetime: typing.Literal[True] lifetime_of: AbstractResource @@ -7338,17 +7343,28 @@ def with_dockerfile_base_image(self, *, build_image: str | None = None, runtime_ self._handle = self._wrap_builder(result) return self - def with_required_command(self, command: str, *, validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult] | None = None, help_link: str | None = None) -> typing.Self: + def with_required_command(self, command: str, *, help_link: str | None = None) -> typing.Self: """Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} rpc_args['command'] = command - if validation_callback is not None: - rpc_args['validationCallback'] = self._client.register_callback(validation_callback) if help_link is not None: rpc_args['helpLink'] = help_link - capability_id = 'Aspire.Hosting/withRequiredCommandValidation' if validation_callback is not None else 'Aspire.Hosting/withRequiredCommand' result = self._client.invoke_capability( - capability_id, + 'Aspire.Hosting/withRequiredCommand', + rpc_args, + ) + self._handle = self._wrap_builder(result) + return self + + def with_required_command_validation(self, command: str, validation_callback: typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult], *, help_link: str | None = None) -> typing.Self: + """Declares that a resource requires a specific command/executable to be available on the local machine PATH before it can start, with custom validation logic.""" + rpc_args: dict[str, typing.Any] = {'builder': self._handle} + rpc_args['command'] = command + rpc_args['validationCallback'] = self._client.register_callback(validation_callback) + if help_link is not None: + rpc_args['helpLink'] = help_link + result = self._client.invoke_capability( + 'Aspire.Hosting/withRequiredCommandValidation', rpc_args, ) self._handle = self._wrap_builder(result) @@ -7948,15 +7964,27 @@ def __init__(self, handle: Handle, client: AspireClient, **kwargs: typing.Unpack rpc_args: dict[str, typing.Any] = {"builder": handle} rpc_args["command"] = typing.cast(str, _required_command) handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withRequiredCommand', rpc_args)) - elif _validate_dict_types(_required_command, RequiredCommandParameters): + elif _validate_tuple_types(_required_command, (str, str)): rpc_args: dict[str, typing.Any] = {"builder": handle} - rpc_args["command"] = typing.cast(RequiredCommandParameters, _required_command)["command"] - rpc_args["validationCallback"] = client.register_callback(typing.cast(RequiredCommandParameters, _required_command).get("validation_callback")) - rpc_args["helpLink"] = typing.cast(RequiredCommandParameters, _required_command).get("help_link") - capability_id = 'Aspire.Hosting/withRequiredCommandValidation' if "validation_callback" in _required_command else 'Aspire.Hosting/withRequiredCommand' - handle = self._wrap_builder(client.invoke_capability(capability_id, rpc_args)) + rpc_args["command"] = typing.cast(tuple[str, str], _required_command)[0] + rpc_args["helpLink"] = typing.cast(tuple[str, str], _required_command)[1] + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withRequiredCommand', rpc_args)) + else: + raise TypeError("Invalid type for option 'required_command'. Expected: str or (str, str)") + if _required_command_validation := kwargs.pop("required_command_validation", None): + if _validate_tuple_types(_required_command_validation, (str, typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult])): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["command"] = typing.cast(tuple[str, typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult]], _required_command_validation)[0] + rpc_args["validationCallback"] = client.register_callback(typing.cast(tuple[str, typing.Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult]], _required_command_validation)[1]) + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withRequiredCommandValidation', rpc_args)) + elif _validate_dict_types(_required_command_validation, RequiredCommandValidationParameters): + rpc_args: dict[str, typing.Any] = {"builder": handle} + rpc_args["command"] = typing.cast(RequiredCommandValidationParameters, _required_command_validation)["command"] + rpc_args["validationCallback"] = client.register_callback(typing.cast(RequiredCommandValidationParameters, _required_command_validation)["validation_callback"]) + rpc_args["helpLink"] = typing.cast(RequiredCommandValidationParameters, _required_command_validation).get("help_link") + handle = self._wrap_builder(client.invoke_capability('Aspire.Hosting/withRequiredCommandValidation', rpc_args)) else: - raise TypeError("Invalid type for option 'required_command'. Expected: str or RequiredCommandParameters") + raise TypeError("Invalid type for option 'required_command_validation'. Expected: (str, Callable[[RequiredCommandValidationContext], RequiredCommandValidationResult]) or RequiredCommandValidationParameters") if _session_lifetime := kwargs.pop("session_lifetime", None): if _session_lifetime is True: rpc_args: dict[str, typing.Any] = {"builder": handle} diff --git a/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs b/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs index bd9578f9a73..7602f722893 100644 --- a/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs +++ b/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs @@ -134,4 +134,20 @@ public void CreateDirectoryWithNullEntriesThrows() Assert.Throws(() => context.CreateDirectory("conf.d", entries: null!)); } + + [Fact] + public void CreateFileWithBothContentsAndSourcePathThrows() + { + var context = CreateContext(); + + Assert.Throws(() => context.CreateFile("app.conf", contents: "key=value", sourcePath: "/host/app.conf")); + } + + [Fact] + public void CreateCertificateFileWithBothContentsAndSourcePathThrows() + { + var context = CreateContext(); + + Assert.Throws(() => context.CreateCertificateFile("server.pem", contents: "-----BEGIN CERTIFICATE-----", sourcePath: "/host/server.pem")); + } } diff --git a/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py b/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py index 16477a220b3..6ff0aa1827e 100644 --- a/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py +++ b/tests/PolyglotAppHosts/Aspire.Hosting/Python/apphost.py @@ -300,7 +300,7 @@ def required_command_validation(validation_ctx): validation_logger.log_information("RequiredCommandValidation services") return validation_ctx.success() - container.with_required_command("docker", validation_callback=required_command_validation) + container.with_required_command_validation("docker", required_command_validation) # withToolIgnoreExistingFeeds tool.with_tool_ignore_existing_feeds() # withToolIgnoreFailedSources From 19a5a194c13f350812d9ebdff1e33831dc0d3122 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 9 Jun 2026 12:40:08 -0700 Subject: [PATCH 3/6] Fix CI failures: expose services on command contexts after main merge After merging origin/main (#18034 obsoleted ServiceProvider in favor of a Services property), the two command callback contexts were left ignored, which broke the polyglot apphosts that call services() on them and the Containers test helper that still set the obsolete ServiceProvider. - ResourceCommandAnnotation.cs: ExecuteCommandContext and UpdateCommandStateContext now export 'services' via the Services property (ExposeProperties = true); the obsolete ServiceProvider alias keeps [AspireExportIgnore] to avoid a duplicate 'serviceProvider' export. - ContainerFileSystemCallbackContextTests.cs: use Services instead of the obsolete ServiceProvider in the test helper (fixes CS0618/CS9035). - Regenerated all 5 language codegen snapshots to include ExecuteCommandContext.services and UpdateCommandStateContext.services. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ResourceCommandAnnotation.cs | 6 +-- ...TwoPassScanningGeneratedAspire.verified.go | 40 +++++++++++++++++++ ...oPassScanningGeneratedAspire.verified.java | 16 ++++++++ ...TwoPassScanningGeneratedAspire.verified.py | 18 +++++++++ ...TwoPassScanningGeneratedAspire.verified.rs | 18 +++++++++ ...TwoPassScanningGeneratedAspire.verified.ts | 38 ++++++++++++++++++ ...ContainerFileSystemCallbackContextTests.cs | 2 +- 7 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs index 09106bd5360..674e7489cfb 100644 --- a/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs +++ b/src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs @@ -375,7 +375,7 @@ public sealed class UpdateCommandStateContext /// The service provider. /// [Obsolete("Use Services instead.")] - [AspireExportIgnore(Reason = "IServiceProvider is not usable from polyglot command state callbacks.")] + [AspireExportIgnore(Reason = "Obsolete alias for Services. The service provider is exposed to polyglot hosts via Services (services).")] public IServiceProvider ServiceProvider { get => Services; @@ -385,7 +385,6 @@ public IServiceProvider ServiceProvider /// /// The service provider. /// - [AspireExportIgnore(Reason = "IServiceProvider is not usable from polyglot command state callbacks.")] public required IServiceProvider Services { get; init; } } @@ -444,7 +443,7 @@ public sealed class ExecuteCommandContext /// The service provider. /// [Obsolete("Use Services instead.")] - [AspireExportIgnore(Reason = "IServiceProvider is not usable from polyglot command callbacks.")] + [AspireExportIgnore(Reason = "Obsolete alias for Services. The service provider is exposed to polyglot hosts via Services (services).")] public IServiceProvider ServiceProvider { get => Services; @@ -454,7 +453,6 @@ public IServiceProvider ServiceProvider /// /// The service provider. /// - [AspireExportIgnore(Reason = "IServiceProvider is not usable from polyglot command callbacks.")] public required IServiceProvider Services { get; init; } /// diff --git a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go index 272d1f53cfa..cb848596780 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go +++ b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go @@ -15341,6 +15341,7 @@ type ExecuteCommandContext interface { CancellationToken() (*CancellationToken, error) Logger() Logger ResourceName() (string, error) + Services() ServiceProvider Err() error } @@ -15422,6 +15423,25 @@ func (s *executeCommandContext) ResourceName() (string, error) { return decodeAs[string](result) } +// Services the service provider. +func (s *executeCommandContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + // ExecutionConfigurationBuilder is the public interface for handle type ExecutionConfigurationBuilder. type ExecutionConfigurationBuilder interface { handleReference @@ -27211,6 +27231,7 @@ func (s *testResourceContext) Value() (float64, error) { type UpdateCommandStateContext interface { handleReference ResourceSnapshot() (*UpdateCommandStateResourceSnapshot, error) + Services() ServiceProvider Err() error } @@ -27239,6 +27260,25 @@ func (s *updateCommandStateContext) ResourceSnapshot() (*UpdateCommandStateResou return decodeAs[*UpdateCommandStateResourceSnapshot](result) } +// Services the service provider. +func (s *updateCommandStateContext) Services() ServiceProvider { + if s.err != nil { return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(s.err, s.client)} } + ctx := context.Background() + reqArgs := map[string]any{ + "context": s.handle.ToJSON(), + } + result, err := s.client.invokeCapability(ctx, "Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services", reqArgs) + if err != nil { + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + href, ok := result.(handleReference) + if !ok { + err := fmt.Errorf("aspire: Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services returned unexpected type %T", result) + return &serviceProvider{resourceBuilderBase: newErroredResourceBuilder(err, s.client)} + } + return &serviceProvider{resourceBuilderBase: newResourceBuilderBase(href.getHandle(), s.client)} +} + // UserSecretsManager is the public interface for handle type UserSecretsManager. type UserSecretsManager interface { handleReference diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java index 447d878eb50..6ef7205d487 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java @@ -12133,6 +12133,14 @@ public class ExecuteCommandContext extends HandleWrapperBase { super(handle, client); } + /** The service provider. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services", reqArgs); + return (IServiceProvider) result; + } + /** The resource name. */ public String resourceName() { Map reqArgs = new HashMap<>(); @@ -26739,6 +26747,14 @@ public UpdateCommandStateResourceSnapshot resourceSnapshot() { return UpdateCommandStateResourceSnapshot.fromMap((Map) result); } + /** The service provider. */ + public IServiceProvider services() { + Map reqArgs = new HashMap<>(); + reqArgs.put("context", AspireClient.serializeValue(getHandle())); + var result = getClient().invokeCapability("Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services", reqArgs); + return (IServiceProvider) result; + } + } // ===== UpdateCommandStateResourceSnapshot.java ===== diff --git a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py index 3e092a7d818..42f388340fc 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py +++ b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py @@ -5091,6 +5091,15 @@ def handle(self) -> Handle: """The underlying object reference handle.""" return self._handle + @_cached_property + def services(self) -> AbstractServiceProvider: + """The service provider.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + @_cached_property def resource_name(self) -> str: """The resource name.""" @@ -6793,6 +6802,15 @@ def resource_snapshot(self) -> UpdateCommandStateResourceSnapshot: ) return typing.cast(UpdateCommandStateResourceSnapshot, result) + @_cached_property + def services(self) -> AbstractServiceProvider: + """The service provider.""" + result = self._client.invoke_capability( + 'Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services', + {'context': self._handle} + ) + return typing.cast(AbstractServiceProvider, result) + # ============================================================================ # Interface Classes diff --git a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs index ae91ddc501e..709155e400b 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs +++ b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs @@ -9605,6 +9605,15 @@ impl ExecuteCommandContext { &self.client } + /// The service provider. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } + /// The resource name. pub fn resource_name(&self) -> Result> { let mut args: HashMap = HashMap::new(); @@ -20498,6 +20507,15 @@ impl UpdateCommandStateContext { let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.resourceSnapshot", args)?; Ok(serde_json::from_value(result)?) } + + /// The service provider. + pub fn services(&self) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("context".to_string(), self.handle.to_json()); + let result = self.client.invoke_capability("Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(IServiceProvider::new(handle, self.client.clone())) + } } // ============================================================================ diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts index d3d21363d47..98e7e9ede2c 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts @@ -5562,6 +5562,8 @@ class EventingSubscriberRegistrationContextPromiseImpl implements EventingSubscr /** Context for {@link ResourceCommandAnnotation.ExecuteCommand}. */ export interface ExecuteCommandContext { toJSON(): MarshalledHandle; + /** The service provider. */ + services(): ServiceProviderPromise; /** The resource name. */ resourceName(): Promise; /** The cancellation token. */ @@ -5579,6 +5581,8 @@ export interface ExecuteCommandContext { } export interface ExecuteCommandContextPromise extends PromiseLike { + /** The service provider. */ + services(): ServiceProviderPromise; /** The resource name. */ resourceName(): Promise; /** The cancellation token. */ @@ -5606,6 +5610,17 @@ class ExecuteCommandContextImpl implements ExecuteCommandContext { /** Serialize for JSON-RPC transport */ toJSON(): MarshalledHandle { return this._handle.toJSON(); } + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ExecuteCommandContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + async resourceName(): Promise { return await this._client.invokeCapability( 'Aspire.Hosting.ApplicationModel/ExecuteCommandContext.resourceName', @@ -5656,6 +5671,10 @@ class ExecuteCommandContextPromiseImpl implements ExecuteCommandContextPromise { return this._promise.then(onfulfilled, onrejected); } + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + resourceName(): Promise { return this._promise.then(obj => obj.resourceName()); } @@ -9412,11 +9431,15 @@ export interface UpdateCommandStateContext { toJSON(): MarshalledHandle; /** Gets the resource snapshot data available to polyglot command state callbacks. */ resourceSnapshot(): Promise; + /** The service provider. */ + services(): ServiceProviderPromise; } export interface UpdateCommandStateContextPromise extends PromiseLike { /** Gets the resource snapshot data available to polyglot command state callbacks. */ resourceSnapshot(): Promise; + /** The service provider. */ + services(): ServiceProviderPromise; } // ============================================================================ @@ -9437,6 +9460,17 @@ class UpdateCommandStateContextImpl implements UpdateCommandStateContext { ); } + services(): ServiceProviderPromise { + const promise = (async () => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/UpdateCommandStateContext.services', + { context: this._handle } + ); + return new ServiceProviderImpl(handle, this._client); + })(); + return new ServiceProviderPromiseImpl(promise, this._client, false); + } + } /** @@ -9458,6 +9492,10 @@ class UpdateCommandStateContextPromiseImpl implements UpdateCommandStateContextP return this._promise.then(obj => obj.resourceSnapshot()); } + services(): ServiceProviderPromise { + return new ServiceProviderPromiseImpl(this._promise.then(obj => obj.services()), this._client, false); + } + } // ============================================================================ diff --git a/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs b/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs index 7602f722893..83257549a9a 100644 --- a/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs +++ b/tests/Aspire.Hosting.Containers.Tests/ContainerFileSystemCallbackContextTests.cs @@ -13,7 +13,7 @@ private static ContainerFileSystemCallbackContext CreateContext() return new ContainerFileSystemCallbackContext { Model = new ContainerResource("test"), - ServiceProvider = new ServiceCollection().BuildServiceProvider(), + Services = new ServiceCollection().BuildServiceProvider(), }; } From b9a88f3395033910dff35fad41ee3c5fd9b78c86 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 9 Jun 2026 13:04:07 -0700 Subject: [PATCH 4/6] Add CancellationToken.fromValue to Java SDK runtime Exposing the container-files callback overload (and other CancellationToken callbacks) made the generated Java SDK call CancellationToken.fromValue(args[N]) to materialize the token the AppHost passes when invoking the callback. The Java runtime CancellationToken template never defined that factory, so the generated apphost failed to compile (cannot find symbol: fromValue), breaking Java SDK Validation. The codegen snapshot tests only diff text and never compile Java, so this latent gap was not caught. Add a static fromValue(Object) factory mirroring the TypeScript and Go SDKs: it returns the value if it is already a CancellationToken, wraps a remote token id string, and otherwise returns a fresh token. Regenerated the Java snapshots. Verified by splitting the generated Java snapshot into files and compiling all 217 with javac: clean (no fromValue/CancellationToken errors). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Resources/Transport.java | 31 +++++++++++++++++ .../AtsGeneratedAspire.verified.java | 33 ++++++++++++++++++- ...oPassScanningGeneratedAspire.verified.java | 31 +++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java b/src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java index b4c0615b75b..acc3bf3f084 100644 --- a/src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java +++ b/src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java @@ -63,6 +63,37 @@ class CancellationToken { private volatile boolean cancelled = false; private final List listeners = new CopyOnWriteArrayList<>(); + // Remote token id supplied by the AppHost when this token is materialized for a + // callback argument. Null for locally-created tokens. Retained so cancellation can + // be correlated back to the AppHost if needed. + private final String remoteTokenId; + + CancellationToken() { + this.remoteTokenId = null; + } + + private CancellationToken(String remoteTokenId) { + this.remoteTokenId = remoteTokenId; + } + + /** + * Materializes a cancellation token from a transport value sent by the AppHost. + * When the AppHost invokes a callback that accepts a CancellationToken it passes a + * remote token id (a string); generated code calls this to turn that wire value into + * a CancellationToken instance. Mirrors the TypeScript/Go SDK behavior. + */ + static CancellationToken fromValue(Object value) { + if (value instanceof CancellationToken token) { + return token; + } + if (value instanceof String tokenId) { + return new CancellationToken(tokenId); + } + return new CancellationToken(); + } + + String getRemoteTokenId() { return remoteTokenId; } + void cancel() { cancelled = true; for (Runnable listener : listeners) { diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java index 1511e3911c8..920fa3dd4aa 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java @@ -1,4 +1,4 @@ -// ===== Aspire.java ===== +// ===== Aspire.java ===== // Aspire.java - GENERATED CODE - DO NOT EDIT package aspire; @@ -1217,6 +1217,37 @@ public class CancellationToken { private volatile boolean cancelled = false; private final List listeners = new CopyOnWriteArrayList<>(); + // Remote token id supplied by the AppHost when this token is materialized for a + // callback argument. Null for locally-created tokens. Retained so cancellation can + // be correlated back to the AppHost if needed. + private final String remoteTokenId; + + CancellationToken() { + this.remoteTokenId = null; + } + + private CancellationToken(String remoteTokenId) { + this.remoteTokenId = remoteTokenId; + } + + /** + * Materializes a cancellation token from a transport value sent by the AppHost. + * When the AppHost invokes a callback that accepts a CancellationToken it passes a + * remote token id (a string); generated code calls this to turn that wire value into + * a CancellationToken instance. Mirrors the TypeScript/Go SDK behavior. + */ + static CancellationToken fromValue(Object value) { + if (value instanceof CancellationToken token) { + return token; + } + if (value instanceof String tokenId) { + return new CancellationToken(tokenId); + } + return new CancellationToken(); + } + + String getRemoteTokenId() { return remoteTokenId; } + void cancel() { cancelled = true; for (Runnable listener : listeners) { diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java index 6ef7205d487..30529d11a29 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java @@ -3303,6 +3303,37 @@ public class CancellationToken { private volatile boolean cancelled = false; private final List listeners = new CopyOnWriteArrayList<>(); + // Remote token id supplied by the AppHost when this token is materialized for a + // callback argument. Null for locally-created tokens. Retained so cancellation can + // be correlated back to the AppHost if needed. + private final String remoteTokenId; + + CancellationToken() { + this.remoteTokenId = null; + } + + private CancellationToken(String remoteTokenId) { + this.remoteTokenId = remoteTokenId; + } + + /** + * Materializes a cancellation token from a transport value sent by the AppHost. + * When the AppHost invokes a callback that accepts a CancellationToken it passes a + * remote token id (a string); generated code calls this to turn that wire value into + * a CancellationToken instance. Mirrors the TypeScript/Go SDK behavior. + */ + static CancellationToken fromValue(Object value) { + if (value instanceof CancellationToken token) { + return token; + } + if (value instanceof String tokenId) { + return new CancellationToken(tokenId); + } + return new CancellationToken(); + } + + String getRemoteTokenId() { return remoteTokenId; } + void cancel() { cancelled = true; for (Runnable listener : listeners) { From 3bf124e1cbebe63efe69b9bd81dcf37c1dd00381 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 9 Jun 2026 14:27:03 -0700 Subject: [PATCH 5/6] Remove redundant ats-summary/ats-returns on withContainerFilesCallback The C# and XML docs are valid for ATS, and the scanner already falls back to the standard tags when no ats- override is present (AtsCapabilityScanner.GetDocumentationElement). Drop the duplicate ats-summary and ats-returns so the generated polyglot docs use the C# documentation directly. Regenerated all 5 language snapshots. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ContainerResourceBuilderExtensions.cs | 2 -- ...TwoPassScanningGeneratedAspire.verified.go | 8 +++---- ...oPassScanningGeneratedAspire.verified.java | 8 +++---- ...TwoPassScanningGeneratedAspire.verified.py | 2 +- ...TwoPassScanningGeneratedAspire.verified.rs | 8 +++---- ...TwoPassScanningGeneratedAspire.verified.ts | 24 +++++++++---------- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs index a3cd57ed9a5..701b623c3fe 100644 --- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs @@ -1599,8 +1599,6 @@ internal static IResourceBuilder WithContainerFilesExport( /// /// Options for the created or updated file system entries. /// The resource builder. - /// Creates or updates files and folders in a container using entries produced by a callback. - /// The resource builder. [AspireExport("withContainerFilesCallback", MethodName = "withContainerFilesCallback")] internal static IResourceBuilder WithContainerFilesCallbackExport( this IResourceBuilder builder, diff --git a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go index cb848596780..5b83c6b0e87 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go +++ b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go @@ -1833,7 +1833,7 @@ func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerF return s } -// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +// WithContainerFilesCallback creates or updates files and/or folders at the destination path in the container using entries produced by a callback. func (s *aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) Aspire_Hosting_CodeGeneration_Go_TestsTestVaultResource { if s.err != nil { return s } ctx := context.Background() @@ -7893,7 +7893,7 @@ func (s *containerResource) WithContainerFiles(destinationPath string, sourcePat return s } -// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +// WithContainerFilesCallback creates or updates files and/or folders at the destination path in the container using entries produced by a callback. func (s *containerResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) ContainerResource { if s.err != nil { return s } ctx := context.Background() @@ -23281,7 +23281,7 @@ func (s *testDatabaseResource) WithContainerFiles(destinationPath string, source return s } -// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +// WithContainerFilesCallback creates or updates files and/or folders at the destination path in the container using entries produced by a callback. func (s *testDatabaseResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) TestDatabaseResource { if s.err != nil { return s } ctx := context.Background() @@ -25692,7 +25692,7 @@ func (s *testRedisResource) WithContainerFiles(destinationPath string, sourcePat return s } -// WithContainerFilesCallback creates or updates files and folders in a container using entries produced by a callback. +// WithContainerFilesCallback creates or updates files and/or folders at the destination path in the container using entries produced by a callback. func (s *testRedisResource) WithContainerFilesCallback(destinationPath string, callback func(arg1 ContainerFileSystemCallbackContext, arg2 *CancellationToken) []ContainerFileSystemItem, options ...*WithContainerFilesCallbackOptions) TestRedisResource { if s.err != nil { return s } ctx := context.Background() diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java index 30529d11a29..51b5c1be8af 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java @@ -5700,7 +5700,7 @@ public ContainerResource withContainerFilesCallback(String destinationPath, Aspi return withContainerFilesCallback(destinationPath, callback, null); } - /** Creates or updates files and folders in a container using entries produced by a callback. */ + /** Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. */ public ContainerResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { Map reqArgs = new HashMap<>(); reqArgs.put("builder", AspireClient.serializeValue(getHandle())); @@ -20716,7 +20716,7 @@ public TestDatabaseResource withContainerFilesCallback(String destinationPath, A return withContainerFilesCallback(destinationPath, callback, null); } - /** Creates or updates files and folders in a container using entries produced by a callback. */ + /** Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. */ public TestDatabaseResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { Map reqArgs = new HashMap<>(); reqArgs.put("builder", AspireClient.serializeValue(getHandle())); @@ -22869,7 +22869,7 @@ public TestRedisResource withContainerFilesCallback(String destinationPath, Aspi return withContainerFilesCallback(destinationPath, callback, null); } - /** Creates or updates files and folders in a container using entries produced by a callback. */ + /** Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. */ public TestRedisResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { Map reqArgs = new HashMap<>(); reqArgs.put("builder", AspireClient.serializeValue(getHandle())); @@ -25094,7 +25094,7 @@ public TestVaultResource withContainerFilesCallback(String destinationPath, Aspi return withContainerFilesCallback(destinationPath, callback, null); } - /** Creates or updates files and folders in a container using entries produced by a callback. */ + /** Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. */ public TestVaultResource withContainerFilesCallback(String destinationPath, AspireFunc2 callback, ContainerFilesOptions options) { Map reqArgs = new HashMap<>(); reqArgs.put("builder", AspireClient.serializeValue(getHandle())); diff --git a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py index 42f388340fc..b12eec523ce 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py +++ b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py @@ -8685,7 +8685,7 @@ def with_container_files(self, destination_path: str, source_path: str, *, optio return self def with_container_files_callback(self, destination_path: str, callback: typing.Callable[[ContainerFileSystemCallbackContext, CancellationToken], typing.Iterable[ContainerFileSystemItem]], *, options: ContainerFilesOptions | None = None) -> typing.Self: - """Creates or updates files and folders in a container using entries produced by a callback.""" + """Creates or updates files and/or folders at the destination path in the container using entries produced by a callback.""" rpc_args: dict[str, typing.Any] = {'builder': self._handle} rpc_args['destinationPath'] = destination_path rpc_args['callback'] = self._client.register_callback(callback) diff --git a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs index 709155e400b..88caaba1d7e 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs +++ b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs @@ -4711,7 +4711,7 @@ impl ContainerResource { Ok(ContainerResource::new(handle, self.client.clone())) } - /// Creates or updates files and folders in a container using entries produced by a callback. + /// Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); @@ -16027,7 +16027,7 @@ impl TestDatabaseResource { Ok(ContainerResource::new(handle, self.client.clone())) } - /// Creates or updates files and folders in a container using entries produced by a callback. + /// Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); @@ -17595,7 +17595,7 @@ impl TestRedisResource { Ok(ContainerResource::new(handle, self.client.clone())) } - /// Creates or updates files and folders in a container using entries produced by a callback. + /// Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); @@ -19269,7 +19269,7 @@ impl TestVaultResource { Ok(ContainerResource::new(handle, self.client.clone())) } - /// Creates or updates files and folders in a container using entries produced by a callback. + /// Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. pub fn with_container_files_callback(&self, destination_path: &str, callback: impl Fn(Vec) -> Value + Send + Sync + 'static, options: Option) -> Result> { let mut args: HashMap = HashMap::new(); args.insert("builder".to_string(), self.handle.to_json()); diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts index 98e7e9ede2c..4e50a6e428d 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts @@ -15354,7 +15354,7 @@ export interface ContainerResource { */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): ContainerResourcePromise; /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -16161,7 +16161,7 @@ export interface ContainerResourcePromise extends PromiseLike */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): ContainerResourcePromise; /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -17253,7 +17253,7 @@ class ContainerResourceImpl extends ResourceBuilderBase } /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -40370,7 +40370,7 @@ export interface TestDatabaseResource { */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestDatabaseResourcePromise; /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -41177,7 +41177,7 @@ export interface TestDatabaseResourcePromise extends PromiseLike */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestRedisResourcePromise; /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -47209,7 +47209,7 @@ class TestRedisResourceImpl extends ResourceBuilderBase } /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -50431,7 +50431,7 @@ export interface TestVaultResource { */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestVaultResourcePromise; /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -51240,7 +51240,7 @@ export interface TestVaultResourcePromise extends PromiseLike */ withContainerFiles(destinationPath: string, sourcePath: string, options?: ContainerFilesOptions): TestVaultResourcePromise; /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. @@ -52333,7 +52333,7 @@ class TestVaultResourceImpl extends ResourceBuilderBase } /** - * Creates or updates files and folders in a container using entries produced by a callback. + * Creates or updates files and/or folders at the destination path in the container using entries produced by a callback. * @param destinationPath The destination absolute path in the container. * @param callback A callback that returns the file system entries to create or update. Use the factory methods on `ContainerFileSystemCallbackContext` (createFile, createDirectory, createCertificateFile) to build the entries. * @param options Additional options. From de4e53c417aabe4918bd3ec70c31658af7639bce Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 9 Jun 2026 14:38:35 -0700 Subject: [PATCH 6/6] Remove redundant remarks duplicating AspireExportIgnore Reason Both ignored WithContainerFiles overloads had a one-line that restated the attribute's Reason. Drop them; the [AspireExportIgnore] Reason already documents how each overload is exposed to ATS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs index 701b623c3fe..99cc2f4ea98 100644 --- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs @@ -1457,7 +1457,6 @@ public static IResourceBuilder WithContainerFiles(this IResourceBuilder /// /// /// - /// This overload is exposed to polyglot app hosts via the withContainerFilesCallback shim, which constructs entries through factory methods on . [AspireExportIgnore(Reason = "Exposed to ATS via the WithContainerFilesCallbackExport shim, which accepts integer file-mode options and lets polyglot callbacks build the IEnumerable result through ContainerFileSystemCallbackContext factory methods (createFile/createDirectory/createCertificateFile).")] public static IResourceBuilder WithContainerFiles(this IResourceBuilder builder, string destinationPath, Func>> callback, int? defaultOwner = null, int? defaultGroup = null, UnixFileMode? umask = null) where T : ContainerResource { @@ -1490,7 +1489,6 @@ public static IResourceBuilder WithContainerFiles(this IResourceBuilder /// The default group ID for the created or updated file system. Defaults to 0 for root if not set. /// The umask permissions to exclude from the default file and folder permissions. This takes away (rather than granting) default permissions to files and folders without an explicit mode permission set. /// The . - /// This overload is exposed to polyglot app hosts via the withContainerFiles shim, which accepts integer file-mode options. [AspireExportIgnore(Reason = "Exposed to ATS via the WithContainerFilesExport shim overload, which accepts integer file-mode options (ContainerFilesOptions) in place of the UnixFileMode parameter.")] public static IResourceBuilder WithContainerFiles(this IResourceBuilder builder, string destinationPath, string sourcePath, int? defaultOwner = null, int? defaultGroup = null, UnixFileMode? umask = null) where T : ContainerResource {