Skip to content

Commit ebf14fa

Browse files
Copilotdandrejvv
andcommitted
Add IMessageBus interface and composite message bus infrastructure to Eventing.Contracts
Co-authored-by: dandrejvv <[email protected]>
1 parent 392fc3c commit ebf14fa

12 files changed

+643
-8
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Intent.Engine;
4+
using Intent.Modules.Common;
5+
using Intent.Modules.Common.CSharp.Builder;
6+
using Intent.Modules.Common.CSharp.Templates;
7+
using Intent.Modules.Common.Templates;
8+
using Intent.RoslynWeaver.Attributes;
9+
using Intent.Templates;
10+
11+
[assembly: DefaultIntentManaged(Mode.Fully)]
12+
[assembly: IntentTemplate("Intent.ModuleBuilder.CSharp.Templates.CSharpTemplatePartial", Version = "1.0")]
13+
14+
namespace Intent.Modules.Eventing.Contracts.Templates.CompositeMessageBus
15+
{
16+
[IntentManaged(Mode.Fully, Body = Mode.Merge)]
17+
public partial class CompositeMessageBusTemplate : CSharpTemplateBase<object>, ICSharpFileBuilderTemplate
18+
{
19+
public const string TemplateId = "Intent.Eventing.Contracts.CompositeMessageBus";
20+
21+
[IntentManaged(Mode.Fully, Body = Mode.Ignore)]
22+
public CompositeMessageBusTemplate(IOutputTarget outputTarget, object model = null) : base(TemplateId, outputTarget, model)
23+
{
24+
CSharpFile = new CSharpFile(this.GetNamespace(), this.GetFolderPath())
25+
.AddUsing("System")
26+
.AddUsing("System.Collections.Generic")
27+
.AddUsing("System.Linq")
28+
.AddUsing("System.Threading")
29+
.AddUsing("System.Threading.Tasks")
30+
.AddClass("CompositeMessageBus", @class =>
31+
{
32+
@class.ImplementsInterface(this.GetMessageBusInterfaceName());
33+
@class.AddField(this.GetMessageBrokerResolverName(), "_resolver", field => field.PrivateReadOnly());
34+
@class.AddField($"HashSet<{this.GetMessageBusInterfaceName()}>", "_messageBusProviders", field =>
35+
field.PrivateReadOnly().WithAssignment(new CSharpStatement("[]")));
36+
37+
@class.AddConstructor(ctor =>
38+
{
39+
ctor.AddParameter(this.GetMessageBrokerResolverName(), "resolver");
40+
ctor.AddStatement("_resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));");
41+
});
42+
43+
@class.AddMethod("void", "Publish", method =>
44+
{
45+
method.AddGenericParameter("TMessage", out var tMessage);
46+
method.AddGenericTypeConstraint(tMessage, c => c.AddType("class"));
47+
method.AddParameter(tMessage, "message");
48+
method.AddStatement("ValidateMessageType<TMessage>();");
49+
method.AddStatement("InnerDispatch<TMessage>(provider => provider.Publish(message));");
50+
});
51+
52+
@class.AddMethod("void", "Publish", method =>
53+
{
54+
method.AddGenericParameter("TMessage", out var tMessage);
55+
method.AddGenericTypeConstraint(tMessage, c => c.AddType("class"));
56+
method.AddParameter(tMessage, "message");
57+
method.AddParameter("IDictionary<string, object>", "additionalData");
58+
method.AddStatement("ValidateMessageType<TMessage>();");
59+
method.AddStatement("InnerDispatch<TMessage>(provider => provider.Publish(message, additionalData));");
60+
});
61+
62+
@class.AddMethod("void", "Send", method =>
63+
{
64+
method.AddGenericParameter("TMessage", out var tMessage);
65+
method.AddGenericTypeConstraint(tMessage, c => c.AddType("class"));
66+
method.AddParameter(tMessage, "message");
67+
method.AddStatement("ValidateMessageType<TMessage>();");
68+
method.AddStatement("InnerDispatch<TMessage>(provider => provider.Send(message));");
69+
});
70+
71+
@class.AddMethod("void", "Send", method =>
72+
{
73+
method.AddGenericParameter("TMessage", out var tMessage);
74+
method.AddGenericTypeConstraint(tMessage, c => c.AddType("class"));
75+
method.AddParameter(tMessage, "message");
76+
method.AddParameter("IDictionary<string, object>", "additionalData");
77+
method.AddStatement("ValidateMessageType<TMessage>();");
78+
method.AddStatement("InnerDispatch<TMessage>(provider => provider.Send(message, additionalData));");
79+
});
80+
81+
@class.AddMethod("Task", "FlushAllAsync", method =>
82+
{
83+
method.Async();
84+
method.AddParameter("CancellationToken", "cancellationToken", param => param.WithDefaultValue("default"));
85+
86+
method.AddIfStatement("_messageBusProviders.Count == 0", stmt =>
87+
{
88+
stmt.AddStatement("return;");
89+
});
90+
91+
method.AddStatement(new CSharpAwaitExpression(new CSharpInvocationStatement("Task.WhenAll")
92+
.AddArgument("_messageBusProviders.Select(provider => provider.FlushAllAsync(cancellationToken))")), stmt => stmt.SeparatedFromPrevious());
93+
method.AddStatement("_messageBusProviders.Clear();");
94+
});
95+
96+
@class.AddMethod("void", "InnerDispatch", method =>
97+
{
98+
method.Private();
99+
method.AddGenericParameter("T");
100+
method.AddParameter($"Action<{this.GetMessageBusInterfaceName()}>", "action");
101+
102+
method.AddStatement("var providers = _resolver.GetMessageBusProvidersForMessageType(typeof(T));");
103+
method.AddForEachStatement("provider", "providers", loop =>
104+
{
105+
loop.AddStatement("_messageBusProviders.Add(provider);");
106+
loop.AddStatement("action(provider);");
107+
});
108+
});
109+
110+
@class.AddMethod("void", "ValidateMessageType", method =>
111+
{
112+
method.Private();
113+
method.AddGenericParameter("TMessage");
114+
115+
method.AddStatement("var messageType = typeof(TMessage);");
116+
method.AddIfStatement("!_resolver.IsMessageTypeRegistered(messageType)", stmt =>
117+
{
118+
stmt.AddStatement(@"throw new InvalidOperationException(
119+
$""Message type '{messageType.FullName}' is not registered with any message broker provider. "" +
120+
$""Ensure the message is configured in the appropriate provider's configuration (e.g., AzureEventGridPublisherOptions)."");");
121+
});
122+
});
123+
});
124+
}
125+
126+
[IntentManaged(Mode.Fully)]
127+
public CSharpFile CSharpFile { get; }
128+
129+
[IntentManaged(Mode.Fully)]
130+
protected override CSharpFileConfig DefineFileConfig()
131+
{
132+
return CSharpFile.GetConfig();
133+
}
134+
135+
[IntentManaged(Mode.Fully)]
136+
public override string TransformText()
137+
{
138+
return CSharpFile.ToString();
139+
}
140+
}
141+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Intent.Engine;
5+
using Intent.Metadata.Models;
6+
using Intent.Modules.Common;
7+
using Intent.Modules.Common.Registrations;
8+
using Intent.RoslynWeaver.Attributes;
9+
using Intent.Templates;
10+
11+
[assembly: DefaultIntentManaged(Mode.Fully)]
12+
[assembly: IntentTemplate("Intent.ModuleBuilder.TemplateRegistration.SingleFileNoModel", Version = "1.0")]
13+
14+
namespace Intent.Modules.Eventing.Contracts.Templates.CompositeMessageBus
15+
{
16+
[IntentManaged(Mode.Merge, Body = Mode.Merge, Signature = Mode.Fully)]
17+
public class CompositeMessageBusTemplateRegistration : SingleFileTemplateRegistration
18+
{
19+
public override string TemplateId => CompositeMessageBusTemplate.TemplateId;
20+
21+
[IntentManaged(Mode.Fully)]
22+
public override ITemplate CreateTemplateInstance(IOutputTarget outputTarget)
23+
{
24+
return new CompositeMessageBusTemplate(outputTarget);
25+
}
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Intent.Engine;
5+
using Intent.Modules.Common;
6+
using Intent.Modules.Common.CSharp.Builder;
7+
using Intent.Modules.Common.CSharp.DependencyInjection;
8+
using Intent.Modules.Common.CSharp.Templates;
9+
using Intent.Modules.Common.Templates;
10+
using Intent.RoslynWeaver.Attributes;
11+
using Intent.Templates;
12+
13+
[assembly: DefaultIntentManaged(Mode.Fully)]
14+
[assembly: IntentTemplate("Intent.ModuleBuilder.CSharp.Templates.CSharpTemplatePartial", Version = "1.0")]
15+
16+
namespace Intent.Modules.Eventing.Contracts.Templates.CompositeMessageBusConfiguration
17+
{
18+
[IntentManaged(Mode.Fully, Body = Mode.Merge)]
19+
public partial class CompositeMessageBusConfigurationTemplate : CSharpTemplateBase<object>, ICSharpFileBuilderTemplate
20+
{
21+
public const string TemplateId = "Intent.Eventing.Contracts.CompositeMessageBusConfiguration";
22+
23+
[IntentManaged(Mode.Fully, Body = Mode.Ignore)]
24+
public CompositeMessageBusConfigurationTemplate(IOutputTarget outputTarget, object model = null) : base(TemplateId, outputTarget, model)
25+
{
26+
CSharpFile = new CSharpFile(this.GetNamespace(), this.GetFolderPath())
27+
.AddUsing("Microsoft.Extensions.Configuration")
28+
.AddUsing("Microsoft.Extensions.DependencyInjection")
29+
.AddClass("CompositeMessageBusConfiguration", @class =>
30+
{
31+
@class.Static();
32+
33+
@class.AddMethod("IServiceCollection", "ConfigureCompositeMessageBus", method =>
34+
{
35+
method.Static();
36+
method.AddParameter("IServiceCollection", "services", param => param.WithThisModifier());
37+
method.AddParameter("IConfiguration", "configuration");
38+
39+
method.AddStatement($"// Create the central registry instance (mutable during startup)");
40+
method.AddStatement($"var registry = new {this.GetMessageBrokerRegistryName()}();");
41+
method.AddStatement("");
42+
method.AddStatement("// Configure all message broker providers, passing the registry to each");
43+
44+
// Add configuration calls - these will be populated dynamically by extensions
45+
method.AddStatement("// Provider configurations will be added here", stmt => stmt.SeparatedFromPrevious());
46+
47+
method.AddStatement($"// Register the now-populated registry as a singleton", stmt => stmt.SeparatedFromPrevious());
48+
method.AddStatement("services.AddSingleton(registry);");
49+
50+
method.AddStatement($"// Register the resolver (scoped, uses IServiceProvider to resolve providers per request)", stmt => stmt.SeparatedFromPrevious());
51+
method.AddStatement($"services.AddScoped<{this.GetMessageBrokerResolverName()}>();");
52+
53+
method.AddStatement($"// Register CompositeMessageBus as {this.GetMessageBusInterfaceName()} (scoped per request)", stmt => stmt.SeparatedFromPrevious());
54+
method.AddStatement($"services.AddScoped<{this.GetMessageBusInterfaceName()}, {this.GetCompositeMessageBusName()}>();");
55+
56+
method.AddStatement("return services;", stmt => stmt.SeparatedFromPrevious());
57+
});
58+
});
59+
}
60+
61+
public override void BeforeTemplateExecution()
62+
{
63+
ExecutionContext.EventDispatcher.Publish(ServiceConfigurationRequest
64+
.ToRegister("ConfigureCompositeMessageBus", ServiceConfigurationRequest.ParameterType.Configuration)
65+
.HasDependency(this));
66+
}
67+
68+
[IntentManaged(Mode.Fully)]
69+
public CSharpFile CSharpFile { get; }
70+
71+
[IntentManaged(Mode.Fully)]
72+
protected override CSharpFileConfig DefineFileConfig()
73+
{
74+
return CSharpFile.GetConfig();
75+
}
76+
77+
[IntentManaged(Mode.Fully)]
78+
public override string TransformText()
79+
{
80+
return CSharpFile.ToString();
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Intent.Engine;
5+
using Intent.Metadata.Models;
6+
using Intent.Modules.Common;
7+
using Intent.Modules.Common.Registrations;
8+
using Intent.RoslynWeaver.Attributes;
9+
using Intent.Templates;
10+
11+
[assembly: DefaultIntentManaged(Mode.Fully)]
12+
[assembly: IntentTemplate("Intent.ModuleBuilder.TemplateRegistration.SingleFileNoModel", Version = "1.0")]
13+
14+
namespace Intent.Modules.Eventing.Contracts.Templates.CompositeMessageBusConfiguration
15+
{
16+
[IntentManaged(Mode.Merge, Body = Mode.Merge, Signature = Mode.Fully)]
17+
public class CompositeMessageBusConfigurationTemplateRegistration : SingleFileTemplateRegistration
18+
{
19+
public override string TemplateId => CompositeMessageBusConfigurationTemplate.TemplateId;
20+
21+
[IntentManaged(Mode.Fully)]
22+
public override ITemplate CreateTemplateInstance(IOutputTarget outputTarget)
23+
{
24+
return new CompositeMessageBusConfigurationTemplate(outputTarget);
25+
}
26+
}
27+
}

Modules/Intent.Modules.Eventing.Contracts/Templates/EventBusInterface/EventBusInterfaceTemplatePartial.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,8 @@ public EventBusInterfaceTemplate(IOutputTarget outputTarget, object model = null
2626
.AddUsing("System.Threading")
2727
.AddUsing("System.Threading.Tasks")
2828
.AddInterface("IEventBus", @interface => @interface
29-
.AddMethod("void", "Publish", m => m
30-
.AddGenericParameter("T")
31-
.AddParameter("T", "message")
32-
.AddGenericTypeConstraint("T", c => c.AddType("class"))
33-
)
34-
.AddMethod("Task", "FlushAllAsync", m => m
35-
.AddParameter("CancellationToken", "cancellationToken", p => p.WithDefaultValue("default"))
36-
)
29+
.ExtendsInterface(this.GetMessageBusInterfaceName())
30+
.AddAttribute($"[Obsolete(\"Use {this.GetMessageBusInterfaceName()} instead\")]")
3731
);
3832
}
3933

0 commit comments

Comments
 (0)