feat(application): add inactive profile validation with custom exception

- Create InactiveProfileException for profile active status validation
- Update TriggeringDEXJobBatchCommand to check profile active status before execution
- Add ICfgProfileRepository dependency to batch command handler
- Throw InactiveProfileException when attempting to execute inactive profiles
- Add profile not found validation with descriptive error message
- Reorganize commands under DEXJob/Commands directory structure
This commit is contained in:
2026-07-11 19:16:35 +02:00
parent eaf24e05ee
commit 8255f1f6aa
5 changed files with 239 additions and 8 deletions

View File

@@ -0,0 +1,47 @@
using System;
namespace ECMJobRunner.Application.Common.Exceptions
{
/// <summary>
/// Exception thrown when attempting to execute a job for an inactive profile
/// Extends JobException with profile-specific context
/// </summary>
public class InactiveProfileException : JobException
{
/// <summary>
/// Initializes a new instance of InactiveProfileException
/// </summary>
/// <param name="profileId">ID of the inactive profile</param>
/// <param name="profileName">Name of the inactive profile (nullable)</param>
/// <param name="batchId">Unique batch identifier for tracking</param>
/// <remarks>
/// Use this exception when:
/// - Attempting to trigger DEX job batch for an inactive profile
/// - Attempting to execute individual jobs from an inactive profile
/// - Profile is deactivated during execution
/// </remarks>
public InactiveProfileException(long profileId, string? profileName, string batchId)
: base(
jobName: "Profile Execution",
processName: "Profile Active Status Validation",
batchId: batchId,
reason: "The profile is marked as inactive and cannot be executed",
innerException: null,
("Profile ID", profileId.ToString(), false),
("Profile Name", profileName, true))
{
ProfileId = profileId;
ProfileName = profileName;
}
/// <summary>
/// Gets the ID of the inactive profile
/// </summary>
public long ProfileId { get; }
/// <summary>
/// Gets the name of the inactive profile (nullable)
/// </summary>
public string? ProfileName { get; }
}
}

View File

@@ -1,10 +1,12 @@
using ECMJobRunner.Domain.Interfaces;
using ECMJobRunner.Application.Common.Exceptions;
using ECMJobRunner.Application.Common.Interfaces;
using ECMJobRunner.Domain.Interfaces;
using MediatR;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ECMJobRunner.Application.DEXJob
namespace ECMJobRunner.Application.DEXJob.Commands
{
/// <summary>
/// Command to trigger DEX job batch for a profile
@@ -21,8 +23,9 @@ namespace ECMJobRunner.Application.DEXJob
/// <summary>
/// Handler for TriggeringDEXJobBatchCommand
/// Retrieves all SQL jobs for a profile and executes them sequentially
/// Validates that the profile is active before execution
/// </summary>
public class TriggeringDEXJobBatchCommandHandler(IProfileSqlJobRepository jobRepo, ISender sender)
public class TriggeringDEXJobBatchCommandHandler(ICfgProfileRepository profileRepo, IProfileSqlJobRepository jobRepo, ISender sender)
: IRequestHandler<TriggeringDEXJobBatchCommand, Unit>
{
/// <summary>
@@ -32,12 +35,28 @@ namespace ECMJobRunner.Application.DEXJob
/// <param name="request">The command containing the profile ID</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Unit value indicating completion</returns>
/// <exception cref="InactiveProfileException">Thrown when the profile is not active</exception>
public async Task<Unit> Handle(TriggeringDEXJobBatchCommand request, CancellationToken cancellationToken)
{
var jobs = await jobRepo.FindAsync(j => j.ProfileId == request.ProfileId, cancellationToken);
var batchId = CreateBatchId();
// Retrieve the profile to check if it's active
var profile = await profileRepo.GetByIdAsync(request.ProfileId, cancellationToken);
// Check if profile exists and is active
if (profile == null)
{
throw new InvalidOperationException($"Profile with ID {request.ProfileId} not found.");
}
if (!profile.Active)
{
throw new InactiveProfileException(profile.Id, profile.ProfileName, batchId);
}
// Retrieve all jobs for the profile
var jobs = await jobRepo.FindAsync(j => j.ProfileId == request.ProfileId, cancellationToken);
foreach (var job in jobs)
{
await sender.Send(new TriggeringDEXJobCommand

View File

@@ -3,7 +3,7 @@ using MediatR;
using System.Threading;
using System.Threading.Tasks;
namespace ECMJobRunner.Application.DEXJob
namespace ECMJobRunner.Application.DEXJob.Commands
{
/// <summary>
/// Command to trigger a single DEX job execution