Skip to content

Commit

Permalink
Add support for routing messages based on attribute decoration
Browse files Browse the repository at this point in the history
  • Loading branch information
patchoulish authored and jeremydmiller committed Dec 2, 2024
1 parent a78f715 commit 5e5d77b
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 8 deletions.
4 changes: 3 additions & 1 deletion src/Testing/CoreTests/Runtime/Green/Messages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ public class GreenMessage1;

public class GreenMessage2;

public class GreenMessage3;
public class GreenMessage3;

public class GreenAttribute : Attribute;
11 changes: 10 additions & 1 deletion src/Testing/CoreTests/Runtime/Red/Messages.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
namespace CoreTests.Runtime.Red;

[Red]
public class RedMessage1;

[Crimson]
public class RedMessage2;

public class RedMessage3;
[Burgundy]
public class RedMessage3;

public class RedAttribute : Attribute;

public class CrimsonAttribute : RedAttribute;

public class BurgundyAttribute : RedAttribute;
44 changes: 43 additions & 1 deletion src/Testing/CoreTests/Runtime/SubscriptionTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ public void description_of_type_name_rule()
rule.ToString().ShouldBe("Message name is 'CoreTests.Runtime.RandomClass'");
}

[Fact]
public void description_of_attribute_rule()
{
var rule = new Subscription
{
BaseOrAttributeType = typeof(RandomClassAttribute),
Scope = RoutingScope.Attribute
};
rule.ToString().ShouldBe("Message type is decorated with 'CoreTests.Runtime.RandomClassAttribute' or a derived type");
}

[Fact]
public void description_of_all_types()
{
Expand Down Expand Up @@ -85,6 +96,20 @@ public void positive_assembly_test()
rule.Matches(typeof(DeleteUser)).ShouldBeTrue();
}

[Fact]
public void negative_attribute_test()
{
var rule = new Subscription
{
Scope = RoutingScope.Attribute,
BaseOrAttributeType = typeof(GreenAttribute)
};

rule.Matches(typeof(GreenMessage1)).ShouldBeFalse();
rule.Matches(typeof(GreenMessage2)).ShouldBeFalse();
rule.Matches(typeof(GreenMessage3)).ShouldBeFalse();
}

[Fact]
public void positive_namespace_test()
{
Expand All @@ -98,6 +123,23 @@ public void positive_namespace_test()
rule.Matches(typeof(RedMessage2)).ShouldBeTrue();
rule.Matches(typeof(RedMessage3)).ShouldBeTrue();
}

[Fact]
public void positive_attribute_test()
{
var rule = new Subscription
{
Scope = RoutingScope.Attribute,
BaseOrAttributeType = typeof(RedAttribute)
};

rule.Matches(typeof(RedMessage1)).ShouldBeTrue();
rule.Matches(typeof(RedMessage2)).ShouldBeTrue();
rule.Matches(typeof(RedMessage3)).ShouldBeTrue();
}
}

public class RandomClass;
[RandomClass]
public class RandomClass;

public class RandomClassAttribute : Attribute;
31 changes: 30 additions & 1 deletion src/Wolverine/Configuration/PublishingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,35 @@ public PublishingExpression MessagesFromAssemblyContaining<T>()
return MessagesFromAssembly(typeof(T).Assembly);
}

/// <summary>
/// Create a publishing rule for all messages decorated with the specified attribute type or a derived type
/// </summary>
/// <param name="attributeType"></param>
/// <returns></returns>
public PublishingExpression MessagesDecoratedWith(Type attributeType)
{
AutoAddSubscriptions = true;

_subscriptions.Add(new Subscription()
{
Scope = RoutingScope.Attribute,
BaseOrAttributeType = attributeType,
});

return this;
}

/// <summary>
/// Create a publishing rule for all messages decorated with the attribute of type T or a derived type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public PublishingExpression MessagesDecoratedWith<T>()
where T : Attribute
{
return MessagesDecoratedWith(typeof(T));
}

internal void AttachSubscriptions()
{
if (!_endpoints.Any())
Expand All @@ -182,6 +211,6 @@ internal void AddSubscriptionForAllMessages()
/// <typeparam name="T"></typeparam>
public void MessagesImplementing<T>()
{
_subscriptions.Add(new Subscription { BaseType = typeof(T), Scope = RoutingScope.Implements });
_subscriptions.Add(new Subscription { BaseOrAttributeType = typeof(T), Scope = RoutingScope.Implements });
}
}
3 changes: 2 additions & 1 deletion src/Wolverine/Runtime/Routing/RoutingScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public enum RoutingScope
Type,
TypeName,
All,
Implements
Implements,
Attribute
}
11 changes: 9 additions & 2 deletions src/Wolverine/Runtime/Routing/Subscription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ public string[] ContentTypes
/// </summary>
public string Match { get; init; } = string.Empty;

public Type? BaseType { get; init; }
/// <summary>
/// A base type if matching on implementation or an attribute type if matching on decoration
/// </summary>
public Type? BaseOrAttributeType { get; init; }

/// <summary>
/// Create a subscription for a specific message type
Expand Down Expand Up @@ -80,7 +83,8 @@ public bool Matches(Type type)
RoutingScope.Type => type.Name.EqualsIgnoreCase(Match) || type.FullName!.EqualsIgnoreCase(Match) ||
type.ToMessageTypeName().EqualsIgnoreCase(Match),
RoutingScope.TypeName => type.ToMessageTypeName().EqualsIgnoreCase(Match),
RoutingScope.Implements => type.CanBeCastTo(BaseType!),
RoutingScope.Implements => type.CanBeCastTo(BaseOrAttributeType!),
RoutingScope.Attribute => type.IsDefined(BaseOrAttributeType!, inherit: false),
_ => !type.CanBeCastTo<IAgentCommand>()
};
}
Expand Down Expand Up @@ -140,6 +144,9 @@ public override string ToString()

case RoutingScope.TypeName:
return $"Message name is '{Match}'";

case RoutingScope.Attribute:
return $"Message type is decorated with '{BaseOrAttributeType?.FullName}' or a derived type";
}

throw new ArgumentOutOfRangeException();
Expand Down
2 changes: 1 addition & 1 deletion src/Wolverine/Transports/Local/LocalTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public LocalTransport() : base(TransportConstants.Local, "Local (In Memory)")
var agentQueue = _queues[TransportConstants.Agents];
agentQueue.TelemetryEnabled = false;
agentQueue.Subscriptions.Add(new Subscription
{ Scope = RoutingScope.Implements, BaseType = typeof(IAgentCommand) });
{ Scope = RoutingScope.Implements, BaseOrAttributeType = typeof(IAgentCommand) });
agentQueue.ExecutionOptions.MaxDegreeOfParallelism = 20;
agentQueue.Role = EndpointRole.System;
agentQueue.Mode = EndpointMode.BufferedInMemory;
Expand Down

0 comments on commit 5e5d77b

Please sign in to comment.