Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Abstractions/Audit/IMutationAuditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace ModularityKit.Mutator.Abstractions.Audit;
/// </remarks>
public interface IMutationAuditor
{
/// <summary>
/// When <see langword="false" />, the runtime may skip creating audit entries for this auditor.
/// </summary>
bool IsEnabled => true;

/// <summary>
/// Records an audit entry for a mutation.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Abstractions/History/IMutationHistoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ namespace ModularityKit.Mutator.Abstractions.History;
/// </example>
public interface IMutationHistoryStore
{
/// <summary>
/// When <see langword="false" />, the runtime may skip creating history entries for this store.
/// </summary>
bool IsEnabled => true;

/// <summary>
/// Persists a mutation history entry.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Runtime/Diagnostics/MutationAuditEntryFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public static MutationHistoryEntry CreateHistoryEntry<TState>(
Intent = mutation.Intent,
Context = mutation.Context,
Changes = result.Changes,
SideEffects = result.SideEffects.ToList(),
SideEffects = result.SideEffects,
Timestamp = mutation.Context.Timestamp,
ExecutionTime = duration
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,17 @@
namespace ModularityKit.Mutator.Runtime.Internal.Execution;

/// <summary>
/// Handles blocked, failed, and completed mutation outcomes after policy evaluation and execution.
/// Processes mutation outcomes after policy evaluation and mutation execution.
/// </summary>
/// <remarks>
/// Handles policy blocked, validation failed, successfully completed, and finalized
/// mutation outcomes. Coordinates interceptor notifications, audit recording,
/// mutation history persistence, and metrics collection.
/// </remarks>
/// <param name="interceptorPipeline">Pipeline responsible for notifying registered mutation interceptors about execution outcomes.</param>
/// <param name="auditor">Auditor responsible for recording mutation success and failure audit entries. </param>
/// <param name="historyStore">Store responsible for persisting mutation history for committed mutations.</param>
/// <param name="metricsCollector">Collector responsible for recording mutation execution metrics.</param>
internal sealed class MutationExecutionOutcomeProcessor(
IInterceptorPipeline interceptorPipeline,
IMutationAuditor auditor,
Expand All @@ -28,8 +37,15 @@ internal sealed class MutationExecutionOutcomeProcessor(
private readonly IMetricsCollector _metricsCollector = metricsCollector ?? throw new ArgumentNullException(nameof(metricsCollector));

/// <summary>
/// Handles a policy-blocked mutation result.
/// Handles mutation that was blocked by a policy decision.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="executionContext">
/// The context containing the mutation, current state, execution metadata,
/// cancellation token, and metrics information.
/// </param>
/// <param name="policyDecision">The policy decision that blocked the mutation.</param>
/// <returns>Policy blocked mutation result with audit and execution metrics finalized.</returns>
public async Task<MutationResult<TState>> HandleBlockedPolicyAsync<TState>(
MutationExecutionContext<TState> executionContext,
PolicyDecision policyDecision)
Expand All @@ -53,9 +69,17 @@ await AuditFailureAsync(
return await FinalizeResultAsync(executionContext, blockedResult).ConfigureAwait(false);
}


/// <summary>
/// Handles a validation-failed mutation result.
/// Handles mutation that failed validation.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="executionContext">
/// The context containing the mutation, current state, execution metadata,
/// cancellation token, and metrics information.
/// </param>
/// <param name="validationFailureResult">The mutation result containing validation errors.</param>
/// <returns>The validation failure result with audit and execution metrics finalized.</returns>
public async Task<MutationResult<TState>> HandleValidationFailureAsync<TState>(
MutationExecutionContext<TState> executionContext,
MutationResult<TState> validationFailureResult)
Expand All @@ -70,8 +94,20 @@ await AuditFailureAsync(
}

/// <summary>
/// Completes a mutation result after execution and policy modifications.
/// Completes mutation execution after applying policy modifications and
/// processing successful mutation outcomes.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="executionContext">
/// The context containing the mutation, current state, execution metadata,
/// cancellation token, and metrics information.
/// </param>
/// <param name="mutationResult">The result produced by the mutation execution.</param>
/// <param name="policyDecision">The policy decision containing any modifications to apply to the mutation result.</param>
/// <returns>
/// The finalized mutation result after interceptor notification, auditing,
/// optional history persistence, and metrics processing.
/// </returns>
public async Task<MutationResult<TState>> CompleteMutationAsync<TState>(
MutationExecutionContext<TState> executionContext,
MutationResult<TState> mutationResult,
Expand Down Expand Up @@ -110,8 +146,13 @@ await StoreInHistoryAsync(
}

/// <summary>
/// Finalizes a runtime result by recording metrics and attaching total execution time.
/// Finalizes mutation result by recording execution metrics and attaching
/// the total execution duration to the result.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="executionContext">The context containing execution timing and metrics information.</param>
/// <param name="result">The mutation result to finalize.</param>
/// <returns>The mutation result with finalized execution metrics.</returns>
public async Task<MutationResult<TState>> FinalizeResultAsync<TState>(
MutationExecutionContext<TState> executionContext,
MutationResult<TState> result)
Expand All @@ -133,13 +174,25 @@ await _metricsCollector.RecordAsync(
};
}

/// <summary>
/// Records successful mutation execution in the audit system when auditing is enabled.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="mutation">The mutation that was executed.</param>
/// <param name="result">The resulting mutation result.</param>
/// <param name="policyDecision">The policy decision applied to the mutation.</param>
/// <param name="executionId">The unique identifier of the mutation execution.</param>
/// <param name="duration">The total execution duration.</param>
private async Task AuditSuccessAsync<TState>(
IMutation<TState> mutation,
MutationResult<TState> result,
PolicyDecision policyDecision,
string executionId,
TimeSpan duration)
{
if (!_auditor.IsEnabled)
return;

var entry = MutationAuditEntryFactory.CreateSuccess(
mutation,
result,
Expand All @@ -150,12 +203,23 @@ private async Task AuditSuccessAsync<TState>(
await _auditor.AuditAsync(entry).ConfigureAwait(false);
}

/// <summary>
/// Records failed mutation execution in the audit system when auditing is enabled.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="mutation">The mutation that failed.</param>
/// <param name="result">The resulting mutation failure.</param>
/// <param name="executionId">The unique identifier of the mutation execution.</param>
/// <param name="duration">The total execution duration.</param>
private async Task AuditFailureAsync<TState>(
IMutation<TState> mutation,
MutationResult<TState> result,
string executionId,
TimeSpan duration)
{
if (!_auditor.IsEnabled)
return;

var entry = MutationAuditEntryFactory.CreateFailure(
mutation,
result,
Expand All @@ -165,13 +229,26 @@ private async Task AuditFailureAsync<TState>(
await _auditor.AuditAsync(entry).ConfigureAwait(false);
}

private async Task StoreInHistoryAsync<TState>(
/// <summary>
/// Stores successful committed mutation in the mutation history store when history
/// persistence is enabled and state identifier can be resolved.
/// </summary>
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
/// <param name="mutation">The mutation that was executed.</param>
/// <param name="result">The resulting mutation result.</param>
/// <param name="executionId">The unique identifier of the mutation execution.</param>
/// <param name="duration">The total execution duration.</param>
/// <param name="cancellationToken">Token that can be used to cancel the history persistence operation. </param>
private async Task StoreInHistoryAsync<TState>(
IMutation<TState> mutation,
MutationResult<TState> result,
string executionId,
TimeSpan duration,
CancellationToken cancellationToken)
{
if (!_historyStore.IsEnabled)
return;

var stateId = MutationAuditEntryFactory.ResolveStateId(mutation.Context);
if (string.IsNullOrEmpty(stateId))
return;
Expand Down
Loading