fix(dashboard): keep the '-' placeholder on failed /browse reads instead of '[redacted]'
This commit is contained in:
@@ -53,9 +53,12 @@ public sealed class DashboardLiveDataService : IDashboardLiveDataService, IAsync
|
||||
/// <summary>
|
||||
/// <c>MxGateway:Dashboard:ShowTagValues</c>. False (the default)
|
||||
/// substitutes <see cref="DashboardTagValue.RedactedValueText"/> for every
|
||||
/// value this service hands the Browse panel; quality, data type, source
|
||||
/// timestamp, and any error still describe the real read, so the panel
|
||||
/// remains a diagnostic surface without being a value-disclosure one.
|
||||
/// successfully read value this service hands the Browse panel; quality,
|
||||
/// data type, source timestamp, and any error still describe the real
|
||||
/// read, so the panel remains a diagnostic surface without being a
|
||||
/// value-disclosure one. Failed reads keep their existing "-" placeholder:
|
||||
/// there was no value to suppress, so claiming one was withheld would
|
||||
/// misreport the failure.
|
||||
/// </summary>
|
||||
private readonly bool _showTagValues;
|
||||
|
||||
@@ -124,9 +127,17 @@ public sealed class DashboardLiveDataService : IDashboardLiveDataService, IAsync
|
||||
// The only place the /browse live-value gate is evaluated: the page
|
||||
// renders whatever ValueText it is handed, so a second check in the
|
||||
// view could only ever disagree with this one.
|
||||
//
|
||||
// A failed read is left alone: it has no value to suppress, and its
|
||||
// ValueText is already the "-" placeholder. Substituting "[redacted]"
|
||||
// there would tell the operator a value was withheld when the read
|
||||
// never produced one — the Error column says why it failed, and the
|
||||
// two must not contradict each other.
|
||||
DashboardTagValue[] values = results
|
||||
.Select(DashboardTagValue.FromBulkReadResult)
|
||||
.Select(value => _showTagValues ? value : value with { ValueText = DashboardTagValue.RedactedValueText })
|
||||
.Select(value => _showTagValues || !value.Ok
|
||||
? value
|
||||
: value with { ValueText = DashboardTagValue.RedactedValueText })
|
||||
.ToArray();
|
||||
return new DashboardLiveReadResult(values, null, session.SessionId, session.WorkerProcessId);
|
||||
}
|
||||
|
||||
@@ -237,6 +237,38 @@ public sealed class DashboardLiveDataServiceTests
|
||||
Assert.Equal("Double", value.DataType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies redaction is scoped to reads that actually produced a value: a
|
||||
/// failed read keeps the "-" placeholder rather than claiming a value was
|
||||
/// withheld, and its diagnostic still reaches the panel. Redacting it would
|
||||
/// contradict the error the same row displays.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task ReadAsync_WhenReadFailedAndShowTagValuesFalse_LeavesPlaceholderUnredacted()
|
||||
{
|
||||
RecordingWorkerClient worker = new()
|
||||
{
|
||||
ReadValue = new MxValue { DataType = MxDataType.Double, DoubleValue = 42.5 },
|
||||
};
|
||||
worker.FailReadFor.Add("Bad.PV");
|
||||
await using FakeSessionManager sessionManager = new(worker);
|
||||
await using DashboardLiveDataService service = CreateService(sessionManager, showTagValues: false);
|
||||
|
||||
DashboardLiveReadResult result = await service.ReadAsync(
|
||||
["Bad.PV", "Tank_001.PV"],
|
||||
CancellationToken.None);
|
||||
|
||||
DashboardTagValue failed = result.Values.Single(value => value.TagAddress == "Bad.PV");
|
||||
Assert.False(failed.Ok);
|
||||
Assert.Equal("-", failed.ValueText);
|
||||
Assert.Equal("Simulated read failure.", failed.Error);
|
||||
Assert.False(failed.QualityGood);
|
||||
|
||||
// The successful row in the same read is still redacted.
|
||||
DashboardTagValue succeeded = result.Values.Single(value => value.TagAddress == "Tank_001.PV");
|
||||
Assert.Equal(DashboardTagValue.RedactedValueText, succeeded.ValueText);
|
||||
}
|
||||
|
||||
private static DashboardLiveDataService CreateService(
|
||||
ISessionManager sessionManager,
|
||||
bool showTagValues = false)
|
||||
@@ -387,6 +419,9 @@ public sealed class DashboardLiveDataServiceTests
|
||||
/// </summary>
|
||||
public MxValue? ReadValue { get; set; }
|
||||
|
||||
/// <summary>Gets the tag addresses whose bulk read comes back unsuccessful.</summary>
|
||||
public HashSet<string> FailReadFor { get; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>Gets the item handle bound for a previously subscribed tag.</summary>
|
||||
/// <param name="tagAddress">Tag address to look up.</param>
|
||||
/// <returns>The bound item handle.</returns>
|
||||
@@ -499,6 +534,20 @@ public sealed class DashboardLiveDataServiceTests
|
||||
BulkReadReply readReply = new();
|
||||
foreach (string tagAddress in tagAddresses)
|
||||
{
|
||||
if (FailReadFor.Contains(tagAddress))
|
||||
{
|
||||
readReply.Results.Add(new BulkReadResult
|
||||
{
|
||||
ServerHandle = RegisteredServerHandle,
|
||||
TagAddress = tagAddress,
|
||||
ItemHandle = _itemHandles.TryGetValue(tagAddress, out int failedHandle) ? failedHandle : 0,
|
||||
WasSuccessful = false,
|
||||
Quality = 0,
|
||||
ErrorMessage = "Simulated read failure.",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
BulkReadResult readResult = new()
|
||||
{
|
||||
ServerHandle = RegisteredServerHandle,
|
||||
|
||||
Reference in New Issue
Block a user