fix(galaxy): complete PR 7.2 rename — use canonical GalaxyMxGateway driver type
v2-ci / build (push) Failing after 48s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

The driver/factory/seed use 'GalaxyMxGateway' (legacy 'Galaxy' was retired),
but the AdminUI editor router, GalaxyDriverPage, address picker, identity
dropdown, the Galaxy browser/probe, and DraftValidator still keyed on 'Galaxy'.
Result: the seeded GalaxyMxGateway driver couldn't be edited ('no editor
registered'), UI-created Galaxy drivers wrote a type with no factory, and a
SystemPlatform-bound GalaxyMxGateway driver failed publish validation.
Align all stragglers to GalaxyMxGateway (+ failing-test-first DraftValidator
coverage). ShouldStub's 'Galaxy' legacy safety-net left intact.
This commit is contained in:
Joseph Doherty
2026-05-29 12:31:55 -04:00
parent de666b24c3
commit 32d7fd7cc9
9 changed files with 47 additions and 15 deletions
@@ -172,8 +172,8 @@ public static class DraftValidator
var compat = ns.Kind switch
{
NamespaceKind.SystemPlatform => di.DriverType == "Galaxy",
NamespaceKind.Equipment => di.DriverType != "Galaxy",
NamespaceKind.SystemPlatform => di.DriverType == "GalaxyMxGateway",
NamespaceKind.Equipment => di.DriverType != "GalaxyMxGateway",
_ => true,
};
@@ -42,8 +42,10 @@ public sealed class GalaxyDriverBrowser : IDriverBrowser
_logger = logger ?? NullLogger<GalaxyDriverBrowser>.Instance;
}
/// <summary>Driver type key — matches the AdminUI's persisted "Galaxy" value.</summary>
public string DriverType => "Galaxy";
/// <summary>Driver type key — matches the AdminUI's persisted "GalaxyMxGateway" value.</summary>
// Hardcoded literal: this project references Driver.Galaxy.Contracts, not Driver.Galaxy,
// so GalaxyDriverFactoryExtensions.DriverTypeName isn't available here.
public string DriverType => "GalaxyMxGateway";
/// <summary>
/// Deserializes a <see cref="GalaxyDriverOptions"/> blob, opens a transient
@@ -25,7 +25,7 @@ public sealed class GalaxyDriverProbe : IDriverProbe
/// <inheritdoc />
// Matches DriverInstance.DriverType strings set by the AdminUI's GalaxyDriverPage.
public string DriverType => "Galaxy";
public string DriverType => GalaxyDriverFactoryExtensions.DriverTypeName;
/// <inheritdoc />
public async Task<DriverProbeResult> ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct)
@@ -59,7 +59,7 @@ else
["TwinCat"] = typeof(TwinCATDriverPage),
["Focas"] = typeof(FocasDriverPage),
["OpcUaClient"] = typeof(OpcUaClientDriverPage),
["Galaxy"] = typeof(GalaxyDriverPage),
["GalaxyMxGateway"] = typeof(GalaxyDriverPage),
["Historian.Wonderware"] = typeof(HistorianWonderwareDriverPage),
};
@@ -208,7 +208,7 @@ else
[Parameter] public string ClusterId { get; set; } = "";
[Parameter] public string? DriverInstanceId { get; set; }
private const string DriverTypeKey = "Galaxy";
private const string DriverTypeKey = "GalaxyMxGateway";
private bool IsNew => string.IsNullOrEmpty(DriverInstanceId);
@@ -35,7 +35,7 @@
<option value="TwinCat">TwinCat</option>
<option value="Focas">Focas</option>
<option value="OpcUaClient">OpcUaClient</option>
<option value="Galaxy">Galaxy</option>
<option value="GalaxyMxGateway">Galaxy</option>
<option value="Historian.Wonderware">Historian.Wonderware</option>
</InputSelect>
<div class="form-text">Cannot be changed after creation — drives the actor type that owns this instance.</div>
@@ -126,7 +126,7 @@
try
{
var json = GetConfigJson() ?? "{}";
var result = await BrowserService.OpenAsync("Galaxy", json, default);
var result = await BrowserService.OpenAsync("GalaxyMxGateway", json, default);
if (result.Ok) _token = result.Token;
else _openError = result.Message;
}
@@ -122,15 +122,45 @@ public sealed class DraftValidatorTests
DraftValidator.Validate(draft).ShouldContain(e => e.Code == "EquipmentIdNotDerived");
}
/// <summary>Verifies that Galaxy driver cannot be placed in Equipment namespace.</summary>
/// <summary>Verifies that the canonical Galaxy driver type (GalaxyMxGateway, per PR 7.2 —
/// it was "Galaxy" pre-PR-7.2) is allowed in a SystemPlatform namespace, i.e. produces no
/// kind-mismatch error.</summary>
[Fact]
public void Galaxy_driver_in_Equipment_namespace_is_rejected()
public void GalaxyMxGateway_driver_in_SystemPlatform_namespace_is_allowed()
{
var draft = new DraftSnapshot
{
GenerationId = 1, ClusterId = "c",
Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c", NamespaceUri = "urn:x", Kind = NamespaceKind.SystemPlatform }],
DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c", NamespaceId = "ns-1", Name = "drv", DriverType = "GalaxyMxGateway", DriverConfig = "{}" }],
};
DraftValidator.Validate(draft).ShouldNotContain(e => e.Code == "DriverNamespaceKindMismatch");
}
/// <summary>Verifies that the canonical Galaxy driver type cannot be placed in an Equipment namespace.</summary>
[Fact]
public void GalaxyMxGateway_driver_in_Equipment_namespace_is_rejected()
{
var draft = new DraftSnapshot
{
GenerationId = 1, ClusterId = "c",
Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c", NamespaceUri = "urn:x", Kind = NamespaceKind.Equipment }],
DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c", NamespaceId = "ns-1", Name = "drv", DriverType = "Galaxy", DriverConfig = "{}" }],
DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c", NamespaceId = "ns-1", Name = "drv", DriverType = "GalaxyMxGateway", DriverConfig = "{}" }],
};
DraftValidator.Validate(draft).ShouldContain(e => e.Code == "DriverNamespaceKindMismatch");
}
/// <summary>Verifies that a non-Galaxy driver cannot be placed in a SystemPlatform namespace.</summary>
[Fact]
public void NonGalaxy_driver_in_SystemPlatform_namespace_is_rejected()
{
var draft = new DraftSnapshot
{
GenerationId = 1, ClusterId = "c",
Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c", NamespaceUri = "urn:x", Kind = NamespaceKind.SystemPlatform }],
DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c", NamespaceId = "ns-1", Name = "drv", DriverType = "ModbusTcp", DriverConfig = "{}" }],
};
DraftValidator.Validate(draft).ShouldContain(e => e.Code == "DriverNamespaceKindMismatch");
@@ -145,7 +175,7 @@ public sealed class DraftValidatorTests
{
GenerationId = 1, ClusterId = "c-A",
Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c-B", NamespaceUri = "urn:x", Kind = NamespaceKind.Equipment }],
DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c-A", NamespaceId = "ns-1", Name = "drv", DriverType = "Galaxy", DriverConfig = "{}" }],
DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c-A", NamespaceId = "ns-1", Name = "drv", DriverType = "GalaxyMxGateway", DriverConfig = "{}" }],
Equipment = [new Equipment { EquipmentUuid = uuid, EquipmentId = "EQ-wrong", Name = "BAD NAME", DriverInstanceId = "d-1", UnsLineId = "line-a", MachineCode = "m" }],
};
@@ -16,10 +16,10 @@ public sealed class GalaxyDriverBrowserTests
{
private readonly GalaxyDriverBrowser _sut = new();
/// <summary>The DriverType key must match the AdminUI's persisted "Galaxy" value
/// <summary>The DriverType key must match the AdminUI's persisted "GalaxyMxGateway" value
/// so the factory wire-up picks the right browser implementation.</summary>
[Fact]
public void DriverType_is_Galaxy() => _sut.DriverType.ShouldBe("Galaxy");
public void DriverType_is_GalaxyMxGateway() => _sut.DriverType.ShouldBe("GalaxyMxGateway");
/// <summary>An empty Gateway.Endpoint must fail fast with a clear, endpoint-mentioning
/// message rather than surfacing a downstream gRPC URI parse error.</summary>