Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
45 changes: 27 additions & 18 deletions src/Aspire.Dashboard/Model/ResourceSourceViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using Aspire.Dashboard.Utils;

namespace Aspire.Dashboard.Model;

public class ResourceSourceViewModel(string value, List<LaunchArgument>? contentAfterValue, string valueToVisualize, string tooltip)
Expand All @@ -12,15 +15,15 @@ public class ResourceSourceViewModel(string value, List<LaunchArgument>? content

internal static ResourceSourceViewModel? GetSourceViewModel(ResourceViewModel resource)
{
(List<LaunchArgument>? Arguments, string? ArgumentsString) commandLineInfo;
CommandLineInfo? commandLineInfo;
Comment thread
adamint marked this conversation as resolved.
Outdated

// If the resource contains launch arguments, these project arguments should be shown in place of all executable arguments,
// which include args added by the app host
if (resource.TryGetAppArgs(out var launchArguments))
{
if (launchArguments.IsDefaultOrEmpty)
{
commandLineInfo = (null, null);
commandLineInfo = null;
}
else
{
Expand All @@ -31,46 +34,50 @@ public class ResourceSourceViewModel(string value, List<LaunchArgument>? content
.Select((arg, i) => new LaunchArgument(arg, IsShown: !areArgumentsSensitive[i]))
.ToList();

commandLineInfo = (Arguments: arguments, argumentsString);
commandLineInfo = new CommandLineInfo(
Arguments: arguments,
ArgumentsString: argumentsString,
TooltipString: string.Join(" ", arguments.Select(arg => arg.IsShown
? arg.Value
: WebUtility.HtmlDecode(DashboardUIHelpers.GetMaskingText(6).Value))));
Comment thread
adamint marked this conversation as resolved.
Outdated
Comment thread
adamint marked this conversation as resolved.
Outdated
}
else
{
commandLineInfo = (Arguments: launchArguments.Select(arg => new LaunchArgument(arg, true)).ToList(), argumentsString);
commandLineInfo = new CommandLineInfo(Arguments: launchArguments.Select(arg => new LaunchArgument(arg, true)).ToList(), ArgumentsString: argumentsString, TooltipString: argumentsString);
}
}
}
else if (resource.TryGetExecutableArguments(out var executableArguments) && !resource.IsProject())
{
var arguments = executableArguments.IsDefaultOrEmpty ? null : executableArguments.Select(arg => new LaunchArgument(arg, true)).ToList();
commandLineInfo = (Arguments: arguments, string.Join(' ', executableArguments));
var arguments = executableArguments.IsDefaultOrEmpty ? [] : executableArguments.Select(arg => new LaunchArgument(arg, true)).ToList();
Comment thread
adamint marked this conversation as resolved.
Outdated
commandLineInfo = new CommandLineInfo(Arguments: arguments, ArgumentsString: string.Join(' ', executableArguments), TooltipString: string.Join(' ', executableArguments));
Comment thread
adamint marked this conversation as resolved.
Outdated
}
else
{
commandLineInfo = (Arguments: null, null);
commandLineInfo = null;
}

// NOTE projects are also executables, so we have to check for projects first
if (resource.IsProject() && resource.TryGetProjectPath(out var projectPath))
{
if (commandLineInfo is { Arguments: { } arguments, ArgumentsString: { } fullCommandLine })
{
return new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: arguments, valueToVisualize: $"{projectPath} {fullCommandLine}", tooltip: $"{projectPath} {fullCommandLine}");
}

// default to project path if there is no executable path or executable arguments
return new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: commandLineInfo.Arguments, valueToVisualize: projectPath, tooltip: projectPath);
return commandLineInfo is not null
? new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: commandLineInfo.Arguments, valueToVisualize: $"{projectPath} {commandLineInfo.ArgumentsString}", tooltip: $"{projectPath} {commandLineInfo.TooltipString}")
// default to project path if there is no executable path or executable arguments
: new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: null, valueToVisualize: projectPath, tooltip: projectPath);
Comment thread
adamint marked this conversation as resolved.
Outdated
}

if (resource.TryGetExecutablePath(out var executablePath))
{
var fullSource = commandLineInfo.ArgumentsString is not null ? $"{executablePath} {commandLineInfo.ArgumentsString}" : executablePath;
return new ResourceSourceViewModel(value: Path.GetFileName(executablePath), contentAfterValue: commandLineInfo.Arguments, valueToVisualize: fullSource, tooltip: fullSource);
return commandLineInfo is not null
? new ResourceSourceViewModel(value: Path.GetFileName(executablePath), contentAfterValue: commandLineInfo.Arguments, valueToVisualize: $"{executablePath} {commandLineInfo.ArgumentsString}", tooltip: $"{executablePath} {commandLineInfo.TooltipString}")
: new ResourceSourceViewModel(value: Path.GetFileName(executablePath), contentAfterValue: null, valueToVisualize: executablePath, tooltip: executablePath);
}

