using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using FrameworkBLL.Promotion; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.GOP; using Microsoft.Extensions.Logging; namespace FrameworkBLL.GOP.Worker.NodeExecutors { // ============================================================ // PromotionExportNodeExecutor — executes a "PromotionExport" node type. // // NOT the primary export path — the "Export Package" HTTP endpoint calls // IPromotionBLL.BuildExportBundleAsync directly and synchronously, because a // file-download response needs its result immediately and GOP execution is // deliberately async/queued (GopQueueBLL.SubmitExecution enqueues and // returns; a background worker runs it later). This node type is kept as a // valid, DI-registered GOP step for a possible future scheduled/automated // export trigger, delegating to the same IPromotionBLL logic so there is // exactly one implementation of "resolve entity + export + validate," not // two. See /Users/venkatv/.claude/plans/as-part-of-developement-indexed-stonebraker.md. // // Input payload: {"EntityTypeCode":"MENU","OriginEntityId":123}. // Output payload: the serialized PromotionBundleDTO. // ============================================================ public class PromotionExportNodeExecutor : INodeExecutor { private readonly IPromotionBLL _PromotionBLL; private readonly ILogger _Logger; public PromotionExportNodeExecutor( IPromotionBLL promotionBLL, ILogger logger) { _PromotionBLL = promotionBLL; _Logger = logger; } public string NodeType => "PromotionExport"; private class ExportRequest { public string EntityTypeCode { get; set; } = string.Empty; public int OriginEntityId { get; set; } } public async Task ExecuteAsync( GopFlowSnapshotStepDTO step, GopExecutionHeaderDTO header, string? inputPayloadJson, LoginDTO loginDTO, CancellationToken ct) { if (string.IsNullOrWhiteSpace(inputPayloadJson)) throw new InvalidOperationException( "PromotionExportNodeExecutor requires an input payload with EntityTypeCode/OriginEntityId."); var request = JsonSerializer.Deserialize(inputPayloadJson) ?? throw new InvalidOperationException("PromotionExport payload could not be parsed."); _Logger.LogDebug( "PromotionExportNodeExecutor: execution {ExecutionId} exporting {EntityTypeCode}/{OriginEntityId}", header.ExecutionId, request.EntityTypeCode, request.OriginEntityId); // Permanent failures (validation blocks) propagate as-is — this flow step's config // should set MaxRetries=0 so GOP doesn't waste attempts re-running the same validation. var bundle = await _PromotionBLL.BuildExportBundleAsync( request.EntityTypeCode, request.OriginEntityId, loginDTO, ct).ConfigureAwait(false); _Logger.LogDebug( "PromotionExportNodeExecutor: execution {ExecutionId} export complete for {EntityTypeCode}/{OriginEntityId}", header.ExecutionId, request.EntityTypeCode, request.OriginEntityId); return JsonSerializer.Serialize(bundle); } } }