160 lines
6.4 KiB
C#
160 lines
6.4 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Entry point for the ActionConnector.
|
|
/// Subscribes to <see cref="ActionConnectorEvents"/>, reads the configuration once,
|
|
/// and dispatches actions whenever a trigger event fires.
|
|
///
|
|
/// Usage from the host application:
|
|
/// <code>
|
|
/// 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();
|
|
/// </code>
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the configuration and subscribes to all trigger events.
|
|
/// Call once during application startup.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsubscribes from all trigger events.
|
|
/// Call during application shutdown.
|
|
/// </summary>
|
|
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<CaseClosedPayload> args)
|
|
=> DispatchTrigger(TriggerEvent.CaseClosed, args.Payload, sender);
|
|
|
|
private void OnCaseCreated(object sender, TriggerEventArgs<CaseCreatedPayload> args)
|
|
=> DispatchTrigger(TriggerEvent.CaseCreated, args.Payload, sender);
|
|
|
|
private void OnApplicationStartup(object sender, TriggerEventArgs<ApplicationStartupPayload> 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<string, ActionDefinitionBase> 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<ActionResult> ExecuteSafeAsync(ActionDefinitionBase action, ActionContext context)
|
|
{
|
|
try
|
|
{
|
|
return await _dispatcher.DispatchAsync(action, context);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogException(ex);
|
|
return ActionResult.Fail(action.Id, ex.Message);
|
|
}
|
|
}
|
|
}
|
|
}
|