From b14d848537545bbcfdabba9fab533123ee1c2e2e Mon Sep 17 00:00:00 2001 From: Rikitav Date: Sun, 3 Aug 2025 00:28:10 +0400 Subject: [PATCH] * Added new namespace "Aspects" and types dedicated for aspected handlers processing, including Pre\Post processors * Added new DescriptorAspectsSet class, used for mamanging and executing aspects of handlers * Processors can prevent handlers execution using Result.Fault() * Added field to DescribedHandlerInfo for getting descriptor from which this handler was described * Removed unused reflection extension methods --- .../TelegramBotWebOptions.cs | 1 - Telegrator/Aspects/AfterExecutionAttribute.cs | 8 +++ .../Aspects/BeforeExecutionAttribute.cs | 8 +++ Telegrator/Aspects/IPostProcessor.cs.cs | 13 ++++ Telegrator/Aspects/IPreProcessor.cs | 13 ++++ .../Attributes/UpdateHandlerAttribute.cs | 36 +++++++++-- .../Descriptors/DescribedHandlerInfo.cs | 30 ++++++++- .../Descriptors/DescriptorAspectsSet.cs | 63 +++++++++++++++++++ .../Descriptors/HandlerDescriptor.cs | 9 ++- .../Descriptors/HandlerInspector.cs | 25 ++++++++ Telegrator/Polling/UpdateRouter.cs | 4 +- Telegrator/Providers/HandlersCollection.cs | 2 +- Telegrator/TypesExtensions.cs | 2 + 13 files changed, 203 insertions(+), 11 deletions(-) create mode 100644 Telegrator/Aspects/AfterExecutionAttribute.cs create mode 100644 Telegrator/Aspects/BeforeExecutionAttribute.cs create mode 100644 Telegrator/Aspects/IPostProcessor.cs.cs create mode 100644 Telegrator/Aspects/IPreProcessor.cs create mode 100644 Telegrator/MadiatorCore/Descriptors/DescriptorAspectsSet.cs diff --git a/Telegrator.Hosting.Web/TelegramBotWebOptions.cs b/Telegrator.Hosting.Web/TelegramBotWebOptions.cs index 267ccb6..8a53d6f 100644 --- a/Telegrator.Hosting.Web/TelegramBotWebOptions.cs +++ b/Telegrator.Hosting.Web/TelegramBotWebOptions.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Options; -using System.Diagnostics.CodeAnalysis; namespace Telegrator.Hosting.Web { diff --git a/Telegrator/Aspects/AfterExecutionAttribute.cs b/Telegrator/Aspects/AfterExecutionAttribute.cs new file mode 100644 index 0000000..21be3da --- /dev/null +++ b/Telegrator/Aspects/AfterExecutionAttribute.cs @@ -0,0 +1,8 @@ +namespace Telegrator.Aspects +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class AfterExecutionAttribute : Attribute where T : IPostProcessor + { + public Type ProcessorType => typeof(T); + } +} diff --git a/Telegrator/Aspects/BeforeExecutionAttribute.cs b/Telegrator/Aspects/BeforeExecutionAttribute.cs new file mode 100644 index 0000000..582b54c --- /dev/null +++ b/Telegrator/Aspects/BeforeExecutionAttribute.cs @@ -0,0 +1,8 @@ +namespace Telegrator.Aspects +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class BeforeExecutionAttribute : Attribute where T : IPreProcessor + { + public Type ProcessorType => typeof(T); + } +} diff --git a/Telegrator/Aspects/IPostProcessor.cs.cs b/Telegrator/Aspects/IPostProcessor.cs.cs new file mode 100644 index 0000000..6e7eba1 --- /dev/null +++ b/Telegrator/Aspects/IPostProcessor.cs.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Telegrator.Handlers; +using Telegrator.Handlers.Components; + +namespace Telegrator.Aspects +{ + public interface IPostProcessor + { + public Task AfterExecution(IHandlerContainer container); + } +} diff --git a/Telegrator/Aspects/IPreProcessor.cs b/Telegrator/Aspects/IPreProcessor.cs new file mode 100644 index 0000000..d62c4d8 --- /dev/null +++ b/Telegrator/Aspects/IPreProcessor.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Telegrator.Handlers; +using Telegrator.Handlers.Components; + +namespace Telegrator.Aspects +{ + public interface IPreProcessor + { + public Task BeforeExecution(IHandlerContainer container); + } +} diff --git a/Telegrator/Attributes/UpdateHandlerAttribute.cs b/Telegrator/Attributes/UpdateHandlerAttribute.cs index 0a8beb4..222f4f7 100644 --- a/Telegrator/Attributes/UpdateHandlerAttribute.cs +++ b/Telegrator/Attributes/UpdateHandlerAttribute.cs @@ -9,10 +9,38 @@ namespace Telegrator.Attributes /// Provides a type-safe way to associate handler types with specific update types and importance settings. /// /// The type of the update handler that this attribute is applied to. - /// The type of update that this handler can process. - /// The importance level for this handler (default: 0 for unlimited). - public abstract class UpdateHandlerAttribute(UpdateType updateType, int importance = 0) - : UpdateHandlerAttributeBase([typeof(T)], updateType, importance) where T : UpdateHandlerBase + public abstract class UpdateHandlerAttribute : UpdateHandlerAttributeBase where T : UpdateHandlerBase { + /// + /// Initializes new instance of + /// + /// The type of update that this handler can process. + protected UpdateHandlerAttribute(UpdateType updateType) + : base([typeof(T)], updateType, 0) { } + + /// + /// Initializes new instance of + /// + /// The type of update that this handler can process. + /// The importance level for this handler + protected UpdateHandlerAttribute(UpdateType updateType, int importance) + : base([typeof(T)], updateType, importance) { } + + /// + /// Initializes new instance of + /// + /// Additional suported types. + /// The type of update that this handler can process. + protected UpdateHandlerAttribute(Type[] types, UpdateType updateType) + : base([..types, typeof(T)], updateType, 0) { } + + /// + /// Initializes new instance of + /// + /// Additional suported types. + /// The type of update that this handler can process. + /// The importance level for this handler + protected UpdateHandlerAttribute(Type[] types, UpdateType updateType, int importance) + : base([.. types, typeof(T)], updateType, importance) { } } } diff --git a/Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs b/Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs index b5ea82a..87df5c2 100644 --- a/Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs +++ b/Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs @@ -12,6 +12,11 @@ namespace Telegrator.MadiatorCore.Descriptors /// public class DescribedHandlerInfo { + /// + /// descriptor from that handler was described from + /// + public readonly HandlerDescriptor From; + /// /// The update router associated with this handler. /// @@ -60,13 +65,15 @@ namespace Telegrator.MadiatorCore.Descriptors /// /// Initializes a new instance of the class. /// + /// descriptor from that handler was described from /// The update router. /// The Telegram bot client. /// The handler instance. /// The filter execution context. /// Optional display string. - public DescribedHandlerInfo(IUpdateRouter updateRouter, ITelegramBotClient client, UpdateHandlerBase handlerInstance, FilterExecutionContext filterContext, string? displayString) + public DescribedHandlerInfo(HandlerDescriptor fromDescriptor, IUpdateRouter updateRouter, ITelegramBotClient client, UpdateHandlerBase handlerInstance, FilterExecutionContext filterContext, string? displayString) { + From = fromDescriptor; UpdateRouter = updateRouter; Client = client; HandlerInstance = handlerInstance; @@ -90,7 +97,26 @@ namespace Telegrator.MadiatorCore.Descriptors try { HandlerContainer = GetContainer(UpdateRouter.AwaitingProvider, this); - return await HandlerInstance.Execute(HandlerContainer, cancellationToken); + + if (From.Aspects != null) + { + Result preResult = await From.Aspects.ExecutePre(HandlerInstance, HandlerContainer); + if (!preResult.Positive) + return preResult; + } + + Result execResult = await HandlerInstance.Execute(HandlerContainer, cancellationToken); + if (!execResult.Positive) + return execResult; + + if (From.Aspects != null) + { + Result postResult = await From.Aspects.ExecutePost(HandlerInstance, HandlerContainer); + if (!postResult.Positive) + return postResult; + } + + return Result.Ok(); } catch (OperationCanceledException) { diff --git a/Telegrator/MadiatorCore/Descriptors/DescriptorAspectsSet.cs b/Telegrator/MadiatorCore/Descriptors/DescriptorAspectsSet.cs new file mode 100644 index 0000000..f852768 --- /dev/null +++ b/Telegrator/MadiatorCore/Descriptors/DescriptorAspectsSet.cs @@ -0,0 +1,63 @@ +using Telegrator.Aspects; +using Telegrator.Handlers; +using Telegrator.Handlers.Components; + +namespace Telegrator.MadiatorCore.Descriptors +{ + public sealed class DescriptorAspectsSet + { + public bool SelfPre { get; private set; } + + public bool SelfPost { get; private set; } + + public Type? TypedPre { get; private set; } + + public Type? TypedPost { get; private set; } + + public DescriptorAspectsSet(bool selfPre, Type? typedPre, bool selfPost, Type? typedPost) + { + SelfPre = selfPre; + SelfPost = selfPost; + TypedPre = typedPre; + TypedPost = typedPost; + } + + public async Task ExecutePre(UpdateHandlerBase handler, IHandlerContainer container) + { + if (SelfPre) + { + if (handler is not IPreProcessor preProcessor) + throw new InvalidOperationException(); + + return await preProcessor.BeforeExecution(container); + } + + if (TypedPre != null) + { + IPreProcessor preProcessor = (IPreProcessor)Activator.CreateInstance(TypedPre); + return await preProcessor.BeforeExecution(container); + } + + return Result.Ok(); + } + + public async Task ExecutePost(UpdateHandlerBase handler, IHandlerContainer container) + { + if (SelfPost) + { + if (handler is not IPostProcessor postProcessor) + throw new InvalidOperationException(); + + return await postProcessor.AfterExecution(container); + } + + if (TypedPost != null) + { + IPostProcessor postProcessor = (IPostProcessor)Activator.CreateInstance(TypedPost); + return await postProcessor.AfterExecution(container); + } + + return Result.Ok(); + } + } +} diff --git a/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs b/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs index 8f9de2b..37c7e84 100644 --- a/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs +++ b/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs @@ -76,7 +76,13 @@ namespace Telegrator.MadiatorCore.Descriptors /// /// The set of filters associated with this handler. /// - public DescriptorFiltersSet Filters + public DescriptorFiltersSet? Filters + { + get; + protected set; + } + + public DescriptorAspectsSet? Aspects { get; protected set; @@ -154,6 +160,7 @@ namespace Telegrator.MadiatorCore.Descriptors UpdateType = handlerAttribute.Type; Indexer = handlerAttribute.GetIndexer(); Filters = new DescriptorFiltersSet(handlerAttribute, stateKeeperAttribute, filters); + Aspects = HandlerInspector.GetAspects(handlerType); DisplayString = HandlerInspector.GetDisplayName(handlerType); } diff --git a/Telegrator/MadiatorCore/Descriptors/HandlerInspector.cs b/Telegrator/MadiatorCore/Descriptors/HandlerInspector.cs index 2205d48..8d593e5 100644 --- a/Telegrator/MadiatorCore/Descriptors/HandlerInspector.cs +++ b/Telegrator/MadiatorCore/Descriptors/HandlerInspector.cs @@ -2,6 +2,7 @@ using System.Reflection; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; +using Telegrator.Aspects; using Telegrator.Attributes.Components; using Telegrator.Filters.Components; @@ -80,5 +81,29 @@ namespace Telegrator.MadiatorCore.Descriptors } } } + + public static DescriptorAspectsSet GetAspects(Type handlerType) + { + bool selfPre = handlerType.GetInterface(nameof(IPreProcessor)) != null; + bool selfPost = handlerType.GetInterface(nameof(IPostProcessor)) != null; + Type? typedPre = null; + Type? typedPost = null; + + if (!selfPre) + { + Attribute? preAttr = handlerType.GetCustomAttribute(typeof(BeforeExecutionAttribute<>)); + if (preAttr != null) + typedPre = preAttr.GetType().GetGenericArguments()[0]; + } + + if (!selfPost) + { + Attribute? postAttr = handlerType.GetCustomAttribute(typeof(AfterExecutionAttribute<>)); + if (postAttr != null) + typedPre = postAttr.GetType().GetGenericArguments()[0]; + } + + return new DescriptorAspectsSet(selfPre, typedPre, selfPost, typedPost); + } } } diff --git a/Telegrator/Polling/UpdateRouter.cs b/Telegrator/Polling/UpdateRouter.cs index f75f49e..5ca4e89 100644 --- a/Telegrator/Polling/UpdateRouter.cs +++ b/Telegrator/Polling/UpdateRouter.cs @@ -223,11 +223,11 @@ namespace Telegrator.Polling }; FilterExecutionContext filterContext = new FilterExecutionContext(_botInfo, update, update, data, []); - if (!descriptor.Filters.Validate(filterContext)) + if (descriptor.Filters != null && !descriptor.Filters.Validate(filterContext)) return null; UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken); - return new DescribedHandlerInfo(updateRouter, client, handlerInstance, filterContext, descriptor.DisplayString); + return new DescribedHandlerInfo(descriptor, updateRouter, client, handlerInstance, filterContext, descriptor.DisplayString); } /// diff --git a/Telegrator/Providers/HandlersCollection.cs b/Telegrator/Providers/HandlersCollection.cs index 0ecb87d..ff67aaf 100644 --- a/Telegrator/Providers/HandlersCollection.cs +++ b/Telegrator/Providers/HandlersCollection.cs @@ -68,7 +68,7 @@ namespace Telegrator.Providers public virtual IHandlersCollection AddDescriptor(HandlerDescriptor descriptor) { if (MustHaveParameterlessCtor && !descriptor.HandlerType.HasParameterlessCtor()) - throw new Exception(); + throw new Exception("This handler (" + descriptor.HandlerType.FullName + "), must contain constructor without parameters."); _allowedTypes.Union(descriptor.UpdateType); MightAwaitAttribute[] mightAwaits = descriptor.HandlerType.GetCustomAttributes().ToArray(); diff --git a/Telegrator/TypesExtensions.cs b/Telegrator/TypesExtensions.cs index 5755475..7755354 100644 --- a/Telegrator/TypesExtensions.cs +++ b/Telegrator/TypesExtensions.cs @@ -886,6 +886,7 @@ namespace Telegrator public static bool HasParameterlessCtor(this Type type) => type.GetConstructors().Any(ctor => ctor.GetParameters().Length == 0); + /* /// /// Invokes a "" method of an object /// @@ -948,6 +949,7 @@ namespace Telegrator /// public static T? InvokeGenericMethod(this object obj, string methodName, Type[] genericParameters, params object[]? parameters) => (T?)obj.GetType().GetMethod(methodName).InvokeGenericMethod(obj, genericParameters, parameters); + */ /// /// Checks is has public properties