* Version incremented

* Added ability to handle errors during filters validation inside Handlers
* Moved Result class to root directory
* Added FilterOrigin enum
This commit is contained in:
2025-08-04 05:11:59 +04:00
parent afb500cfc6
commit a794b6ed54
13 changed files with 156 additions and 79 deletions
@@ -15,7 +15,7 @@
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<Version>1.0.10</Version> <Version>1.0.11</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+1 -1
View File
@@ -16,7 +16,7 @@
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<Version>1.0.10</Version> <Version>1.0.11</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+1 -2
View File
@@ -1,5 +1,4 @@
using Telegrator.Handlers; using Telegrator.Handlers.Components;
using Telegrator.Handlers.Components;
namespace Telegrator.Aspects namespace Telegrator.Aspects
{ {
+1 -2
View File
@@ -1,5 +1,4 @@
using Telegrator.Handlers; using Telegrator.Handlers.Components;
using Telegrator.Handlers.Components;
namespace Telegrator.Aspects namespace Telegrator.Aspects
{ {
+28
View File
@@ -70,6 +70,33 @@
Sender Sender
} }
/// <summary>
/// Messages from where this filter was originated
/// </summary>
public enum FilterOrigin
{
/// <summary>
/// None, empty filter
/// </summary>
None,
/// <summary>
/// From <see cref="Attributes.UpdateHandlerAttribute{T}"/> update validator filter
/// </summary>
Validator,
/// <summary>
/// From <see cref="Attributes.StateKeeperAttribute{TKey, TState, TKeeper}"/> state machine filter
/// </summary>
StateKeeper,
/// <summary>
/// From regular <see cref="Attributes.UpdateFilterAttribute{T}"/>
/// </summary>
Regualr
}
/*
/// <summary> /// <summary>
/// Levels of debug writing /// Levels of debug writing
/// </summary> /// </summary>
@@ -101,4 +128,5 @@
/// </summary> /// </summary>
HandlersPool = 0x8 HandlersPool = 0x8
} }
*/
} }
@@ -1,10 +1,8 @@
using System.ComponentModel; using Telegram.Bot.Polling;
using System.Threading; using Telegram.Bot.Types;
using Telegram.Bot.Polling;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegrator.MadiatorCore; using Telegrator.Filters.Components;
using Telegrator.MadiatorCore.Descriptors; using Telegrator.MadiatorCore.Descriptors;
using Telegrator.Polling;
namespace Telegrator.Handlers.Components namespace Telegrator.Handlers.Components
{ {
@@ -102,5 +100,20 @@ namespace Telegrator.Handlers.Components
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected abstract Task<Result> ExecuteInternal(IHandlerContainer container, CancellationToken cancellationToken); protected abstract Task<Result> ExecuteInternal(IHandlerContainer container, CancellationToken cancellationToken);
/// <summary>
/// Handles failed filters during handler describing.
/// Use <see cref="Result"/> to control how router should treat this fail.
/// <see cref="Result.Ok"/> to silently continue decribing.
/// <see cref="Result.Fault"/> to stop\break decribing sequence.
/// </summary>
/// <param name="context"></param>
/// <param name="failedFilter"></param>
/// <param name="origin"></param>
/// <returns></returns>
public virtual Task<Result> FiltersFallback(FilterExecutionContext<Update> context, IFilter<Update> failedFilter, FilterOrigin origin)
{
return Task.FromResult(Result.Ok());
}
} }
} }
-59
View File
@@ -1,59 +0,0 @@
namespace Telegrator.Handlers
{
/// <summary>
/// Represents handler results, allowing to communicate with router and control aspect execution
/// </summary>
public sealed class Result
{
/// <summary>
/// Is result positive
/// </summary>
public bool Positive { get; }
/// <summary>
/// Should router search for next matching handler
/// </summary>
public bool RouteNext { get; }
/// <summary>
/// Exact type that router should search
/// </summary>
public Type? NextType { get; }
internal Result(bool positive, bool routeNext, Type? nextType)
{
Positive = positive;
RouteNext = routeNext;
NextType = nextType;
}
/// <summary>
/// "OK" result
/// </summary>
/// <returns></returns>
public static Result Ok()
=> new Result(true, false, null);
/// <summary>
/// "Somethong went wrong" result
/// </summary>
/// <returns></returns>
public static Result Fault()
=> new Result(false, false, null);
/// <summary>
/// "Search next handler" result
/// </summary>
/// <returns></returns>
public static Result Next()
=> new Result(true, true, null);
/// <summary>
/// "Search next handler of type" result
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Result Next<T>()
=> new Result(true, true, typeof(T));
}
}
@@ -1,5 +1,4 @@
using Telegrator.Aspects; using Telegrator.Aspects;
using Telegrator.Handlers;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
namespace Telegrator.MadiatorCore.Descriptors namespace Telegrator.MadiatorCore.Descriptors
@@ -41,14 +41,18 @@ namespace Telegrator.MadiatorCore.Descriptors
/// Validates the filter context using all filters in the set. /// Validates the filter context using all filters in the set.
/// </summary> /// </summary>
/// <param name="filterContext">The filter execution context.</param> /// <param name="filterContext">The filter execution context.</param>
/// <param name="failedFilter"></param>
/// <param name="origin"></param>
/// <returns>True if all filters pass; otherwise, false.</returns> /// <returns>True if all filters pass; otherwise, false.</returns>
public bool Validate(FilterExecutionContext<Update> filterContext) public bool Validate(FilterExecutionContext<Update> filterContext, out IFilter<Update> failedFilter, out FilterOrigin origin)
{ {
if (UpdateValidator != null) if (UpdateValidator != null)
{ {
if (!UpdateValidator.CanPass(filterContext)) if (!UpdateValidator.CanPass(filterContext))
{ {
Alligator.LogDebug("(E) UpdateValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id); Alligator.LogDebug("(E) UpdateValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id);
failedFilter = UpdateValidator;
origin = FilterOrigin.Validator;
return false; return false;
} }
@@ -61,6 +65,8 @@ namespace Telegrator.MadiatorCore.Descriptors
if (!StateKeeperValidator.CanPass(filterContext)) if (!StateKeeperValidator.CanPass(filterContext))
{ {
Alligator.LogDebug("(E) StateKeeperValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id); Alligator.LogDebug("(E) StateKeeperValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id);
failedFilter = StateKeeperValidator;
origin = FilterOrigin.StateKeeper;
return false; return false;
} }
@@ -77,6 +83,8 @@ namespace Telegrator.MadiatorCore.Descriptors
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter) if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
Alligator.LogDebug("(E) {0} filter of {1} for Update ({2}) didnt pass!", filter.GetType().Name, filterContext.Data["handler_name"], filterContext.Update.Id); Alligator.LogDebug("(E) {0} filter of {1} for Update ({2}) didnt pass!", filter.GetType().Name, filterContext.Data["handler_name"], filterContext.Update.Id);
failedFilter = filter;
origin = FilterOrigin.Regualr;
return false; return false;
} }
@@ -85,6 +93,8 @@ namespace Telegrator.MadiatorCore.Descriptors
} }
} }
failedFilter = null!;
origin = FilterOrigin.None;
return true; return true;
} }
} }
+1 -2
View File
@@ -1,5 +1,4 @@
using Telegrator.Handlers; using Telegrator.Logging;
using Telegrator.Logging;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors; using Telegrator.MadiatorCore.Descriptors;
+13 -4
View File
@@ -215,11 +215,18 @@ namespace Telegrator.Polling
{ "handler_name", descriptor.ToString() } { "handler_name", descriptor.ToString() }
}; };
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(_botInfo, update, update, data, []);
if (descriptor.Filters != null && !descriptor.Filters.Validate(filterContext))
return null;
UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken); UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken);
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(_botInfo, update, update, data, []);
if (descriptor.Filters != null && !descriptor.Filters.Validate(filterContext, out IFilter<Update> failedFilter, out FilterOrigin origin))
{
Result fallbackResult = handlerInstance.FiltersFallback(filterContext, failedFilter, origin).Result;
if (!fallbackResult.Positive)
throw new BreakDescribingException();
return null;
}
return new DescribedHandlerInfo(descriptor, this, AwaitingProvider, client, handlerInstance, filterContext, descriptor.DisplayString); return new DescribedHandlerInfo(descriptor, this, AwaitingProvider, client, handlerInstance, filterContext, descriptor.DisplayString);
} }
@@ -266,5 +273,7 @@ namespace Telegrator.Polling
} }
} }
} }
private class BreakDescribingException : Exception { }
} }
} }
+80
View File
@@ -0,0 +1,80 @@
using Telegram.Bot.Types;
using Telegrator.Aspects;
using Telegrator.Handlers.Components;
using Telegrator.Filters.Components;
using Telegrator.MadiatorCore;
namespace Telegrator
{
/// <summary>
/// Represents handler results, allowing to communicate with router and control aspect execution
/// </summary>
public sealed class Result
{
/// <summary>
/// Is result positive
/// </summary>
public bool Positive { get; }
/// <summary>
/// Should router search for next matching handler
/// </summary>
public bool RouteNext { get; }
/// <summary>
/// Exact type that router should search
/// </summary>
public Type? NextType { get; }
internal Result(bool positive, bool routeNext, Type? nextType)
{
Positive = positive;
RouteNext = routeNext;
NextType = nextType;
}
/// <summary>
/// Represents 'success'
/// <list type="bullet">
/// <item>Inside <see cref="IPreProcessor"/> - let handler's main block be executed</item>
/// <item>Inside <see cref="UpdateHandlerBase.FiltersFallback(FilterExecutionContext{Update}, IFilter{Update}, FilterOrigin)"/> - let router continue describing</item>
/// <item>Inside <see cref="UpdateHandlerBase.ExecuteInternal(IHandlerContainer, CancellationToken)"/> - tells <see cref="IUpdateRouter"/> that he can stop describing, as needed handler was found</item>
/// </list>
/// </summary>
/// <returns></returns>
public static Result Ok()
=> new Result(true, false, null);
/// <summary>
/// Represents 'fault' or 'error'. Use cases:
/// <list type="bullet">
/// <item>Inside <see cref="IPreProcessor"/> - interupts execution of handler, main block and <see cref="IPostProcessor"/> wont be executed</item>
/// <item>Inside <see cref="UpdateHandlerBase.FiltersFallback(FilterExecutionContext{Update}, IFilter{Update}, FilterOrigin)"/> - interupts router's describing sequence</item>
/// </list>
/// </summary>
/// <returns></returns>
public static Result Fault()
=> new Result(false, false, null);
/// <summary>
/// Represents 'continue'. Use cases:
/// <list type="bullet">
/// <item>Inside <see cref="UpdateHandlerBase.ExecuteInternal(IHandlerContainer, CancellationToken)"/> - Tells <see cref="IUpdateRouter"/> to continue describing handlers</item>
/// </list>
/// </summary>
/// <returns></returns>
public static Result Next()
=> new Result(true, true, null);
/// <summary>
/// Represents 'chain'. Use cases:
/// <list type="bullet">
/// <item>Inside <see cref="UpdateHandlerBase.ExecuteInternal(IHandlerContainer, CancellationToken)"/> - Tells <see cref="IUpdateRouter"/> to continue describing handlers and execute only handlers of exact type</item>
/// </list>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Result Next<T>()
=> new Result(true, true, typeof(T));
}
}
+1 -1
View File
@@ -17,7 +17,7 @@
<EnableNETAnalyzers>True</EnableNETAnalyzers> <EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<Version>1.0.10</Version> <Version>1.0.11</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>