using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using F4SD.ActionConnector.Enumerations; using F4SD.ActionConnector.Events; using F4SD.ActionConnector.Execution; using F4SD.ActionConnector.Models; using F4SD.ActionConnector.Payloads; using static C4IT.Logging.cLogManager; namespace F4SD.ActionConnector.Bus { /// /// Entry point for the ActionConnector. /// Subscribes to , reads the configuration once, /// and dispatches actions whenever a trigger event fires. /// /// Usage from the host application: /// /// var bus = new ActionBus(new ActionBusOptions /// { /// ConfigurationFilePath = @"C:\Config\F4SD-ExternalCommunication-Configuration.xml", /// QuickActionExecutor = async request => /// { /// await myJiraClient.CreateTicketAsync(request.QuickActionRef, request.Parameters); /// return ActionResult.Ok(request.ActionId); /// } /// }); /// /// bus.Start(); /// // later: /// bus.Stop(); /// /// public sealed class ActionBus : IDisposable { private readonly ActionBusOptions _options; private readonly ActionDispatcher _dispatcher; private ExternalCommunicationConfiguration _configuration; private bool _running; public ActionBus(ActionBusOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); _dispatcher = new ActionDispatcher(options.QuickActionExecutor); } /// /// Loads the configuration and subscribes to all trigger events. /// Call once during application startup. /// public void Start() { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { _configuration = new ExternalCommunicationConfiguration(); _configuration.Initiate(_options.ConfigRootElement, _options.Parser); ActionConnectorEvents.CaseClosed += OnCaseClosed; ActionConnectorEvents.CaseCreated += OnCaseCreated; ActionConnectorEvents.ApplicationStartup += OnApplicationStartup; _running = true; LogEntry("ActionBus started.", C4IT.Logging.LogLevels.Info); } catch (Exception ex) { LogException(ex); } finally { LogMethodEnd(CM); } } /// /// Unsubscribes from all trigger events. /// Call during application shutdown. /// public void Stop() { ActionConnectorEvents.CaseClosed -= OnCaseClosed; ActionConnectorEvents.CaseCreated -= OnCaseCreated; ActionConnectorEvents.ApplicationStartup -= OnApplicationStartup; _running = false; LogEntry("ActionBus stopped.", C4IT.Logging.LogLevels.Info); } public void Dispose() => Stop(); // ────────────────────────────────────────────────────────────────────────── // Event handlers — each one creates a typed ActionContext and fires dispatch // ────────────────────────────────────────────────────────────────────────── private void OnCaseClosed(object sender, TriggerEventArgs args) => DispatchTrigger(TriggerEvent.CaseClosed, args.Payload, sender); private void OnCaseCreated(object sender, TriggerEventArgs args) => DispatchTrigger(TriggerEvent.CaseCreated, args.Payload, sender); private void OnApplicationStartup(object sender, TriggerEventArgs args) => DispatchTrigger(TriggerEvent.ApplicationStartup, args.Payload, sender); // ────────────────────────────────────────────────────────────────────────── // Core dispatch — fire-and-forget wrapper that surfaces results via events // ────────────────────────────────────────────────────────────────────────── private void DispatchTrigger(TriggerEvent triggerEvent, PayloadBase payload, object sender) { if (!_running) return; if (!_configuration.Triggers.TryGetValue(triggerEvent, out Trigger trigger)) return; // Run async dispatch on the thread pool; do not block the caller. Task.Run(() => DispatchActionsAsync(trigger.Actions, triggerEvent, payload, sender)); } private async Task DispatchActionsAsync( IDictionary actions, TriggerEvent triggerEvent, PayloadBase payload, object sender) { foreach (var pair in actions) { ActionDefinitionBase action = pair.Value; var context = new ActionContext { TriggerEvent = triggerEvent, AwaitResult = action.AwaitResult, Payload = payload, }; ActionResult result = await ExecuteSafeAsync(action, context); ActionConnectorEvents.RaiseActionCompleted( sender, new ActionCompletedEventArgs(result, context)); } } private async Task ExecuteSafeAsync(ActionDefinitionBase action, ActionContext context) { try { return await _dispatcher.DispatchAsync(action, context); } catch (Exception ex) { LogException(ex); return ActionResult.Fail(action.Id, ex.Message); } } } }