Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetArgumentListAsync to resource extentions #6646

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,49 @@ public static int GetReplicaCount(this IResource resource)
}
}

/// <summary>
/// Get the arguments from the given resource.
/// </summary>
/// <remarks>
/// This method is useful when you want to make sure the arguments are added properly to resources, mostly in test situations.
/// This method has asynchronous behavior when arguments were provided from <see cref="IValueProvider"/> otherwise it will be synchronous.
/// </remarks>
/// <param name="resource">The resource to get the arguments from.</param>
/// <returns>The arguments retrieved from the resource.</returns>
public static async ValueTask<IReadOnlyList<string>> GetArgumentListAsync(this IResourceWithArgs resource)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any places in the current code that can be using this new API now? I see a bunch of places where we already have this logic. Especially in tests.

Copy link
Contributor Author

@Alirexaa Alirexaa Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are. Do you want me to update those codes that use this new API or can I do this in another PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do it here. It adds extra validation to the API.

{
var finalArgs = new List<string>();

if (resource.TryGetAnnotationsOfType<CommandLineArgsCallbackAnnotation>(out var exeArgsCallbacks))
{
var args = new List<object>();
var commandLineContext = new CommandLineArgsCallbackContext(args, default);

foreach (var exeArgsCallback in exeArgsCallbacks)
{
await exeArgsCallback.Callback(commandLineContext).ConfigureAwait(false);
}

foreach (var arg in args)
{
var value = arg switch
{
string s => s,
IValueProvider valueProvider => await valueProvider.GetValueAsync().ConfigureAwait(false),
null => null,
_ => throw new InvalidOperationException($"Unexpected value for {arg}")
};

if (value is not null)
{
finalArgs.Add(value);
}
}
}

return finalArgs;
}

