Repository navigation
Improve validations for explicit topologies #9986
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2f65f83
Imprve validations for explicit topologies
PascalSenn 39de861
Merge branch 'main' into pse/mocha-validations
PascalSenn ef4ee15
Potential fix for pull request finding
PascalSenn 3837c95
Merge branch 'main' into pse/mocha-validations
PascalSenn f6f5164
Refactor RabbitMQ topology tests to suppress convention entities unde…
PascalSenn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
184 changes: 184 additions & 0 deletions
184
src/Mocha/src/Mocha/Builder/MessageBusBuilder.Validation.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| using System.Text; | ||
|
|
||
| namespace Mocha; | ||
|
|
||
| public partial class MessageBusBuilder | ||
| { | ||
| private static void ValidateInboundRoutesAreBound(IMessagingSetupContext context) | ||
| { | ||
| var unboundRoutes = context.Router.InboundRoutes | ||
| .Where(route => | ||
| route is { IsInitialized: true, IsCompleted: false, Endpoint: null, Kind: not InboundRouteKind.Reply }) | ||
| .OrderBy(route => route.Consumer?.Name, StringComparer.Ordinal) | ||
| .ThenBy(route => route.MessageType?.RuntimeType.FullName, StringComparer.Ordinal) | ||
| .ThenBy(route => route.Kind) | ||
| .ToArray(); | ||
|
|
||
| if (unboundRoutes.Length == 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| throw new InvalidOperationException(context.FormatUnboundInboundRoutes(unboundRoutes)); | ||
| } | ||
| } | ||
|
|
||
| file static class Extensions | ||
| { | ||
| public static string FormatUnboundInboundRoutes( | ||
| this IMessagingSetupContext context, | ||
| IReadOnlyList<InboundRoute> routes) | ||
| { | ||
| var sb = new StringBuilder(); | ||
| sb.Append("The message bus configuration has "); | ||
| sb.Append(routes.Count); | ||
| sb.Append(routes.Count == 1 ? " unbound inbound route." : " unbound inbound routes."); | ||
| sb.AppendLine(); | ||
| sb.AppendLine("Each route below was registered but never attached to a receive endpoint."); | ||
|
|
||
| for (var i = 0; i < routes.Count; i++) | ||
| { | ||
| var route = routes[i]; | ||
| sb.AppendLine(); | ||
| sb.Append('['); | ||
| sb.Append(i + 1); | ||
| sb.Append("] "); | ||
| sb.Append(route.GetConsumerName()); | ||
| sb.Append(" ("); | ||
| sb.Append(route.GetConsumerType()); | ||
| sb.AppendLine(")"); | ||
|
|
||
| sb.Append(" message : "); | ||
| sb.AppendLine(route.GetMessage()); | ||
| sb.Append(" kind : "); | ||
| sb.AppendLine(route.Kind.ToString()); | ||
| sb.Append(" where : "); | ||
| sb.AppendLine(route.GetCondition()); | ||
| sb.Append(" fix : "); | ||
| sb.AppendLine(route.GetHint(context)); | ||
| } | ||
|
|
||
| return sb.ToString(); | ||
| } | ||
|
|
||
| private static string GetHint(this InboundRoute route, IMessagingSetupContext context) | ||
| { | ||
| var sameMessageRoute = route.FindBoundRouteForSameMessage(context); | ||
| if (sameMessageRoute is not null) | ||
| { | ||
| return "Another route for this message is already bound to consumer " | ||
| + $"'{sameMessageRoute.GetConsumerName()}' on receive endpoint " | ||
| + $"'{sameMessageRoute.Endpoint!.Name}' for transport " | ||
| + $"'{sameMessageRoute.Endpoint.Transport.Name}'. " | ||
| + "If this consumer is unintended, remove its registration. " | ||
| + "If it should receive independently, bind it to its own receive endpoint."; | ||
| } | ||
|
|
||
| var transportHint = GetTransportHint(context); | ||
| var endpointHint = GetEndpointHint(context); | ||
|
|
||
| return transportHint | ||
| + " If this consumer should receive over a transport, bind it to a receive endpoint. " | ||
| + "If it is handled in-process only or superseded, remove it from message bus registration. " | ||
| + endpointHint; | ||
| } | ||
|
|
||
| private static InboundRoute? FindBoundRouteForSameMessage(this InboundRoute route, IMessagingSetupContext context) | ||
| { | ||
| if (route.MessageType is not { } messageType) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| return context.Router | ||
| .GetInboundByMessageType(messageType) | ||
| .Where(candidate => | ||
| !ReferenceEquals(candidate, route) | ||
| && candidate.Kind == route.Kind | ||
| && candidate.Endpoint is not null) | ||
| .OrderBy(candidate => candidate.Consumer?.Name, StringComparer.Ordinal) | ||
| .ThenBy(candidate => candidate.Endpoint?.Name, StringComparer.Ordinal) | ||
| .FirstOrDefault(); | ||
| } | ||
|
|
||
| private static string GetTransportHint(IMessagingSetupContext context) | ||
| { | ||
| if (context.Transports.Length == 0) | ||
| { | ||
| return "No messaging transports are configured, so no receive endpoint can be discovered."; | ||
| } | ||
|
|
||
| if (context.Transports.Length == 1) | ||
| { | ||
| var transport = context.Transports[0]; | ||
| return transport.BindMode == MessagingBindMode.Explicit | ||
| ? $"Transport '{transport.Name}' uses explicit bind mode, so no convention receive endpoint is created for unbound consumers." | ||
| : $"Transport '{transport.Name}' uses implicit bind mode, but it did not connect this route."; | ||
| } | ||
|
|
||
| var transports = string.Join( | ||
| ", ", | ||
| context.Transports.Select(static t => $"'{t.Name}' ({t.BindMode})")); | ||
|
|
||
| return $"No configured transport connected this route. Configured transports: {transports}."; | ||
| } | ||
|
|
||
| private static string GetEndpointHint(IMessagingSetupContext context) | ||
| { | ||
| var endpoints = context.Transports | ||
| .Select(static transport => new | ||
| { | ||
| Transport = transport.Name, | ||
| Endpoints = transport.ReceiveEndpoints | ||
| .Where(static endpoint => endpoint.Kind == ReceiveEndpointKind.Default) | ||
| .Select(static endpoint => endpoint.Name) | ||
| .Order(StringComparer.Ordinal) | ||
| .ToArray() | ||
| }) | ||
| .Where(static transport => transport.Endpoints.Length > 0) | ||
| .OrderBy(static transport => transport.Transport, StringComparer.Ordinal) | ||
| .ToArray(); | ||
|
|
||
| if (endpoints.Length == 0) | ||
| { | ||
| return "Available default receive endpoints: none."; | ||
| } | ||
|
|
||
| return "Available default receive endpoints: " | ||
| + string.Join("; ", endpoints.Select(static t => $"{t.Transport}: {string.Join(", ", t.Endpoints)}")) | ||
| + "."; | ||
| } | ||
|
|
||
| private static string GetConsumerName(this InboundRoute route) | ||
| => route.Consumer?.Name ?? "(unknown consumer)"; | ||
|
|
||
| private static string GetConsumerType(this InboundRoute route) | ||
| => route.Consumer?.Identity.FullName | ||
| ?? route.Consumer?.Identity.Name | ||
| ?? route.Consumer?.GetType().FullName | ||
| ?? "(unknown consumer type)"; | ||
|
|
||
| private static string GetMessage(this InboundRoute route) | ||
| { | ||
| if (route.MessageType is null) | ||
| { | ||
| return "(no message type)"; | ||
| } | ||
|
|
||
| return route.MessageType.Identity + " (" + route.MessageType.RuntimeType.FullName + ")"; | ||
| } | ||
|
|
||
| private static string GetCondition(this InboundRoute route) | ||
| => FormatCondition(route.Condition.Describe()); | ||
|
|
||
| private static string FormatCondition(RouteConditionDescription condition) | ||
| { | ||
| var text = condition.Detail is null | ||
| ? condition.Kind | ||
| : $"{condition.Kind}({condition.Detail})"; | ||
|
|
||
| return condition.Children.Count == 0 | ||
| ? text | ||
| : text + "[" + string.Join(", ", condition.Children.Select(FormatCondition)) + "]"; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.