using Microsoft.Extensions.Logging; namespace Finch.Raven; public class InterceptorInstruction where T : FinchIdEntity, new() { public Guid Guid { get; private set; } public InterceptorRunType Runtype { get; private set; } public T Model { get; private set; } public T PreviousModel { get; private set; } public Type ModelType { get; private set; } public Result Result { get; private set; } protected IFinchContext Context { get; private set; } protected IFinchStore Store { get; private set; } protected Lazy> Interceptors { get; private set; } protected Dictionary InterceptorCache { get; private set; } = new(); protected IInterceptors InterceptorHandler { get; private set; } protected ILogger Logger { get; private set; } protected Func InterceptorFilter { get; private set; } = x => true; internal InterceptorInstruction(IInterceptors interceptors, IFinchStore store, IFinchContext context, Lazy> registrations, ILogger logger, InterceptorRunType runtype, T model, T previousModel = null) { InterceptorHandler = interceptors; Store = store; Context = context; Guid = Guid.NewGuid(); Logger = logger; Runtype = runtype; Model = model; PreviousModel = previousModel; Interceptors = registrations; ModelType = model.GetType(); } /// /// Custom interceptor filter for this instruction (the CanHandle() method for each interceptor is still activated) /// public void Filter(Func predicate) { InterceptorFilter = predicate; } /// /// Run all interceptors (in order) which can handle the given type. /// Depending on the action any of the following methods on the interceptor is called: Creating(), Updating(), Deleting(). /// If any of the interceptors returns a result the operation is cancelled and this result is returned. /// public async Task Start(IRavenOperations operations) { foreach (IInterceptor interceptor in GetInterceptors()) { InterceptorParameters parameters = new() { Context = Context, Store = Store, Properties = new(), Interceptors = InterceptorHandler, Operations = operations, PreviousModel = PreviousModel }; if (!interceptor.CanHandle(parameters, ModelType)) { continue; } Logger.LogDebug("Run interceptor {interceptor} for {type}:{operation}", interceptor.Name, ModelType, Runtype); InterceptorResult result = (await HandleBefore(interceptor, parameters)) ?? new(); result.InterceptorHash = IdGenerator.Create(32); InterceptorCache.Add(interceptor, parameters); // we cancel all further interceptors if a result is available and return this instead if (result.Result != null) { Result = result.Result.ConvertTo(result.Result.Model as T); return false; } // the Continue task will cancel all further interceptors if (!result.Continue) { break; } } return true; } /// /// Run all interceptors (in order) which can handle the given type. /// Depending on the action any of the following methods on the interceptor is called: Created(), Updated(), Deleted(). /// The parameters which are returned from the Start() operation are passed to the methods. /// public async Task Complete() { foreach ((IInterceptor interceptor, InterceptorParameters parameters) in InterceptorCache) { await HandleAfter(interceptor, parameters); } } protected IEnumerable GetInterceptors() { return Interceptors.Value.Where(InterceptorFilter).OrderByDescending(x => x.Gravity); } /// /// Proxy for handling methods on an interceptor /// protected Task> HandleBefore(IInterceptor interceptor, InterceptorParameters parameters) => Runtype switch { InterceptorRunType.Create => interceptor.Creating(parameters, Model), InterceptorRunType.Update => interceptor.Updating(parameters, Model), InterceptorRunType.Delete => interceptor.Deleting(parameters, Model), _ => throw new NotImplementedException() }; /// /// Proxy for handling methods on an interceptor /// protected Task HandleAfter(IInterceptor interceptor, InterceptorParameters parameters) => Runtype switch { InterceptorRunType.Create => interceptor.Created(parameters, Model), InterceptorRunType.Update => interceptor.Updated(parameters, Model), InterceptorRunType.Delete => interceptor.Deleted(parameters, Model), _ => throw new NotImplementedException() }; }