Skip to content

Commit d9077f9

Browse files
CopilotJamesNK
andauthored
Add configuration to suppress unsecured telemetry message in dashboard (#11673)
Co-authored-by: JamesNK <[email protected]> Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: James Newton-King <[email protected]>
1 parent e5ed068 commit d9077f9

File tree

4 files changed

+72
-6
lines changed

4 files changed

+72
-6
lines changed

src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ protected override async Task OnInitializedAsync()
105105
TimeProvider.SetBrowserTimeZone(result.TimeZone);
106106
TelemetryContextProvider.SetBrowserUserAgent(result.UserAgent);
107107

108-
if (Options.CurrentValue.Otlp.AuthMode == OtlpAuthMode.Unsecured)
108+
if (Options.CurrentValue.Otlp.AuthMode == OtlpAuthMode.Unsecured && !Options.CurrentValue.Otlp.SuppressUnsecuredTelemetryMessage)
109109
{
110110
var dismissedResult = await LocalStorage.GetUnprotectedAsync<bool>(BrowserStorageKeys.UnsecuredTelemetryMessageDismissedKey);
111111
var skipMessage = dismissedResult.Success && dismissedResult.Value;

src/Aspire.Dashboard/Configuration/DashboardOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ public sealed class OtlpOptions
8585

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

88+
/// <summary>
89+
/// Gets or sets a value indicating whether to suppress the unsecured telemetry message in the dashboard UI.
90+
/// When true, the warning message about unsecured OTLP endpoints will not be displayed.
91+
/// </summary>
92+
public bool SuppressUnsecuredTelemetryMessage { get; set; }
93+
8894
public BindingAddress? GetGrpcEndpointAddress()
8995
{
9096
return _parsedGrpcEndpointAddress;

src/Shared/DashboardConfigNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal static class DashboardConfigNames
1818
public static readonly ConfigName DashboardOtlpAuthModeName = new("Dashboard:Otlp:AuthMode", "DASHBOARD__OTLP__AUTHMODE");
1919
public static readonly ConfigName DashboardOtlpPrimaryApiKeyName = new("Dashboard:Otlp:PrimaryApiKey", "DASHBOARD__OTLP__PRIMARYAPIKEY");
2020
public static readonly ConfigName DashboardOtlpSecondaryApiKeyName = new("Dashboard:Otlp:SecondaryApiKey", "DASHBOARD__OTLP__SECONDARYAPIKEY");
21+
public static readonly ConfigName DashboardOtlpSuppressUnsecuredTelemetryMessageName = new("Dashboard:Otlp:SuppressUnsecuredTelemetryMessage", "DASHBOARD__OTLP__SUPPRESSUNSECUREDTELEMETRYMESSAGE");
2122
public static readonly ConfigName DashboardOtlpCorsAllowedOriginsKeyName = new("Dashboard:Otlp:Cors:AllowedOrigins", "DASHBOARD__OTLP__CORS__ALLOWEDORIGINS");
2223
public static readonly ConfigName DashboardOtlpCorsAllowedHeadersKeyName = new("Dashboard:Otlp:Cors:AllowedHeaders", "DASHBOARD__OTLP__CORS__ALLOWEDHEADERS");
2324
public static readonly ConfigName DashboardOtlpAllowedCertificatesName = new("Dashboard:Otlp:AllowedCertificates", "DASHBOARD__OTLP__ALLOWEDCERTIFICATES");

tests/Aspire.Dashboard.Components.Tests/Layout/MainLayoutTests.cs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Aspire.Dashboard.Tests;
1313
using Aspire.Dashboard.Utils;
1414
using Bunit;
15+
using Microsoft.AspNetCore.InternalTesting;
1516
using Microsoft.Extensions.DependencyInjection;
1617
using Microsoft.FluentUI.AspNetCore.Components;
1718
using Microsoft.FluentUI.AspNetCore.Components.Components.Tooltip;
@@ -72,13 +73,13 @@ public async Task OnInitialize_UnsecuredOtlp_NotDismissed_DisplayMessageBar()
7273
});
7374

7475
// Assert
75-
await messageShownTcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
76+
await messageShownTcs.Task.DefaultTimeout();
7677

7778
Assert.NotNull(message);
7879

7980
message.Close();
8081

81-
Assert.True(await dismissedSettingSetTcs.Task.WaitAsync(TimeSpan.FromSeconds(5)));
82+
Assert.True(await dismissedSettingSetTcs.Task.DefaultTimeout());
8283
}
8384

