feat: add WorkProcessor interfaces and options

- ISearchNotificationService: SignalR notification abstraction in Core
- WorkProcessorOptions: configuration for work processor behavior
- ISearchRepository: Search table operations contract
- ISearchExecutionService: search pipeline orchestration contract
This commit is contained in:
Joseph Doherty
2026-01-07 06:06:12 -05:00
parent 488aef560b
commit 14799b62dc
4 changed files with 118 additions and 0 deletions
@@ -0,0 +1,24 @@
using JdeScoping.Core.Models.Search;
namespace JdeScoping.Core.Interfaces;
/// <summary>
/// Interface for SignalR notifications - lives in Core to avoid dependency issues.
/// Implementation lives in Host/Api layer.
/// </summary>
public interface ISearchNotificationService
{
/// <summary>
/// Notifies clients of search status update.
/// </summary>
/// <param name="search">The search with updated status.</param>
/// <param name="ct">Cancellation token.</param>
Task NotifySearchUpdateAsync(Search search, CancellationToken ct = default);
/// <summary>
/// Notifies clients of work processor status change.
/// </summary>
/// <param name="status">Status message.</param>
/// <param name="ct">Cancellation token.</param>
Task NotifyStatusAsync(string status, CancellationToken ct = default);
}
@@ -0,0 +1,17 @@
using JdeScoping.Core.Models.Search;
namespace JdeScoping.DataSync.Contracts;
/// <summary>
/// Interface for search execution pipeline orchestration.
/// </summary>
public interface ISearchExecutionService
{
/// <summary>
/// Executes the complete search pipeline: query, Excel generation, result storage.
/// Handles status transitions and notifications.
/// </summary>
/// <param name="search">The search to execute.</param>
/// <param name="ct">Cancellation token.</param>
Task ExecuteSearchAsync(Search search, CancellationToken ct = default);
}
@@ -0,0 +1,40 @@
using JdeScoping.Core.Models.Search;
namespace JdeScoping.DataSync.Contracts;
/// <summary>
/// Repository interface for Search table operations.
/// </summary>
public interface ISearchRepository
{
/// <summary>
/// Gets the next queued search ordered by SubmitDT (FIFO).
/// </summary>
/// <param name="ct">Cancellation token.</param>
/// <returns>The next queued search, or null if none.</returns>
Task<Search?> GetNextQueuedSearchAsync(CancellationToken ct = default);
/// <summary>
/// Resets partial searches (Running but not Ended) back to Queued status.
/// Called at service startup to handle interrupted operations.
/// </summary>
/// <param name="ct">Cancellation token.</param>
/// <returns>Count of reset searches.</returns>
Task<int> ResetPartialSearchesAsync(CancellationToken ct = default);
/// <summary>
/// Marks search as Running with StartDT = now.
/// </summary>
/// <param name="searchId">Search ID.</param>
/// <param name="ct">Cancellation token.</param>
Task StartSearchAsync(int searchId, CancellationToken ct = default);
/// <summary>
/// Marks search as Ended (success) or Error (failure) with EndDT and optional Results.
/// </summary>
/// <param name="searchId">Search ID.</param>
/// <param name="success">Whether search completed successfully.</param>
/// <param name="results">Excel file bytes (null on failure).</param>
/// <param name="ct">Cancellation token.</param>
Task CompleteSearchAsync(int searchId, bool success, byte[]? results, CancellationToken ct = default);
}
@@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations;
namespace JdeScoping.DataSync.Options;
/// <summary>
/// Configuration options for the WorkProcessor background service.
/// </summary>
public class WorkProcessorOptions
{
/// <summary>
/// Configuration section name.
/// </summary>
public const string SectionName = "WorkProcessor";
/// <summary>
/// Whether the work processor is enabled. Default: true.
/// </summary>
public bool Enabled { get; set; } = true;
/// <summary>
/// Interval between work cycles. Default: 5 seconds.
/// </summary>
[Range(typeof(TimeSpan), "00:00:01", "01:00:00")]
public TimeSpan WorkInterval { get; set; } = TimeSpan.FromSeconds(5);
/// <summary>
/// Search execution timeout. Default: 30 minutes.
/// </summary>
[Range(typeof(TimeSpan), "00:01:00", "04:00:00")]
public TimeSpan SearchTimeout { get; set; } = TimeSpan.FromMinutes(30);
/// <summary>
/// Number of days to retain DataUpdate records. Default: 30.
/// </summary>
[Range(1, 365)]
public int PurgeRetentionDays { get; set; } = 30;
}