| | | 1 | | namespace Nabs.ActivityFramework; |
| | | 2 | | |
| | | 3 | | public interface IActivity |
| | | 4 | | { |
| | | 5 | | bool HasStateChanged { get; } |
| | | 6 | | ValidationResult ValidationResult { get; } |
| | | 7 | | Task RunAsync(); |
| | | 8 | | } |
| | | 9 | | |
| | | 10 | | public interface IActivity<TActivityState> |
| | | 11 | | : IActivity |
| | | 12 | | where TActivityState : class, IActivityState; |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// An activity that holds the state based on TActivityState. |
| | | 16 | | /// An activity that simply creates and instance of the state defined by TActivityState. |
| | | 17 | | /// Typically this state is used to start off a workflow that will present blank or partially filled state. |
| | | 18 | | /// </summary> |
| | | 19 | | /// <typeparam name="TActivityState"></typeparam> |
| | | 20 | | public abstract class Activity<TActivityState> |
| | | 21 | | : IActivity<TActivityState> |
| | | 22 | | where TActivityState : class, IActivityState |
| | | 23 | | { |
| | | 24 | | private IActivityStateFactory<TActivityState>? _activityStateFactory; |
| | | 25 | | private IActivityStateValidator<TActivityState>? _activityStateValidator; |
| | | 26 | | |
| | | 27 | | protected Activity() |
| | | 28 | | { |
| | 1 | 29 | | } |
| | | 30 | | |
| | | 31 | | protected Activity(TActivityState activityState) |
| | | 32 | | { |
| | 1 | 33 | | InitialActivityState = activityState; |
| | 1 | 34 | | ActivityState = activityState; |
| | 1 | 35 | | } |
| | | 36 | | |
| | | 37 | | private TActivityState? _initialActivityState; |
| | | 38 | | public TActivityState? InitialActivityState |
| | | 39 | | { |
| | 2 | 40 | | get { return _initialActivityState; } |
| | | 41 | | protected set |
| | | 42 | | { |
| | 2 | 43 | | if (_initialActivityState is not null) |
| | | 44 | | { |
| | 0 | 45 | | throw new InvalidOperationException("InitialActivityState can only be set once."); |
| | | 46 | | } |
| | 2 | 47 | | _initialActivityState = value; |
| | 2 | 48 | | ActivityState = value; |
| | 2 | 49 | | } |
| | | 50 | | } |
| | | 51 | | public TActivityState? ActivityState { get; protected set; } = default!; |
| | | 52 | | |
| | 0 | 53 | | public bool HasStateChanged => InitialActivityState != ActivityState; |
| | | 54 | | |
| | | 55 | | public ValidationResult ValidationResult { get; set; } = default!; |
| | | 56 | | |
| | 2 | 57 | | protected Dictionary<IActivityStateBehaviour, Action?> Behaviours { get; } = []; |
| | | 58 | | |
| | | 59 | | protected void AddFactory(IActivityStateFactory<TActivityState> factory) |
| | | 60 | | { |
| | 1 | 61 | | _activityStateFactory = factory; |
| | 1 | 62 | | } |
| | | 63 | | |
| | | 64 | | protected void AddValidator(IActivityStateValidator<TActivityState> validator) |
| | | 65 | | { |
| | 1 | 66 | | _activityStateValidator = validator; |
| | 1 | 67 | | } |
| | | 68 | | |
| | | 69 | | protected void AddValidator<TStateValidator>() |
| | | 70 | | where TStateValidator : IActivityStateValidator<TActivityState>, new() |
| | | 71 | | { |
| | 1 | 72 | | _activityStateValidator = new TStateValidator(); |
| | 1 | 73 | | } |
| | | 74 | | |
| | | 75 | | protected void AddBehaviour(IActivityStateBehaviour behaviour, Action? action = null) |
| | | 76 | | { |
| | 2 | 77 | | Behaviours.Add(behaviour, action); |
| | 2 | 78 | | } |
| | | 79 | | |
| | | 80 | | public async Task RunAsync() |
| | | 81 | | { |
| | 2 | 82 | | if (InitialActivityState is null) |
| | | 83 | | { |
| | 0 | 84 | | if (_activityStateFactory is not null) |
| | | 85 | | { |
| | 0 | 86 | | InitialActivityState = _activityStateFactory.Run(); |
| | | 87 | | } |
| | | 88 | | else |
| | | 89 | | { |
| | 0 | 90 | | throw new InvalidOperationException("InitialActivityState is null and no factory has been provided. Set |
| | | 91 | | } |
| | | 92 | | } |
| | | 93 | | |
| | 2 | 94 | | ActivityState ??= InitialActivityState; |
| | | 95 | | |
| | 2 | 96 | | if (Behaviours.Count > 0) |
| | | 97 | | { |
| | 2 | 98 | | await ProcessBehaviours(); |
| | | 99 | | } |
| | | 100 | | |
| | 2 | 101 | | if (_activityStateValidator is not null) |
| | | 102 | | { |
| | 1 | 103 | | ValidationResult = _activityStateValidator.Run(ActivityState); |
| | | 104 | | } |
| | 2 | 105 | | ValidationResult ??= new ValidationResult(); |
| | 2 | 106 | | } |
| | | 107 | | |
| | | 108 | | private async Task ProcessBehaviours() |
| | | 109 | | { |
| | 2 | 110 | | if (ActivityState is null) |
| | | 111 | | { |
| | 0 | 112 | | return; |
| | | 113 | | } |
| | | 114 | | |
| | 6 | 115 | | foreach (var behaviour in Behaviours) |
| | | 116 | | { |
| | 2 | 117 | | if (behaviour.Key is IActivityStateBehaviourSync<TActivityState> syncBehaviour) |
| | | 118 | | { |
| | 2 | 119 | | ActivityState = syncBehaviour.Run(ActivityState); |
| | | 120 | | |
| | | 121 | | } |
| | 0 | 122 | | else if (behaviour.Key is IActivityStateBehaviourAsync<TActivityState> asyncBehaviour) |
| | | 123 | | { |
| | 0 | 124 | | ActivityState = await asyncBehaviour.RunAsync(ActivityState); |
| | | 125 | | } |
| | | 126 | | else |
| | | 127 | | { |
| | 0 | 128 | | throw new InvalidOperationException("Behaviour is not of type IActivityStateBehaviourSync or IActivitySt |
| | | 129 | | } |
| | | 130 | | |
| | 2 | 131 | | if (behaviour.Value is not null) |
| | | 132 | | { |
| | 0 | 133 | | behaviour.Value(); |
| | | 134 | | } |
| | 2 | 135 | | } |
| | 2 | 136 | | } |
| | | 137 | | } |