Skip to content

Commit c471f5b

Browse files
committed
Resource masking fixes (#6618)
1 parent 973a623 commit c471f5b

File tree

8 files changed

+460
-21
lines changed

8 files changed

+460
-21
lines changed

src/Aspire.Dashboard/Components/Controls/GridValue.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
IconEnd="@(IsMasked ? _unmaskIcon : _maskIcon)"
8282
Title="@(IsMasked ? Loc[nameof(ControlsStrings.GridValueMaskShowValue)] : Loc[nameof(ControlsStrings.GridValueMaskHideValue)])"
8383
OnClick="ToggleMaskStateAsync"
84-
aria-label="@(IsMasked ? Loc[nameof(ControlsStrings.GridValueMaskShowValue)] : Loc[nameof(ControlsStrings.GridValueMaskHideValue)])" />
84+
aria-label="@(IsMasked ? Loc[nameof(ControlsStrings.GridValueMaskShowValue)] : Loc[nameof(ControlsStrings.GridValueMaskHideValue)])"
85+
Class="grid-value-mask-button" />
8586
}
8687

8788
</div>

src/Aspire.Dashboard/Components/Controls/PropertyGrid.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
Style="width:100%"
1212
GenerateHeader="@GenerateHeader"
1313
GridTemplateColumns="@GridTemplateColumns"
14-
ShowHover="true">
14+
ShowHover="true"
15+
Class="@Class">
1516
<AspireTemplateColumn Title="@(NameColumnTitle ?? Loc[nameof(ControlsStrings.NameColumnHeader)])" Class="nameColumn" SortBy="@NameSort" Sortable="@IsNameSortable">
1617
<GridValue
1718
ValueDescription="@(NameColumnTitle ?? Loc[nameof(ControlsStrings.NameColumnHeader)])"

src/Aspire.Dashboard/Components/Controls/PropertyGrid.razor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ public partial class PropertyGrid<TItem> where TItem : IPropertyGridItem
140140
[Parameter]
141141
public GenerateHeaderOption GenerateHeader { get; set; } = GenerateHeaderOption.Sticky;
142142

143+
[Parameter]
144+
public string? Class { get; set; }
145+
143146
private async Task OnIsValueMaskedChanged(TItem item, bool isValueMasked)
144147
{
145148
item.IsValueMasked = isValueMasked;

src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@
2525
slot="end"/>
2626
}
2727

28-
<FluentIconSwitch @bind-Value="@_isMaskAllChecked"
28+
<FluentIconSwitch @bind-Value="@IsMaskAllChecked"
2929
Appearance="Appearance.Lightweight"
3030
CheckedTitle="@ControlStringsLoc[nameof(ControlsStrings.EnvironmentVariablesShowVariableValues)]"
3131
UncheckedTitle="@ControlStringsLoc[nameof(ControlsStrings.EnvironmentVariablesHideVariableValues)]"
3232
OnToggle="@OnMaskAllCheckedChanged"
3333
CheckedIcon="@(new Icons.Regular.Size16.Eye())"
3434
UncheckedIcon="@(new Icons.Regular.Size16.EyeOff())"
35+
class="mask-all-switch"
3536
slot="end" />
3637