/// <summary>
/// Gets the lifetime type of the container for the specified resource.
/// Defaults to <see cref="ContainerLifetime.Session"/> if no <see cref="ContainerLifetimeAnnotation"/> is found.
Expand Down
1 change: 1 addition & 0 deletions src/Aspire.Hosting/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ Aspire.Hosting.LaunchSettings.Profiles.get -> System.Collections.Generic.Diction
Aspire.Hosting.LaunchSettings.Profiles.set -> void
Aspire.Hosting.Utils.VolumeNameGenerator
static Aspire.Hosting.ApplicationModel.CommandResults.Success() -> Aspire.Hosting.ApplicationModel.ExecuteCommandResult!
static Aspire.Hosting.ApplicationModel.ResourceExtensions.GetArgumentListAsync(this Aspire.Hosting.ApplicationModel.IResourceWithArgs! resource) -> System.Threading.Tasks.ValueTask<System.Collections.Generic.IReadOnlyList<string!>!>
static Aspire.Hosting.ApplicationModel.ResourceExtensions.GetEnvironmentVariableValuesAsync(this Aspire.Hosting.ApplicationModel.IResourceWithEnvironment! resource, Aspire.Hosting.DistributedApplicationOperation applicationOperation = Aspire.Hosting.DistributedApplicationOperation.Run) -> System.Threading.Tasks.ValueTask<System.Collections.Generic.Dictionary<string!, string!>!>
Aspire.Hosting.ApplicationModel.ResourceNotificationService.WaitForResourceAsync(string! resourceName, string? targetState = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Aspire.Hosting.ApplicationModel.ResourceNotificationService.WaitForResourceAsync(string! resourceName, System.Collections.Generic.IEnumerable<string!>! targetStates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string!>!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Tests.Utils;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
Expand Down Expand Up @@ -73,7 +72,7 @@ public async Task AddContainerWithArgs()

using var app = appBuilder.Build();

var args = await ArgumentEvaluator.GetArgumentListAsync(c2.Resource);
var args = await c2.Resource.GetArgumentListAsync();

Assert.Collection(args,
arg => Assert.Equal("arg1", arg),
Expand Down
4 changes: 2 additions & 2 deletions tests/Aspire.Hosting.Dapr.Tests/DaprTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public async Task WithDaprSideCarAddsAnnotationAndSidecarResource()
}

var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(container, DistributedApplicationOperation.Run, TestServiceProvider.Instance);
var sidecarArgs = await ArgumentEvaluator.GetArgumentListAsync(sideCarCli);
var sidecarArgs = await sideCarCli.GetArgumentListAsync();

Assert.Equal("3500", config["DAPR_HTTP_PORT"]);
Assert.Equal("50001", config["DAPR_GRPC_PORT"]);
Expand Down Expand Up @@ -156,7 +156,7 @@ public async Task WithDaprSideCarAddsAnnotationBasedOnTheSidecarAppOptions(strin
}

var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(container, DistributedApplicationOperation.Run, TestServiceProvider.Instance);
var sidecarArgs = await ArgumentEvaluator.GetArgumentListAsync(sideCarCli);
var sidecarArgs = await sideCarCli.GetArgumentListAsync();

Assert.Equal("http://host.docker.internal:3500", config["DAPR_HTTP_ENDPOINT"]);
Assert.Equal("http://host.docker.internal:50001", config["DAPR_GRPC_ENDPOINT"]);
Expand Down
5 changes: 2 additions & 3 deletions tests/Aspire.Hosting.Nats.Tests/AddNatsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Net.Sockets;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Tests.Utils;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
Expand Down Expand Up @@ -110,7 +109,7 @@ public async Task AddNatsContainerWithDefaultsAddsAnnotationMetadata()
Assert.Equal(NatsContainerImageTags.Image, containerAnnotation.Image);
Assert.Equal(NatsContainerImageTags.Registry, containerAnnotation.Registry);

var args = await ArgumentEvaluator.GetArgumentListAsync(containerResource);
var args = await containerResource.GetArgumentListAsync();

Assert.Collection(args,
arg => Assert.Equal("--user", arg),
Expand Down Expand Up @@ -156,7 +155,7 @@ public async Task AddNatsContainerAddsAnnotationMetadata()
Assert.Equal(NatsContainerImageTags.Image, containerAnnotation.Image);
Assert.Equal(NatsContainerImageTags.Registry, containerAnnotation.Registry);

var args = await ArgumentEvaluator.GetArgumentListAsync(containerResource);
var args = await containerResource.GetArgumentListAsync();

Assert.Collection(args,
arg => Assert.Equal("--user", arg),
Expand Down
7 changes: 3 additions & 4 deletions tests/Aspire.Hosting.Python.Tests/AddPythonAppTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Xunit;
using Microsoft.Extensions.DependencyInjection;
using Aspire.Hosting.Utils;
using Aspire.Hosting.Tests.Utils;
using System.Diagnostics;
using Aspire.Components.Common.Tests;
using Xunit.Abstractions;
Expand Down Expand Up @@ -154,7 +153,7 @@ public async Task AddPythonApp_SetsResourcePropertiesCorrectly()
Assert.Equal(Path.Join(projectDirectory, ".venv", "bin", "python"), pythonProjectResource.Command);
}

var commandArguments = await ArgumentEvaluator.GetArgumentListAsync(pythonProjectResource);
var commandArguments = await pythonProjectResource.GetArgumentListAsync();

Assert.Equal(scriptName, commandArguments[0]);

Expand All @@ -177,7 +176,7 @@ public async Task AddPythonAppWithInstrumentation_SwitchesExecutableToInstrument
var executableResources = appModel.GetExecutableResources();

var pythonProjectResource = Assert.Single(executableResources);
var commandArguments = await ArgumentEvaluator.GetArgumentListAsync(pythonProjectResource);
var commandArguments = await pythonProjectResource.GetArgumentListAsync();

if (OperatingSystem.IsWindows())
{
Expand Down Expand Up @@ -229,7 +228,7 @@ public async Task AddPythonAppWithScriptArgs_IncludesTheArguments()
Assert.Equal(Path.Join(projectDirectory, ".venv", "bin", "python"), pythonProjectResource.Command);
}

var commandArguments = await ArgumentEvaluator.GetArgumentListAsync(pythonProjectResource);
var commandArguments = await pythonProjectResource.GetArgumentListAsync();

Assert.Equal(scriptName, commandArguments[0]);
Assert.Equal("test", commandArguments[1]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public async Task DashboardWithDllPathLaunchesDotnet()

var dashboard = Assert.Single(model.Resources.OfType<ExecutableResource>());

var args = await ArgumentEvaluator.GetArgumentListAsync(dashboard).DefaultTimeout();
var args = await dashboard.GetArgumentListAsync().DefaultTimeout();

Assert.NotNull(dashboard);
Assert.Equal("aspire-dashboard", dashboard.Name);
Expand Down
3 changes: 1 addition & 2 deletions tests/Aspire.Hosting.Tests/ExecutableResourceTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// 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.Tests.Utils;
using Aspire.Hosting.Utils;
using Microsoft.AspNetCore.InternalTesting;
using Xunit;
Expand Down Expand Up @@ -36,7 +35,7 @@ public async Task AddExecutableWithArgs()

using var app = appBuilder.Build();

var args = await ArgumentEvaluator.GetArgumentListAsync(exe2.Resource).DefaultTimeout();
var args = await exe2.Resource.GetArgumentListAsync().DefaultTimeout();

Assert.Collection(args,
arg => Assert.Equal("app.py", arg),
Expand Down
2 changes: 1 addition & 1 deletion tests/Aspire.Hosting.Tests/ProjectResourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ public async Task AddProjectWithArgs()

using var app = appBuilder.Build();

var args = await ArgumentEvaluator.GetArgumentListAsync(project.Resource).DefaultTimeout();
var args = await project.Resource.GetArgumentListAsync().DefaultTimeout();

Assert.Collection(args,
arg => Assert.Equal("arg1", arg),
Expand Down
31 changes: 31 additions & 0 deletions tests/Aspire.Hosting.Tests/ResourceExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,32 @@ public async Task GetEnvironmentVariableValuesAsyncReturnCorrectVariablesUsingMa
});
}

[Fact]
public async Task GetArgumentListAsyncReturnCorrectArguments()
{
var builder = DistributedApplication.CreateBuilder();
builder.Configuration["Parameters:DummyKey"] = "DummyValue";

var dummyParameter = builder.AddParameter("DummyKey").Resource;
var resource = new DummyResource("dummy");
var container = builder.AddResource(resource)
.WithArgs(["--arg1", "--arg2"])
.WithArgs(context =>
{
context.Args.Add("--arg3");
})
.WithArgs(dummyParameter);

var args = await container.Resource.GetArgumentListAsync().DefaultTimeout();

Assert.Collection(args,
arg => Assert.Equal("--arg1", arg),
arg => Assert.Equal("--arg2", arg),
arg => Assert.Equal("--arg3", arg),
arg => Assert.Equal("DummyValue", arg)
);
}

private sealed class ParentResource(string name) : Resource(name)
{

Expand All @@ -278,4 +304,9 @@ private sealed class AnotherDummyAnnotation : IResourceAnnotation
{

}

private sealed class DummyResource(string name) : Resource(name), IResourceWithArgs
{

}
}
41 changes: 0 additions & 41 deletions tests/Aspire.Hosting.Tests/Utils/ArgumentEvaluator.cs

This file was deleted.