if (resource.TryGetContainerImage(out var containerImage))
{
var fullSource = commandLineInfo.ArgumentsString is null ? containerImage : $"{containerImage} {commandLineInfo.ArgumentsString}";
return new ResourceSourceViewModel(value: containerImage, contentAfterValue: commandLineInfo.Arguments, valueToVisualize: fullSource, tooltip: fullSource);
return commandLineInfo is not null
? new ResourceSourceViewModel(value: containerImage, contentAfterValue: commandLineInfo.Arguments, valueToVisualize: $"{containerImage} {commandLineInfo.ArgumentsString}", tooltip: $"{containerImage} {commandLineInfo.TooltipString}")
: new ResourceSourceViewModel(value: containerImage, contentAfterValue: null, valueToVisualize: containerImage, tooltip: containerImage);
}

if (resource.Properties.TryGetValue(KnownProperties.Resource.Source, out var property) && property.Value is { HasStringValue: true, StringValue: var value })
Expand All @@ -80,6 +87,8 @@ public class ResourceSourceViewModel(string value, List<LaunchArgument>? content

return null;
}

private record CommandLineInfo(List<LaunchArgument> Arguments, string ArgumentsString, string TooltipString);
}

public record LaunchArgument(string Value, bool IsShown);
16 changes: 7 additions & 9 deletions src/Aspire.Hosting/ApplicationModel/ExpressionResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,14 @@ async Task<ResolvedValue> EvalValueProvider(IValueProvider vp)
/// </summary>
async ValueTask<ResolvedValue> ResolveInternalAsync(object? value)
{
return (value, sourceIsContainer) switch
Comment thread
adamint marked this conversation as resolved.
return value switch
{
(ConnectionStringReference cs, true) => await ResolveInternalAsync(cs.Resource.ConnectionStringExpression).ConfigureAwait(false),
(IResourceWithConnectionString cs, true) => await ResolveInternalAsync(cs.ConnectionStringExpression).ConfigureAwait(false),
(ReferenceExpression ex, false) => await EvalExpressionAsync(ex).ConfigureAwait(false),
(ReferenceExpression ex, true) => await EvalExpressionAsync(ex).ConfigureAwait(false),
(EndpointReference endpointReference, true) => new(await EvalEndpointAsync(endpointReference, EndpointProperty.Url).ConfigureAwait(false), false),
(EndpointReferenceExpression ep, true) => new(await EvalEndpointAsync(ep.Endpoint, ep.Property).ConfigureAwait(false), false),
(IValueProvider vp, false) => await EvalValueProvider(vp).ConfigureAwait(false),
(IValueProvider vp, true) => await EvalValueProvider(vp).ConfigureAwait(false),
ConnectionStringReference cs => await ResolveInternalAsync(cs.Resource.ConnectionStringExpression).ConfigureAwait(false),
IResourceWithConnectionString cs => await ResolveInternalAsync(cs.ConnectionStringExpression).ConfigureAwait(false),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@davidfowl @mitchdenny @davidebbo - do we even need these 2 cases? Since ConnectionStringReference and IResourceWithConnectionString implement IValueProvider, shouldn't this just fall to that case?

(Note we don't necessarily need to change this in this PR, but I'm just trying to understand if I have the logic correct. If we are going to backport this to 9.1, we may not want to risk a change here.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nevermind, I think I understand why this is necessary - because we want to intercept any "host" references inside the ConnectionString and redirect them to the containerHostName.

Would it ever work if someone implemented an IValueProvider that contained a "host" reference, but wasn't one of these?

ReferenceExpression ex => await EvalExpressionAsync(ex).ConfigureAwait(false),
EndpointReference endpointReference => new(await EvalEndpointAsync(endpointReference, EndpointProperty.Url).ConfigureAwait(false), false),
EndpointReferenceExpression ep => new(await EvalEndpointAsync(ep.Endpoint, ep.Property).ConfigureAwait(false), false),
IValueProvider vp => await EvalValueProvider(vp).ConfigureAwait(false),
_ => throw new NotImplementedException()
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Dashboard.Model;
using Aspire.Dashboard.Utils;
using Aspire.Tests.Shared.DashboardModel;
using Google.Protobuf.WellKnownTypes;
using Microsoft.Extensions.Logging.Abstractions;
Expand Down Expand Up @@ -94,7 +95,7 @@ void AddStringProperty(string propertyName, string? propertyValue)
value: "project",
contentAfterValue: [new LaunchArgument("arg2", true), new LaunchArgument("--key", true), new LaunchArgument("secret", false)],
valueToVisualize: "path/to/project arg2 --key secret",
tooltip: "path/to/project arg2 --key secret"));
tooltip: $"path/to/project arg2 --key {DashboardUIHelpers.GetMaskingText(6).Value}"));

// Project without executable arguments
data.Add(new TestData(
Expand Down