using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using FastEndpoints; using FrameworkBLL.Promotion; using GB5Shared.Deployment; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.DTO.Promotion; using GB5Shared.FastEndPoint; using Microsoft.Extensions.Configuration; using static FrameworkDAL.Constant.Constant; namespace FrameworkSL.Endpoints.Promotion { // ============================================================ // ExportAndUploadToExchange — Tier 2 (Exchange) package dispatch. Same // build-and-encrypt logic as ExportPromotionPackage, but instead of // streaming the file back to the caller for manual carry, uploads it // straight to the separate PromotionExchange service so the destination // can pick it up on its own schedule (PromotionExchangeIngestQuartzJob), // without either side needing to be online at the same moment. // ============================================================ public class ExportAndUploadToExchange : BaseEndpoint< ExportAndUploadToExchange.ExportAndUploadToExchangeParameters, ResponseStandardDTO> { private readonly IPromotionBLL _PromotionBLL; private readonly IPromotionExchangeClient _ExchangeClient; private readonly IConfiguration _Configuration; private readonly IGB5Environment _GB5Environment; public ExportAndUploadToExchange( IPromotionBLL promotionBLL, IPromotionExchangeClient exchangeClient, IConfiguration configuration, IGB5Environment gb5Environment) { _PromotionBLL = promotionBLL; _ExchangeClient = exchangeClient; _Configuration = configuration; _GB5Environment = gb5Environment; } public override void Configure() { Post("/Promotion/ExportAndUploadToExchange"); AllowAnonymous(); } public record ExportAndUploadToExchangeParameters( [property: FromHeader] string Login, [property: FromBody] PromotionExportRequestDTO Request ); protected override async Task> ExecuteAsync( ExportAndUploadToExchangeParameters req, LoginDTO LoginDTO, CancellationToken ct) { try { if (req.Request.Items.Count == 0) throw new ArgumentException("At least one item is required to export a package."); if (string.IsNullOrWhiteSpace(req.Request.DestinationEnvironmentCode)) throw new ArgumentException("DestinationEnvironmentCode is required."); // Defaults to this deployment's own tier code (Part A, GB5Shared.Deployment) — // the appsettings key is now an optional override only, for the rare case of // re-running a historical promotion under a different origin code. No longer // throws if unset (previously shipped as the literal "CHANGE_ME_PER_ENVIRONMENT" // placeholder, a "ships broken until hand-edited" footgun). var configuredOverride = _Configuration["Promotion:OriginEnvironmentCode"]; var originEnvironmentCode = string.IsNullOrWhiteSpace(configuredOverride) ? _GB5Environment.GopEnvironmentCode : configuredOverride; var bundles = new List(req.Request.Items.Count); foreach (var item in req.Request.Items) { var bundle = await _PromotionBLL.BuildExportBundleAsync( item.EntityTypeCode, item.OriginEntityId, LoginDTO, ct).ConfigureAwait(false); bundle.OriginEnvironmentCode = originEnvironmentCode; bundles.Add(bundle); } var packageBytes = await _PromotionBLL.BuildPackageAsync( bundles, req.Request.DestinationEnvironmentCode, LoginDTO, ct).ConfigureAwait(false); var exchangePackageId = await _ExchangeClient.UploadAsync( packageBytes, req.Request.DestinationEnvironmentCode, ct).ConfigureAwait(false); if (exchangePackageId is null) throw new InvalidOperationException( "The package was built successfully but could not be uploaded to the Exchange " + "(unreachable or rejected) — use ExportPromotionPackage for manual carry instead."); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse( new { ExchangePackageId = exchangePackageId.Value }, CacheKeyLevel.NOT_REQUIRED, LoginDTO); } catch (Exception ex) { return await GB5Shared.ResponseStandard.Response.CreateExceptionError( ex, CacheKeyLevel.NOT_REQUIRED, LoginDTO, ex.Message, 500); } } } }