Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ protected override async Task OnInitializedAsync()
TimeProvider.SetBrowserTimeZone(result.TimeZone);
TelemetryContextProvider.SetBrowserUserAgent(result.UserAgent);

if (Options.CurrentValue.Otlp.AuthMode == OtlpAuthMode.Unsecured)
if (Options.CurrentValue.Otlp.AuthMode == OtlpAuthMode.Unsecured && !Options.CurrentValue.Otlp.SuppressUnsecuredTelemetryMessage)
{
var dismissedResult = await LocalStorage.GetUnprotectedAsync<bool>(BrowserStorageKeys.UnsecuredTelemetryMessageDismissedKey);
var skipMessage = dismissedResult.Success && dismissedResult.Value;
Expand Down
6 changes: 6 additions & 0 deletions src/Aspire.Dashboard/Configuration/DashboardOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public sealed class OtlpOptions

public List<AllowedCertificateRule> AllowedCertificates { get; set; } = new();

/// <summary>
/// Gets or sets a value indicating whether to suppress the unsecured telemetry message in the dashboard UI.
/// When true, the warning message about unsecured OTLP endpoints will not be displayed.
/// </summary>
public bool SuppressUnsecuredTelemetryMessage { get; set; }

public BindingAddress? GetGrpcEndpointAddress()
{
return _parsedGrpcEndpointAddress;
Expand Down
1 change: 1 addition & 0 deletions src/Shared/DashboardConfigNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal static class DashboardConfigNames
public static readonly ConfigName DashboardOtlpAuthModeName = new("Dashboard:Otlp:AuthMode", "DASHBOARD__OTLP__AUTHMODE");
public static readonly ConfigName DashboardOtlpPrimaryApiKeyName = new("Dashboard:Otlp:PrimaryApiKey", "DASHBOARD__OTLP__PRIMARYAPIKEY");
public static readonly ConfigName DashboardOtlpSecondaryApiKeyName = new("Dashboard:Otlp:SecondaryApiKey", "DASHBOARD__OTLP__SECONDARYAPIKEY");
public static readonly ConfigName DashboardOtlpSuppressUnsecuredTelemetryMessageName = new("Dashboard:Otlp:SuppressUnsecuredTelemetryMessage", "DASHBOARD__OTLP__SUPPRESSUNSECUREDTELEMETRYMESSAGE");
public static readonly ConfigName DashboardOtlpCorsAllowedOriginsKeyName = new("Dashboard:Otlp:Cors:AllowedOrigins", "DASHBOARD__OTLP__CORS__ALLOWEDORIGINS");
public static readonly ConfigName DashboardOtlpCorsAllowedHeadersKeyName = new("Dashboard:Otlp:Cors:AllowedHeaders", "DASHBOARD__OTLP__CORS__ALLOWEDHEADERS");
public static readonly ConfigName DashboardOtlpAllowedCertificatesName = new("Dashboard:Otlp:AllowedCertificates", "DASHBOARD__OTLP__ALLOWEDCERTIFICATES");
Expand Down
69 changes: 64 additions & 5 deletions tests/Aspire.Dashboard.Components.Tests/Layout/MainLayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Aspire.Dashboard.Tests;
using Aspire.Dashboard.Utils;
using Bunit;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FluentUI.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components.Components.Tooltip;
Expand Down Expand Up @@ -72,13 +73,13 @@ public async Task OnInitialize_UnsecuredOtlp_NotDismissed_DisplayMessageBar()
});

// Assert
await messageShownTcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
await messageShownTcs.Task.DefaultTimeout();

Assert.NotNull(message);

message.Close();

Assert.True(await dismissedSettingSetTcs.Task.WaitAsync(TimeSpan.FromSeconds(5)));
Assert.True(await dismissedSettingSetTcs.Task.DefaultTimeout());
}

[Fact]
Expand Down Expand Up @@ -117,15 +118,69 @@ public async Task OnInitialize_UnsecuredOtlp_Dismissed_NoMessageBar()

// Assert
var timeoutTask = Task.Delay(100);
var completedTask = await Task.WhenAny(messageShownTcs.Task, timeoutTask).WaitAsync(TimeSpan.FromSeconds(5));
var completedTask = await Task.WhenAny(messageShownTcs.Task, timeoutTask).DefaultTimeout();

// It's hard to test something not happening.
// In this case of checking for a message, apply a small display and then double check that no message was displayed.
Assert.True(completedTask != messageShownTcs.Task, "No message bar should be displayed.");
Assert.Empty(messageService.AllMessages);
}

private void SetupMainLayoutServices(TestLocalStorage? localStorage = null, MessageService? messageService = null)
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task OnInitialize_UnsecuredOtlp_SuppressConfigured_NoMessageBar(bool suppressUnsecuredMessage)
{
// Arrange
var testLocalStorage = new TestLocalStorage();
var messageService = new MessageService();

SetupMainLayoutServices(localStorage: testLocalStorage, messageService: messageService, suppressUnsecuredMessage: suppressUnsecuredMessage);

var messageShownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
messageService.OnMessageItemsUpdatedAsync += () =>
{
messageShownTcs.TrySetResult();
return Task.CompletedTask;
};

testLocalStorage.OnGetUnprotectedAsync = key =>
{
if (key == BrowserStorageKeys.UnsecuredTelemetryMessageDismissedKey)
{
return (false, false); // Message not dismissed, but should be suppressed by config if suppressUnsecuredMessage is true
}
else
{
throw new InvalidOperationException("Unexpected key.");
}
};

// Act
var cut = RenderComponent<MainLayout>(builder =>
{
builder.Add(p => p.ViewportInformation, new ViewportInformation(IsDesktop: true, IsUltraLowHeight: false, IsUltraLowWidth: false));
});

// Assert
if (suppressUnsecuredMessage)
{
var timeoutTask = Task.Delay(100);
var completedTask = await Task.WhenAny(messageShownTcs.Task, timeoutTask).DefaultTimeout();

// When suppressed, no message should be displayed
Assert.True(completedTask != messageShownTcs.Task, "No message bar should be displayed when suppressed by configuration.");
Assert.Empty(messageService.AllMessages);
}
else
{
// When not suppressed, message should be displayed since it wasn't dismissed
await messageShownTcs.Task.DefaultTimeout();
Assert.NotEmpty(messageService.AllMessages);
}
}

private void SetupMainLayoutServices(TestLocalStorage? localStorage = null, MessageService? messageService = null, bool suppressUnsecuredMessage = false)
{
Services.AddLocalization();
Services.AddOptions();
Expand All @@ -144,7 +199,11 @@ private void SetupMainLayoutServices(TestLocalStorage? localStorage = null, Mess
Services.AddSingleton<DashboardTelemetryService>();
Services.AddSingleton<IDashboardTelemetrySender, TestDashboardTelemetrySender>();
Services.AddSingleton<ComponentTelemetryContextProvider>();
Services.Configure<DashboardOptions>(o => o.Otlp.AuthMode = OtlpAuthMode.Unsecured);
Services.Configure<DashboardOptions>(o =>
{
o.Otlp.AuthMode = OtlpAuthMode.Unsecured;
o.Otlp.SuppressUnsecuredTelemetryMessage = suppressUnsecuredMessage;
});

var version = typeof(FluentMain).Assembly.GetName().Version!;

Expand Down