3738
<FluentSearch Placeholder="@ControlStringsLoc[nameof(ControlsStrings.FilterPlaceholder)]"
@@ -82,7 +83,8 @@
8283
Items="@FilteredEnvironmentVariables"
8384
IsValueMaskedChanged="@OnValueMaskedChanged"
8485
HighlightText="@_filter"
85-
GridTemplateColumns="1fr 1.5fr" />
86+
GridTemplateColumns="1fr 1.5fr"
87+
Class="env-var-properties" />
8688
</FluentAccordionItem>
8789
@if (Resource.IsContainer())
8890
{

src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor.cs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using Aspire.Dashboard.Model;
56
using Google.Protobuf.WellKnownTypes;
67
using Microsoft.AspNetCore.Components;
@@ -23,28 +24,29 @@ public partial class ResourceDetails
2324

2425
private bool _showAll;
2526
private ResourceViewModel? _resource;
27+
private readonly HashSet<string> _unmaskedItemNames = new();
2628

27-
private IQueryable<EnvironmentVariableViewModel> FilteredEnvironmentVariables =>
29+
internal IQueryable<EnvironmentVariableViewModel> FilteredEnvironmentVariables =>
2830
Resource.Environment
2931
.Where(vm => (_showAll || vm.FromSpec) && ((IPropertyGridItem)vm).MatchesFilter(_filter))
3032
.AsQueryable();
3133

32-
private IQueryable<DisplayedEndpoint> FilteredEndpoints =>
34+
internal IQueryable<DisplayedEndpoint> FilteredEndpoints =>
3335
GetEndpoints()
3436
.Where(vm => vm.MatchesFilter(_filter))
3537
.AsQueryable();
3638

37-
private IQueryable<VolumeViewModel> FilteredVolumes =>
39+
internal IQueryable<VolumeViewModel> FilteredVolumes =>
3840
Resource.Volumes
3941
.Where(vm => vm.MatchesFilter(_filter))
4042
.AsQueryable();
4143

42-
private IQueryable<HealthReportViewModel> FilteredHealthReports =>
44+
internal IQueryable<HealthReportViewModel> FilteredHealthReports =>
4345
Resource.HealthReports
4446
.Where(vm => vm.MatchesFilter(_filter))
4547
.AsQueryable();
4648

47-
private IQueryable<ResourcePropertyViewModel> FilteredResourceProperties =>
49+
internal IQueryable<ResourcePropertyViewModel> FilteredResourceProperties =>
4850
GetResourceProperties(ordered: true)
4951
.Where(vm => (_showAll || vm.KnownProperty != null) && vm.MatchesFilter(_filter))
5052
.AsQueryable();
@@ -55,14 +57,27 @@ public partial class ResourceDetails
5557
private bool _isHealthChecksExpanded;
5658

5759
private string _filter = "";
58-
private bool _isMaskAllChecked = true;
60+
private bool? _isMaskAllChecked;
61+
62+
private bool IsMaskAllChecked
63+
{
64+
get => _isMaskAllChecked ?? false;
65+
set { _isMaskAllChecked = value; }
66+
}
5967

6068
private readonly GridSort<DisplayedEndpoint> _endpointValueSort = GridSort<DisplayedEndpoint>.ByAscending(vm => vm.Url ?? vm.Text);
6169

6270
protected override void OnParametersSet()
6371
{
6472
if (!ReferenceEquals(Resource, _resource))
6573
{
74+
// Reset masking when the resource changes.
75+
if (!string.Equals(Resource.Name, _resource?.Name, StringComparisons.ResourceName))
76+
{
77+
_isMaskAllChecked = true;
78+
_unmaskedItemNames.Clear();
79+
}
80+
6681
_resource = Resource;
6782

6883
// Collapse details sections when they have no data.
@@ -73,7 +88,14 @@ protected override void OnParametersSet()
7388

7489
foreach (var item in SensitiveGridItems)
7590
{
76-
item.IsValueMasked = _isMaskAllChecked;
91+
if (_isMaskAllChecked != null)
92+
{
93+
item.IsValueMasked = _isMaskAllChecked.Value;
94+
}
95+
else if (_unmaskedItemNames.Count > 0)
96+
{
97+
item.IsValueMasked = !_unmaskedItemNames.Contains(item.Name);
98+
}
7799
}
78100
}
79101
}
@@ -95,25 +117,37 @@ private IEnumerable<ResourcePropertyViewModel> GetResourceProperties(bool ordere
95117

96118
private void OnMaskAllCheckedChanged()
97119
{
120+
Debug.Assert(_isMaskAllChecked != null);
121+
122+
_unmaskedItemNames.Clear();
123+
98124
foreach (var vm in SensitiveGridItems)
99125
{
100-
vm.IsValueMasked = _isMaskAllChecked;
126+
vm.IsValueMasked = _isMaskAllChecked.Value;
101127
}
102128
}
103129

104-
private void OnValueMaskedChanged()
130+
private void OnValueMaskedChanged(IPropertyGridItem vm)
105131
{
106132
// Check the "Mask All" checkbox if all sensitive values are masked.
107-
108-
foreach (var item in SensitiveGridItems)
133+
var valueMaskedValues = SensitiveGridItems.Select(i => i.IsValueMasked).Distinct().ToList();
134+
if (valueMaskedValues.Count == 1)
135+
{
136+
_isMaskAllChecked = valueMaskedValues[0];
137+
_unmaskedItemNames.Clear();
138+
}
139+
else
109140
{
110-
if (!item.IsValueMasked)
141+
_isMaskAllChecked = null;
142+
143+
if (vm.IsValueMasked)
111144
{
112-
_isMaskAllChecked = false;
113-
return;
145+
_unmaskedItemNames.Remove(vm.Name);
146+
}
147+
else
148+
{
149+
_unmaskedItemNames.Add(vm.Name);
114150
}
115151
}
116-
117-
_isMaskAllChecked = true;
118152
}
119153
}

0 commit comments

Comments
 (0)