-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathRejectedScenario.cs
More file actions
122 lines (104 loc) · 4.93 KB
/
Copy pathRejectedScenario.cs
File metadata and controls
122 lines (104 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
using ModularityKit.Mutator.Abstractions.Context;
using ModularityKit.Mutator.Abstractions.Effects;
using ModularityKit.Mutator.Abstractions.Engine;
using WorkflowApprovals.Mutations;
using WorkflowApprovals.State;
namespace WorkflowApprovals.Scenarios;
/// <summary>
/// RejectedScenario
///
/// Demonstrates an approval workflow scenario where steps may be blocked due to policy decisions.
///
/// Scenario Details:
/// - Starts a new workflow with multiple steps using <see cref="StartApprovalMutation"/>.
/// - Sequentially attempts to approve each step using <see cref="ApproveStepMutation"/>.
/// - Uses <see cref="MutationContext.User"/> to simulate individual users performing approvals.
/// - Logs success or policy-based rejections for each step.
/// - Works with <see cref="ApprovalWorkflowState"/> which tracks the status of each step, who approved or rejected it.
///
/// Key Steps:
/// 1. Initialize <see cref="ApprovalWorkflowState"/> as empty workflow state.
/// 2. Start workflow with predefined steps using <see cref="StartApprovalMutation"/> and <see cref="MutationContext.System"/>.
/// 3. Sequentially attempt to approve each step using different user contexts.
/// 4. Evaluate each mutation result for success or policy denial.
/// 5. Print final workflow state with detailed approval/rejection info.
///
/// Example Use Case:
/// - Testing policy enforcement in approval workflows.
/// - Simulating rejected steps due to validation rules, risk controls, or approval policies.
/// - Demonstrating auditability by showing which users attempted approvals and why any were blocked.
///
/// Notes:
/// - Policy decisions may block specific steps and are logged per mutation.
/// - The scenario can be extended to mix approvals and rejections to test complex workflows.
/// - Correlation IDs and metadata can be used to trace and audit workflow operations.
/// </summary>
internal static class RejectedScenario
{
internal static async Task Run(IMutationEngine engine)
{
Console.WriteLine("\n=== Rejected Scenario ===");
var state = new ApprovalWorkflowState();
var systemCtx = MutationContext.System("Start workflow", correlationId: "workflow-456");
var start = new StartApprovalMutation("initiator", ["Step1", "Step2", "Step3"], systemCtx);
var result = await engine.ExecuteAsync(start, state);
if (!result.IsSuccess || result.NewState == null)
{
Console.WriteLine("✗ Failed to start workflow.");
return;
}
state = result.NewState;
var approvers = new[] { "alice", "bob", "carol" };
var workflowRejected = false;
for (var i = 0; i < state.Steps.Count; i++)
{
var userCtx = MutationContext.User(approvers[i], reason: $"Approve Step{i}");
var approve = new ApproveStepMutation(i, approvers[i], userCtx);
var res = await engine.ExecuteAsync(approve, state);
if (res.IsSuccess)
{
state = res.NewState!;
Console.WriteLine($"✓ Step {i} approved by {approvers[i]}");
}
else
{
Console.WriteLine($"✗ Step {i} blocked for {approvers[i]}:");
foreach (var dec in res.PolicyDecisions)
Console.WriteLine($" Policy: {dec.PolicyName} – {dec.Reason}");
var rejectContext = MutationContext.User("security.lead", reason: "Reject blocked workflow");
var reject = new RejectWorkflowMutation("security.lead", rejectContext);
var rejectResult = await engine.ExecuteAsync(reject, state);
if (!rejectResult.IsSuccess || rejectResult.NewState == null)
{
Console.WriteLine("✗ Failed to reject workflow after policy block.");
break;
}
state = rejectResult.NewState;
workflowRejected = true;
Console.WriteLine("Workflow rejected after policy block.");
PrintSideEffects("Reject workflow", rejectResult.SideEffects);
break;
}
}
Console.WriteLine("Workflow final state:");
for (var i = 0; i < state.Steps.Count; i++)
{
var s = state.Steps[i];
Console.WriteLine($" Step{i}: {s.Status} by {(s.ApprovedBy ?? s.RejectedBy ?? "-")}");
}
if (!workflowRejected)
{
Console.WriteLine("Workflow remained active because no rejection path was triggered.");
}
}
private static void PrintSideEffects(string operation, IReadOnlyList<SideEffect> sideEffects)
{
Console.WriteLine($"{operation} side effects:");
foreach (var effect in sideEffects)
{
Console.WriteLine(
$" {effect.Type} | severity={effect.Severity} | requiresAction={effect.RequiresAction}");
Console.WriteLine($" {effect.Description}");
}
}
}