8485
[Fact]
@@ -117,15 +118,69 @@ public async Task OnInitialize_UnsecuredOtlp_Dismissed_NoMessageBar()
117118

118119
// Assert
119120
var timeoutTask = Task.Delay(100);
120-
var completedTask = await Task.WhenAny(messageShownTcs.Task, timeoutTask).WaitAsync(TimeSpan.FromSeconds(5));
121+
var completedTask = await Task.WhenAny(messageShownTcs.Task, timeoutTask).DefaultTimeout();
121122

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

128-
private void SetupMainLayoutServices(TestLocalStorage? localStorage = null, MessageService? messageService = null)
129+
[Theory]
130+
[InlineData(false)]
131+
[InlineData(true)]
132+
public async Task OnInitialize_UnsecuredOtlp_SuppressConfigured_NoMessageBar(bool suppressUnsecuredMessage)
133+
{
134+
// Arrange
135+
var testLocalStorage = new TestLocalStorage();
136+
var messageService = new MessageService();
137+
138+
SetupMainLayoutServices(localStorage: testLocalStorage, messageService: messageService, suppressUnsecuredMessage: suppressUnsecuredMessage);
139+
140+
var messageShownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
141+
messageService.OnMessageItemsUpdatedAsync += () =>
142+
{
143+
messageShownTcs.TrySetResult();
144+
return Task.CompletedTask;
145+
};
146+
147+
testLocalStorage.OnGetUnprotectedAsync = key =>
148+
{
149+
if (key == BrowserStorageKeys.UnsecuredTelemetryMessageDismissedKey)
150+
{
151+
return (false, false); // Message not dismissed, but should be suppressed by config if suppressUnsecuredMessage is true
152+
}
153+
else
154+
{
155+
throw new InvalidOperationException("Unexpected key.");
156+
}
157+
};
158+
159+
// Act
160+
var cut = RenderComponent<MainLayout>(builder =>
161+
{
162+
builder.Add(p => p.ViewportInformation, new ViewportInformation(IsDesktop: true, IsUltraLowHeight: false, IsUltraLowWidth: false));
163+
});
164+
165+
// Assert
166+
if (suppressUnsecuredMessage)
167+
{
168+
var timeoutTask = Task.Delay(100);
169+
var completedTask = await Task.WhenAny(messageShownTcs.Task, timeoutTask).DefaultTimeout();
170+
171+
// When suppressed, no message should be displayed
172+
Assert.True(completedTask != messageShownTcs.Task, "No message bar should be displayed when suppressed by configuration.");
173+
Assert.Empty(messageService.AllMessages);
174+
}
175+
else
176+
{
177+
// When not suppressed, message should be displayed since it wasn't dismissed
178+
await messageShownTcs.Task.DefaultTimeout();
179+
Assert.NotEmpty(messageService.AllMessages);
180+
}
181+
}
182+
183+
private void SetupMainLayoutServices(TestLocalStorage? localStorage = null, MessageService? messageService = null, bool suppressUnsecuredMessage = false)
129184
{
130185
Services.AddLocalization();
131186
Services.AddOptions();
@@ -144,7 +199,11 @@ private void SetupMainLayoutServices(TestLocalStorage? localStorage = null, Mess
144199
Services.AddSingleton<DashboardTelemetryService>();
145200
Services.AddSingleton<IDashboardTelemetrySender, TestDashboardTelemetrySender>();
146201
Services.AddSingleton<ComponentTelemetryContextProvider>();
147-
Services.Configure<DashboardOptions>(o => o.Otlp.AuthMode = OtlpAuthMode.Unsecured);
202+
Services.Configure<DashboardOptions>(o =>
203+
{
204+
o.Otlp.AuthMode = OtlpAuthMode.Unsecured;
205+
o.Otlp.SuppressUnsecuredTelemetryMessage = suppressUnsecuredMessage;
206+
});
148207

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

0 commit comments

Comments
 (0)