From 25f768f37994de45fba49bac590777738217a15b Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 26 Jun 2026 12:42:46 -0400 Subject: [PATCH] feat(deploy): RefreshDeploymentAsync send method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CommunicationService.RefreshDeploymentAsync — the typed send method for the small notify-and-fetch wire message (RefreshDeploymentCommand). Mirrors DeployInstanceAsync exactly: SiteEnvelope + Ask bounded by DeploymentTimeout. CentralCommunicationActor needs no change (HandleSiteEnvelope is fully generic — all SiteEnvelope messages forward to /user/site-communication without a per-type switch). Adds a parallel routing test asserting the envelope reaches the site ClusterClient. --- .../CommunicationService.cs | 21 +++++++++++++++++++ .../CentralCommunicationActorTests.cs | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/ZB.MOM.WW.ScadaBridge.Communication/CommunicationService.cs b/src/ZB.MOM.WW.ScadaBridge.Communication/CommunicationService.cs index f502e6a2..a7cdfa41 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Communication/CommunicationService.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Communication/CommunicationService.cs @@ -134,6 +134,27 @@ public class CommunicationService envelope, _options.DeploymentTimeout, cancellationToken); } + /// + /// Sends a small "refresh deployment" notify to a site (notify-and-fetch). + /// Replaces on the wire: the site fetches the + /// config over HTTP rather than receiving it inline. Reply is the existing + /// DeploymentStatusResponse, bounded by the deployment timeout. + /// + /// The target site identifier. + /// The refresh-deployment notify. + /// Cancellation token. + /// The deployment status response. + public async Task RefreshDeploymentAsync( + string siteId, RefreshDeploymentCommand command, CancellationToken cancellationToken = default) + { + _logger.LogInformation( + "Sending RefreshDeploymentCommand to site {SiteId}, instance={Instance}, deploymentId={DeploymentId}", + siteId, command.InstanceUniqueName, command.DeploymentId); + var envelope = new SiteEnvelope(siteId, command); + return await GetActor().Ask( + envelope, _options.DeploymentTimeout, cancellationToken); + } + /// /// DeploymentManager-006: queries a site for the currently-applied deployment /// identity of a single instance. Used by the Deployment Manager before a diff --git a/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/CentralCommunicationActorTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/CentralCommunicationActorTests.cs index 90b557fe..a7dae8b0 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/CentralCommunicationActorTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/CentralCommunicationActorTests.cs @@ -101,6 +101,25 @@ public class CentralCommunicationActorTests : TestKit Assert.Equal("dep1", ((DeployInstanceCommand)msg.Message).DeploymentId); } + [Fact] + public void ClusterClientRouting_RefreshDeploymentCommand_RoutesToSite() + { + var site = CreateSite("site1", "akka.tcp://scadabridge@host:8082"); + var (actor, _, siteProbes) = CreateActorWithMockRepo(new[] { site }); + + Thread.Sleep(1000); + + var command = new RefreshDeploymentCommand( + "dep1", "inst1", "rev1", "admin", DateTimeOffset.UtcNow, + "https://central:9000", "tok1"); + actor.Tell(new SiteEnvelope("site1", command)); + + var msg = siteProbes["site1"].ExpectMsg(); + Assert.Equal("/user/site-communication", msg.Path); + Assert.IsType(msg.Message); + Assert.Equal("dep1", ((RefreshDeploymentCommand)msg.Message).DeploymentId); + } + [Fact] public void UnconfiguredSite_MessageIsDropped() {