using System.Text.Json; using Dapr; using EntitlementBLL.Common; using EntitlementBLL.Interfaces; using EntitlementDAL.Enums; using EntitlementDAL.Interfaces; using GB5Shared.QueryExecutor; using Microsoft.AspNetCore.Mvc; namespace EntitlementSL.Subscriptions; // idms.golive.declared → SetStatusAsync(Active) + re-issue bundle + explicit GOLIVE_ACTIVATED // audit entry (SetStatusAsync itself only writes the generic status-change trace, so this // subscriber logs the GOLIVE_ACTIVATED marker itself via IEntitlementAuditBLL.LogActionAsync). // Also appends BUNDLE_ISSUED/GOLIVE_ACTIVATED to the client's MPROVISIONINGJOB.COMPLETEDSTEPS // as an informational trail — JOBSTATUS is deliberately left untouched, since bundle // issuance/activation is a downstream subscription-lifecycle event, not part of the job's // own automated scope (which already reached Completed at provisioning time). [ApiController] public class GoLiveDeclaredSubscriber : ControllerBase { private readonly ISubscriptionService _SubscriptionService; private readonly IEntitlementBundleService _BundleService; private readonly IEntitlementAuditBLL _AuditBLL; private readonly IProvisioningJobDAL _JobDAL; private readonly IQueryExecutor _QueryExecutor; private readonly IEntitlementLoginFactory _LoginFactory; private readonly ILogger _Logger; public GoLiveDeclaredSubscriber( ISubscriptionService subscriptionService, IEntitlementBundleService bundleService, IEntitlementAuditBLL auditBLL, IProvisioningJobDAL jobDAL, IQueryExecutor queryExecutor, IEntitlementLoginFactory loginFactory, ILogger logger) { _SubscriptionService = subscriptionService; _BundleService = bundleService; _AuditBLL = auditBLL; _JobDAL = jobDAL; _QueryExecutor = queryExecutor; _LoginFactory = loginFactory; _Logger = logger; } [Topic("goodbooks-pubsub", "idms.golive.declared")] [HttpPost("/lic/Subscriptions/GoLiveDeclared")] public async Task HandleAsync([FromBody] GoLiveDeclaredEvent evt, CancellationToken ct) { try { var sub = await _SubscriptionService.GetAsync(evt.ClientId, ct); await _SubscriptionService.SetStatusAsync(sub.SubscriptionId, SubscriptionStatusEnum.Active, ct); await _BundleService.IssueSignedBundleAsync(evt.ClientId, ct); var login = _LoginFactory.Create(evt.ClientId); await _AuditBLL.LogActionAsync(evt.ClientId, sub.SubscriptionId, "GOLIVE_ACTIVATED", $"EngagementRef={evt.EngagementRef}", login, ct); await AppendProvisioningJobStepsAsync(evt.ClientId, login, ct); _Logger.LogInformation("GoLive declared and bundle reissued for ClientId {ClientId} SubscriptionId {SubscriptionId}", evt.ClientId, sub.SubscriptionId); return Ok(); } catch (Exception ex) { _Logger.LogError(ex, "GoLiveDeclaredSubscriber failed for ClientId {ClientId}", evt.ClientId); return StatusCode(500, ex.Message); } } private async Task AppendProvisioningJobStepsAsync(int clientId, GB5Shared.DTO.Framework.Login.LoginDTO login, CancellationToken ct) { var job = await _JobDAL.GetByClientAsync(clientId, login, ct).ConfigureAwait(false); if (job is null) return; // no provisioning job on record for this client — nothing to append to var steps = (JsonSerializer.Deserialize(job.CompletedSteps) ?? Array.Empty()).ToList(); steps.Add("BUNDLE_ISSUED"); steps.Add("GOLIVE_ACTIVATED"); var tx = await _QueryExecutor.BeginTransactionAsync(login).ConfigureAwait(false); try { await _JobDAL.UpdateCompletedStepsAsync(job.ProvisioningJobId, JsonSerializer.Serialize(steps), login, tx, ct).ConfigureAwait(false); await _QueryExecutor.CommitAsync(tx).ConfigureAwait(false); } catch { await _QueryExecutor.RollbackAsync(tx).ConfigureAwait(false); throw; } } public record GoLiveDeclaredEvent(int ClientId, string EngagementRef); }