fix(management): case-variant instance override names are rejected with a did-you-mean hint (ScadaBridge#105)
Verified the ManagementActor bulk attribute override and single/bulk native alarm source override paths match names ordinally and already reject a case-variant name (nothing stored); the error now names the single case-insensitive match. Tests pin the rejection. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -1140,6 +1140,18 @@ public class ManagementActor : ReceiveActor
|
||||
: throw new ManagementCommandException(result.Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ScadaBridge#105: override names are matched exactly (the flattener is ordinal), so a name
|
||||
/// that differs from a member only by case is rejected, never stored. When exactly one
|
||||
/// member matches case-insensitively, name it in the error so the caller can correct it;
|
||||
/// an ambiguous match gets no hint.
|
||||
/// </summary>
|
||||
internal static string CaseMismatchHint(string name, IEnumerable<string> candidates)
|
||||
{
|
||||
var matches = candidates.Where(c => string.Equals(c, name, StringComparison.OrdinalIgnoreCase)).Distinct(StringComparer.Ordinal).Take(2).ToList();
|
||||
return matches.Count == 1 ? $" (names are case-sensitive; did you mean '{matches[0]}'?)" : string.Empty;
|
||||
}
|
||||
|
||||
private static async Task<object?> HandleSetInstanceOverrides(IServiceProvider sp, SetInstanceOverridesCommand cmd, AuthenticatedUser user)
|
||||
{
|
||||
await EnforceSiteScopeForInstance(sp, user, cmd.InstanceId);
|
||||
@@ -1163,8 +1175,8 @@ public class ManagementActor : ReceiveActor
|
||||
{
|
||||
if (!attrsByName.TryGetValue(attrName, out var templateAttr))
|
||||
throw new ManagementCommandException(
|
||||
$"Attribute '{attrName}' does not exist in template {instance.TemplateId}. " +
|
||||
"No overrides were applied.");
|
||||
$"Attribute '{attrName}' does not exist in template {instance.TemplateId}" +
|
||||
CaseMismatchHint(attrName, attrsByName.Keys) + ". No overrides were applied.");
|
||||
if (templateAttr.IsLocked)
|
||||
throw new ManagementCommandException(
|
||||
$"Attribute '{attrName}' is locked and cannot be overridden. No overrides were applied.");
|
||||
@@ -1288,7 +1300,9 @@ public class ManagementActor : ReceiveActor
|
||||
.FirstOrDefault(s => string.Equals(s.CanonicalName, cmd.SourceCanonicalName, StringComparison.Ordinal));
|
||||
if (resolvedSource == null)
|
||||
throw new ManagementCommandException(
|
||||
$"Native alarm source '{cmd.SourceCanonicalName}' does not resolve for this instance and cannot be overridden.");
|
||||
$"Native alarm source '{cmd.SourceCanonicalName}' does not resolve for this instance and cannot be overridden" +
|
||||
CaseMismatchHint(cmd.SourceCanonicalName,
|
||||
flattenResult.Value.Configuration.NativeAlarmSources.Select(x => x.CanonicalName)) + ".");
|
||||
if (resolvedSource.IsLocked)
|
||||
throw new ManagementCommandException(
|
||||
$"Native alarm source '{cmd.SourceCanonicalName}' is locked at the template level and cannot be overridden.");
|
||||
@@ -1349,7 +1363,8 @@ public class ManagementActor : ReceiveActor
|
||||
if (!resolvedByName.TryGetValue(entry.SourceCanonicalName, out var resolvedSource))
|
||||
throw new ManagementCommandException(
|
||||
$"Native alarm source '{entry.SourceCanonicalName}' does not resolve for this instance " +
|
||||
"and cannot be overridden. No overrides were applied.");
|
||||
"and cannot be overridden" + CaseMismatchHint(entry.SourceCanonicalName, resolvedByName.Keys) +
|
||||
". No overrides were applied.");
|
||||
if (resolvedSource.IsLocked)
|
||||
throw new ManagementCommandException(
|
||||
$"Native alarm source '{entry.SourceCanonicalName}' is locked at the template level and " +
|
||||
|
||||
Reference in New Issue
Block a user