From dbe488092aa89aedc7f66f1ee437e949152eaa1c Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Fri, 17 Mar 2023 12:55:53 -0500 Subject: [PATCH] decent textual representation of handler discovery rules in describe output. Closes GH-236 --- .../OrderSagaSample/OrderSagaSample.csproj | 5 +- .../HandlerDiscovery.Diagnostics.cs | 12 ++-- .../Configuration/HandlerDiscovery.cs | 56 +++++++++--------- .../HandlerGraph.ISystemDescribedPart.cs | 58 ++++++++++++++++++- 4 files changed, 93 insertions(+), 38 deletions(-) diff --git a/src/Samples/OrderSagaSample/OrderSagaSample.csproj b/src/Samples/OrderSagaSample/OrderSagaSample.csproj index e958de177..34fece35d 100644 --- a/src/Samples/OrderSagaSample/OrderSagaSample.csproj +++ b/src/Samples/OrderSagaSample/OrderSagaSample.csproj @@ -6,9 +6,8 @@ - - - + + diff --git a/src/Wolverine/Configuration/HandlerDiscovery.Diagnostics.cs b/src/Wolverine/Configuration/HandlerDiscovery.Diagnostics.cs index 6217357f4..ae5af9069 100644 --- a/src/Wolverine/Configuration/HandlerDiscovery.Diagnostics.cs +++ b/src/Wolverine/Configuration/HandlerDiscovery.Diagnostics.cs @@ -29,13 +29,13 @@ internal string DescribeHandlerMatch(WolverineOptions options, Type candidateTyp } bool typeNotFound = false; - if (!_handlerQuery.Includes.Matches(candidateType)) + if (!HandlerQuery.Includes.Matches(candidateType)) { writeTypeIncludeMiss(candidateType, writer); typeNotFound = true; } - if (_handlerQuery.Excludes.Matches(candidateType)) + if (HandlerQuery.Excludes.Matches(candidateType)) { writeTypeExcludeMatch(candidateType, writer); typeNotFound = true; @@ -57,7 +57,7 @@ internal string DescribeHandlerMatch(WolverineOptions options, Type candidateTyp { writer.WriteLine($"Method: {method.Name}({method.GetParameters().Select(x => x.ParameterType.ShortNameInCode()).Join(", ")})" ); - foreach (var filter in _methodIncludes) + foreach (var filter in MethodIncludes) { if (filter.Matches(method)) { @@ -69,7 +69,7 @@ internal string DescribeHandlerMatch(WolverineOptions options, Type candidateTyp } } - foreach (var filter in _methodExcludes) + foreach (var filter in MethodExcludes) { if (filter.Matches(method)) { @@ -90,7 +90,7 @@ internal string DescribeHandlerMatch(WolverineOptions options, Type candidateTyp private void writeTypeExcludeMatch(Type candidateType, StringWriter writer) { - foreach (var filter in _handlerQuery.Excludes) + foreach (var filter in HandlerQuery.Excludes) { if (filter.Matches(candidateType)) { @@ -105,7 +105,7 @@ private void writeTypeExcludeMatch(Type candidateType, StringWriter writer) private void writeTypeIncludeMiss(Type candidateType, StringWriter writer) { - foreach (var filter in _handlerQuery.Includes) + foreach (var filter in HandlerQuery.Includes) { if (filter.Matches(candidateType)) { diff --git a/src/Wolverine/Configuration/HandlerDiscovery.cs b/src/Wolverine/Configuration/HandlerDiscovery.cs index c77e76647..a8179ef57 100644 --- a/src/Wolverine/Configuration/HandlerDiscovery.cs +++ b/src/Wolverine/Configuration/HandlerDiscovery.cs @@ -13,9 +13,6 @@ public sealed partial class HandlerDiscovery { private readonly IList _explicitTypes = new List(); - private readonly CompositeFilter _methodIncludes = new(); - private readonly CompositeFilter _methodExcludes = new(); - private readonly string[] _validMethods = { HandlerChain.Handle, HandlerChain.Handles, HandlerChain.Consume, HandlerChain.Consumes, SagaChain.Orchestrate, @@ -25,7 +22,6 @@ public sealed partial class HandlerDiscovery private bool _conventionalDiscoveryDisabled; - private readonly TypeQuery _handlerQuery = new(TypeClassification.Concretes | TypeClassification.Closed); private readonly TypeQuery _messageQuery = new(TypeClassification.Concretes | TypeClassification.Closed); public HandlerDiscovery() @@ -40,48 +36,54 @@ public HandlerDiscovery() _messageQuery.Excludes.IsNotPublic(); } + internal CompositeFilter MethodIncludes { get; } = new(); + + internal CompositeFilter MethodExcludes { get; } = new(); + private void specifyHandlerMethodRules() { foreach (var methodName in _validMethods) { - _methodIncludes.WithCondition($"Method name is '{methodName}' (case sensitive)", m => m.Name == methodName); + MethodIncludes.WithCondition($"Method name is '{methodName}' (case sensitive)", m => m.Name == methodName); var asyncName = methodName + "Async"; - _methodIncludes.WithCondition($"Method name is '{asyncName}' (case sensitive)", m => m.Name == asyncName); + MethodIncludes.WithCondition($"Method name is '{asyncName}' (case sensitive)", m => m.Name == asyncName); } - _methodIncludes.WithCondition("Has attribute [WolverineHandler]", m => m.HasAttribute()); + MethodIncludes.WithCondition("Has attribute [WolverineHandler]", m => m.HasAttribute()); - _methodExcludes.WithCondition("Method is declared by object", method => method.DeclaringType == typeof(object)); - _methodExcludes.WithCondition("IDisposable.Dispose()", method => method.Name == nameof(IDisposable.Dispose)); - _methodExcludes.WithCondition("IAsyncDisposable.DisposeAsync()", + MethodExcludes.WithCondition("Method is declared by object", method => method.DeclaringType == typeof(object)); + MethodExcludes.WithCondition("IDisposable.Dispose()", method => method.Name == nameof(IDisposable.Dispose)); + MethodExcludes.WithCondition("IAsyncDisposable.DisposeAsync()", method => method.Name == nameof(IAsyncDisposable.DisposeAsync)); - _methodExcludes.WithCondition("Contains Generic Parameters", method => method.ContainsGenericParameters); - _methodExcludes.WithCondition("Special Name", method => method.IsSpecialName); - _methodExcludes.WithCondition("Has attribute [WolverineIgnore]", + MethodExcludes.WithCondition("Contains Generic Parameters", method => method.ContainsGenericParameters); + MethodExcludes.WithCondition("Special Name", method => method.IsSpecialName); + MethodExcludes.WithCondition("Has attribute [WolverineIgnore]", method => method.HasAttribute()); - _methodExcludes.WithCondition("Has no arguments", m => !m.GetParameters().Any()); + MethodExcludes.WithCondition("Has no arguments", m => !m.GetParameters().Any()); - _methodExcludes.WithCondition("Cannot determine a valid message type",m => m.MessageType() == null); + MethodExcludes.WithCondition("Cannot determine a valid message type",m => m.MessageType() == null); - _methodExcludes.WithCondition("Returns a primitive type", m => m.ReturnType != typeof(void) && m.ReturnType.IsPrimitive); + MethodExcludes.WithCondition("Returns a primitive type", m => m.ReturnType != typeof(void) && m.ReturnType.IsPrimitive); } private void specifyHandlerDiscovery() { - _handlerQuery.Includes.WithNameSuffix(HandlerChain.HandlerSuffix); - _handlerQuery.Includes.WithNameSuffix(HandlerChain.ConsumerSuffix); - _handlerQuery.Includes.Implements(); - _handlerQuery.Includes.Implements(); - _handlerQuery.Includes.WithAttribute(); - - _handlerQuery.Excludes.WithCondition("Is not a public type", t => isNotPublicType(t)); - _handlerQuery.Excludes.WithAttribute(); + HandlerQuery.Includes.WithNameSuffix(HandlerChain.HandlerSuffix); + HandlerQuery.Includes.WithNameSuffix(HandlerChain.ConsumerSuffix); + HandlerQuery.Includes.Implements(); + HandlerQuery.Includes.Implements(); + HandlerQuery.Includes.WithAttribute(); + + HandlerQuery.Excludes.WithCondition("Is not a public type", t => isNotPublicType(t)); + HandlerQuery.Excludes.WithAttribute(); } + internal TypeQuery HandlerQuery { get; } = new(TypeClassification.Concretes | TypeClassification.Closed); + private static bool isNotPublicType(Type type) { if (type.IsPublic) return false; @@ -106,7 +108,7 @@ public HandlerDiscovery CustomizeHandlerDiscovery(Action configure) throw new ArgumentNullException(nameof(configure)); } - configure(_handlerQuery); + configure(HandlerQuery); return this; } @@ -156,7 +158,7 @@ internal IEnumerable findAllMessages(HandlerGraph handlers) Assemblies.Fill(options.ApplicationAssembly); } - return _handlerQuery.Find(Assemblies) + return HandlerQuery.Find(Assemblies) .Concat(_explicitTypes) .Distinct() .SelectMany(actionsFromType).ToArray(); @@ -166,7 +168,7 @@ internal IEnumerable findAllMessages(HandlerGraph handlers) { return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static) .Where(x => x.DeclaringType != typeof(object)).ToArray() - .Where(m => _methodIncludes.Matches(m) && !_methodExcludes.Matches(m)) + .Where(m => MethodIncludes.Matches(m) && !MethodExcludes.Matches(m)) .Select(m => (type, m)); } diff --git a/src/Wolverine/Runtime/Handlers/HandlerGraph.ISystemDescribedPart.cs b/src/Wolverine/Runtime/Handlers/HandlerGraph.ISystemDescribedPart.cs index 293a76147..787fb406d 100644 --- a/src/Wolverine/Runtime/Handlers/HandlerGraph.ISystemDescribedPart.cs +++ b/src/Wolverine/Runtime/Handlers/HandlerGraph.ISystemDescribedPart.cs @@ -27,13 +27,29 @@ await writer.WriteLineAsync( string IDescribedSystemPart.Title => "Wolverine Handlers"; Task IWriteToConsole.WriteToConsole() + { + writeHandlerDiscoveryRules(); + + if (Chains.Any()) + { + writeHandlerTable(); + } + else + { + AnsiConsole.Write("[yellow]No message handlers were discovered, you may want to review the discovery rules above.[/]"); + } + + return Task.CompletedTask; + } + + private void writeHandlerTable() { var table = new Table(); table.AddColumn("Message Name"); table.AddColumn("[bold]Message Type[/]\n [dim]namespace[/]", c => c.NoWrap = true); table.AddColumn("[bold]Handler.Method()[/]\n [dim]namespace[/]", c => c.NoWrap = true); table.AddColumn("Generated Type Name"); - + foreach (var chain in Chains) { var messageType = $"[bold]{chain.MessageType.NameInCode()}[/]\n [dim]{chain.MessageType.Namespace}[/]"; @@ -46,7 +62,45 @@ Task IWriteToConsole.WriteToConsole() } AnsiConsole.Render(table); + } - return Task.CompletedTask; + private void writeHandlerDiscoveryRules() + { + var tree = new Tree("Handler Discovery Rules"); + var assemblies = tree.AddNode("Assemblies"); + foreach (var assembly in Discovery.Assemblies) + { + assemblies.AddNode(assembly.GetName().Name.EscapeMarkup()); + } + + var typeRules = tree.AddNode("Handler Type Rules"); + var includedNode = typeRules.AddNode("Include:"); + foreach (var filter in Discovery.HandlerQuery.Includes) + { + includedNode.AddNode(filter.Description.EscapeMarkup()); + } + + var excludedNode = typeRules.AddNode("Exclude:"); + foreach (var exclude in Discovery.HandlerQuery.Excludes) + { + excludedNode.AddNode(exclude.Description.EscapeMarkup()); + } + + var methodRules = tree.AddNode("Handler Method Rules"); + var includedMethods = methodRules.AddNode("Include:"); + foreach (var include in Discovery.MethodIncludes) + { + includedMethods.AddNode(include.Description.EscapeMarkup()); + } + + var excludedMethods = methodRules.AddNode("Exclude:"); + foreach (var filter in Discovery.MethodExcludes) + { + excludedMethods.AddNode(filter.Description.EscapeMarkup()); + } + + AnsiConsole.Write(tree); + + AnsiConsole.WriteLine(); } } \ No newline at end of file