feat(deployment): DeployToSiteAsync for single-site artifact deployment
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Text.Json;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment;
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Artifacts;
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Artifacts;
|
||||||
@@ -232,6 +233,38 @@ public class ArtifactDeploymentService
|
|||||||
if (sites.Count == 0)
|
if (sites.Count == 0)
|
||||||
return Result<ArtifactDeploymentSummary>.Failure("No sites configured.");
|
return Result<ArtifactDeploymentSummary>.Failure("No sites configured.");
|
||||||
|
|
||||||
|
return await DeployCoreAsync(sites, user, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deploys system-wide artifacts to a single site (management-API --site-id path).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="siteId">The database identifier of the target site.</param>
|
||||||
|
/// <param name="user">The user initiating the deployment.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>A summary of the artifact deployment result for the single site.</returns>
|
||||||
|
public async Task<Result<ArtifactDeploymentSummary>> DeployToSiteAsync(
|
||||||
|
int siteId,
|
||||||
|
string user,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var site = await _siteRepo.GetSiteByIdAsync(siteId, cancellationToken);
|
||||||
|
if (site is null)
|
||||||
|
return Result<ArtifactDeploymentSummary>.Failure($"Site with ID {siteId} not found.");
|
||||||
|
|
||||||
|
return await DeployCoreAsync([site], user, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Core deployment path shared by the all-sites and single-site entry points.
|
||||||
|
/// Builds a per-site command with that site's data connections and deploys to
|
||||||
|
/// every supplied site under one logical deployment id.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<Result<ArtifactDeploymentSummary>> DeployCoreAsync(
|
||||||
|
IReadOnlyList<Site> sites,
|
||||||
|
string user,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
var deploymentId = Guid.NewGuid().ToString("N");
|
var deploymentId = Guid.NewGuid().ToString("N");
|
||||||
var perSiteResults = new Dictionary<string, SiteArtifactResult>();
|
var perSiteResults = new Dictionary<string, SiteArtifactResult>();
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,41 @@ public class ArtifactDeploymentServiceTests : TestKit
|
|||||||
Arg.Any<string>(), "retry-site", Arg.Any<object>(), Arg.Any<CancellationToken>());
|
Arg.Any<string>(), "retry-site", Arg.Any<object>(), Arg.Any<CancellationToken>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── PLAN-07 Task 4: single-site artifact deployment ──
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeployToSiteAsync_DeploysOnlyToTargetSite()
|
||||||
|
{
|
||||||
|
// Two sites exist; deploying to site 1 must build/send exactly one per-site
|
||||||
|
// command (for SITE1) and never touch SITE2's per-site data.
|
||||||
|
var site1 = new Site("Site One", "SITE1") { Id = 1 };
|
||||||
|
_siteRepo.GetSiteByIdAsync(1, Arg.Any<CancellationToken>()).Returns(site1);
|
||||||
|
var recorder = new ArtifactProbeRecorder();
|
||||||
|
var probe = Sys.ActorOf(Props.Create(() => new ArtifactProbeActor(recorder)));
|
||||||
|
var service = CreateServiceWithCommActor(probe);
|
||||||
|
|
||||||
|
var result = await service.DeployToSiteAsync(1, "tester");
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.Single(result.Value.SiteResults);
|
||||||
|
Assert.Equal("SITE1", result.Value.SiteResults[0].SiteId);
|
||||||
|
Assert.Single(recorder.Received);
|
||||||
|
await _siteRepo.Received(1).GetDataConnectionsBySiteIdAsync(1, Arg.Any<CancellationToken>());
|
||||||
|
await _siteRepo.DidNotReceive().GetDataConnectionsBySiteIdAsync(2, Arg.Any<CancellationToken>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeployToSiteAsync_UnknownSite_ReturnsFailure()
|
||||||
|
{
|
||||||
|
_siteRepo.GetSiteByIdAsync(999, Arg.Any<CancellationToken>()).Returns((Site?)null);
|
||||||
|
var service = CreateService();
|
||||||
|
|
||||||
|
var result = await service.DeployToSiteAsync(999, "tester");
|
||||||
|
|
||||||
|
Assert.True(result.IsFailure);
|
||||||
|
Assert.Contains("999", result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
private ArtifactDeploymentService CreateService()
|
private ArtifactDeploymentService CreateService()
|
||||||
{
|
{
|
||||||
var comms = new CommunicationService(
|
var comms = new CommunicationService(
|
||||||
|
|||||||
Reference in New Issue
Block a user