refactor(adminui): B2-WP8 DriverTypeNames rewire + retire routed driver-authoring flow

Part 1 — rewire the three DriverType dispatch maps from hand-authored string
literals to the canonical DriverTypeNames constants (single source of truth):
- TagConfigEditorMap, TagConfigValidator (AdminUI)
- EquipmentTagConfigInspector (ControlPlane)
This FIXES the live drift ("TwinCat"->TwinCAT, "Focas"->FOCAS). The maps are
OrdinalIgnoreCase so behavior is identical; the value is no future drift.
Calculation is not added to the inspector (no CalculationTagDefinitionFactory.Inspect).
Added TagConfigDriverTypeNameGuardTests pinning the maps to the constants.

Part 2 — retire the routed /clusters/{id}/drivers driver-authoring flow (/raw
Waves A-C now cover driver/device/tag authoring end-to-end):
- Deleted DriverTypePicker, DriverEditRouter, the 8 *DriverPage shells, and the
  ClusterDrivers list page.
- Removed the "Drivers" tab from ClusterNav. Routes /clusters/{id}/drivers* now
  simply do not exist -> clean 404.
- The Forms/DeviceForms bodies, shared driver sections, and address pickers used
  by the /raw modals are untouched.

Part 3 — updated PageAuthorizationGuardTests census: removed the 10 deleted
ConfigEditor pages + ClusterDrivers, updated group-count comments (ConfigEditor
21->11, AuthenticatedRead 17->16). Removed the now-dead Clusters.Drivers usings
from the 4 *DriverPageFormSerializationTests + the guard test.

Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
This commit is contained in:
Joseph Doherty
2026-07-16 04:39:22 -04:00
parent dc80a3b4f6
commit 96471d2345
21 changed files with 103 additions and 1927 deletions
@@ -6,7 +6,6 @@ using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Uns;
using ZB.MOM.WW.OtOpcUa.Security.Auth;
@@ -29,7 +28,10 @@ public class PageAuthorizationGuardTests
/// </summary>
private static readonly IReadOnlyDictionary<Type, string> ExpectedPolicy = new Dictionary<Type, string>
{
// ── ConfigEditor (21): the config-authoring surface (incl. live ResilienceConfig) ──
// ── ConfigEditor (11): the config-authoring surface (incl. live ResilienceConfig).
// The routed /clusters/{id}/drivers* driver-authoring pages (DriverTypePicker,
// DriverEditRouter, + the 8 per-type *DriverPage shells) retired in B2-WP8 — driver
// authoring now flows through the /raw tree, so those routes no longer exist. ──
[typeof(GlobalUns)] = AdminUiPolicies.ConfigEditor,
[typeof(global::ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Raw.GlobalRaw)] = AdminUiPolicies.ConfigEditor,
[typeof(EquipmentPage)] = AdminUiPolicies.ConfigEditor,
@@ -38,16 +40,6 @@ public class PageAuthorizationGuardTests
[typeof(NodeEdit)] = AdminUiPolicies.ConfigEditor,
[typeof(NamespaceEdit)] = AdminUiPolicies.ConfigEditor,
[typeof(AclEdit)] = AdminUiPolicies.ConfigEditor,
[typeof(DriverTypePicker)] = AdminUiPolicies.ConfigEditor,
[typeof(DriverEditRouter)] = AdminUiPolicies.ConfigEditor,
[typeof(ModbusDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(S7DriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(AbCipDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(AbLegacyDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(TwinCATDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(FocasDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(OpcUaClientDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(GalaxyDriverPage)] = AdminUiPolicies.ConfigEditor,
[typeof(Deployments)] = AdminUiPolicies.ConfigEditor,
[typeof(Scripts)] = AdminUiPolicies.ConfigEditor,
[typeof(ScriptEdit)] = AdminUiPolicies.ConfigEditor,
@@ -55,7 +47,7 @@ public class PageAuthorizationGuardTests
// ── FleetAdmin (1) ──
[typeof(RoleGrants)] = AdminUiPolicies.FleetAdmin,
// ── AuthenticatedRead (17): dashboards, lists, live tails (read-only) + the dev ContextMenu demo ──
// ── AuthenticatedRead (16): dashboards, lists, live tails (read-only) + the dev ContextMenu demo ──
[typeof(Home)] = AdminUiPolicies.AuthenticatedRead,
[typeof(global::ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Dev.ContextMenuDemo)] = AdminUiPolicies.AuthenticatedRead,
[typeof(Fleet)] = AdminUiPolicies.AuthenticatedRead,
@@ -71,7 +63,6 @@ public class PageAuthorizationGuardTests
[typeof(ClusterAudit)] = AdminUiPolicies.AuthenticatedRead,
[typeof(ClusterAcls)] = AdminUiPolicies.AuthenticatedRead,
[typeof(ClusterNamespaces)] = AdminUiPolicies.AuthenticatedRead,
[typeof(ClusterDrivers)] = AdminUiPolicies.AuthenticatedRead,
[typeof(ClusterRedundancy)] = AdminUiPolicies.AuthenticatedRead,
};
@@ -2,7 +2,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Forms;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;
@@ -2,7 +2,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests;
@@ -4,7 +4,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Forms;
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
@@ -2,7 +2,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Forms;
using ZB.MOM.WW.OtOpcUa.Driver.S7;
@@ -0,0 +1,65 @@
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
/// <summary>
/// B2-WP8 drift guard: asserts the tag-config dispatch maps (<see cref="TagConfigEditorMap"/> and
/// <see cref="TagConfigValidator"/>) are keyed by the canonical <see cref="DriverTypeNames"/>
/// constants. Because the maps are <c>OrdinalIgnoreCase</c>, a drifted literal (e.g. the historical
/// <c>"TwinCat"</c>/<c>"Focas"</c>) still matched case-insensitively — so a plain lookup test cannot
/// catch drift. These tests resolve <em>through the constants themselves</em>, so if a map key ever
/// stops matching its <see cref="DriverTypeNames"/> constant (a rename on either side) the dispatch
/// goes dark and the test fails.
/// </summary>
public sealed class TagConfigDriverTypeNameGuardTests
{
// Every DriverTypeNames constant that has a typed editor registered.
public static TheoryData<string> EditorMappedDriverTypes() =>
[
DriverTypeNames.Modbus,
DriverTypeNames.S7,
DriverTypeNames.AbCip,
DriverTypeNames.AbLegacy,
DriverTypeNames.TwinCAT,
DriverTypeNames.FOCAS,
DriverTypeNames.OpcUaClient,
DriverTypeNames.Calculation,
];
[Theory]
[MemberData(nameof(EditorMappedDriverTypes))]
public void EditorMap_resolves_every_mapped_DriverTypeNames_constant(string driverType)
=> TagConfigEditorMap.Resolve(driverType).ShouldNotBeNull(
$"TagConfigEditorMap must dispatch DriverTypeNames.{driverType} — key/constant drift.");
// Drivers whose typed model has a required field: a REGISTERED validator returns a non-null error for
// the empty config, whereas an unmapped/drifted key falls through to the null "unmapped ⇒ valid" branch.
// So a non-null result here unambiguously proves the constant is still keyed in the validator map.
public static TheoryData<string> ValidatorRequiredFieldDriverTypes() =>
[
DriverTypeNames.S7,
DriverTypeNames.AbCip,
DriverTypeNames.AbLegacy,
DriverTypeNames.TwinCAT,
DriverTypeNames.FOCAS,
DriverTypeNames.OpcUaClient,
];
[Theory]
[MemberData(nameof(ValidatorRequiredFieldDriverTypes))]
public void Validator_dispatches_every_required_field_DriverTypeNames_constant(string driverType)
=> TagConfigValidator.Validate(driverType, "{}").ShouldNotBeNullOrEmpty(
$"TagConfigValidator must dispatch DriverTypeNames.{driverType} — key/constant drift.");
// The drift the WP8 rewire fixed: these two constants differ in case from the retired literals
// ("TwinCat"/"Focas"). Pin them explicitly.
[Fact]
public void TwinCAT_and_FOCAS_constants_resolve_their_editors()
{
TagConfigEditorMap.Resolve(DriverTypeNames.TwinCAT).ShouldNotBeNull();
TagConfigEditorMap.Resolve(DriverTypeNames.FOCAS).ShouldNotBeNull();
}
}