diff --git a/Source/Messaging/MessagingActorEntryOptions.cs b/Source/Messaging/MessagingActorEntryOptions.cs
new file mode 100644
index 0000000..ad7a087
--- /dev/null
+++ b/Source/Messaging/MessagingActorEntryOptions.cs
@@ -0,0 +1,22 @@
+namespace Fossa.Messaging;
+
+///
+/// Represents the options for a messaging actor entry.
+///
+public class MessagingActorEntryOptions
+{
+ ///
+ /// Gets or sets the name.
+ ///
+ public string? Name { get; set; }
+
+ ///
+ /// Gets or sets the plain text value.
+ ///
+ public string? PlainTextValue { get; set; }
+
+ ///
+ /// Gets or sets the Base64 value.
+ ///
+ public string? Base64Value { get; set; }
+}
diff --git a/Source/Messaging/MessagingOptions.cs b/Source/Messaging/MessagingOptions.cs
index 59906c9..8c426ea 100644
--- a/Source/Messaging/MessagingOptions.cs
+++ b/Source/Messaging/MessagingOptions.cs
@@ -8,7 +8,7 @@ public class MessagingOptions
///
/// Gets the actor.
///
- public Dictionary? Actor { get; init; }
+ public IReadOnlyList? Actor { get; init; }
///
/// Gets the topic.
diff --git a/Source/Messaging/ProducerProvider.cs b/Source/Messaging/ProducerProvider.cs
index 2e9751d..505e588 100644
--- a/Source/Messaging/ProducerProvider.cs
+++ b/Source/Messaging/ProducerProvider.cs
@@ -1,7 +1,9 @@
namespace Fossa.Messaging;
+using System.Text;
using Confluent.Kafka;
using Microsoft.Extensions.Options;
+using static LanguageExt.Prelude;
///
/// Provides an .
@@ -40,7 +42,12 @@ public void Dispose()
#pragma warning restore CA1508 // Avoid dead conditional code
{
var serviceIdentity = this.serviceIdentityProvider.GetIdentity();
- var producerConfig = new ProducerConfig(this.options.Value.Actor)
+ var messagingActorEntryOptions = this.options.Value.Actor ?? [];
+ var messagingActorOptions = messagingActorEntryOptions
+ .ToDictionary(
+ k => k?.Name ?? throw new InvalidOperationException("One of the Message Actor Entry Options Name is not provided."),
+ ResolveActorEntryValue);
+ var producerConfig = new ProducerConfig(messagingActorOptions)
{
ClientId = serviceIdentity.ToString(),
};
@@ -69,4 +76,31 @@ protected virtual void Dispose(bool disposing)
this.disposedValue = true;
}
}
+
+ private static string ResolveActorEntryValue(MessagingActorEntryOptions? options)
+ {
+ if (options is null)
+ {
+ throw new InvalidOperationException("One of the Message Actor Entry Options is null.");
+ }
+
+ var providedValues = Seq(
+ Tuple(nameof(options.PlainTextValue), Optional(options.PlainTextValue)),
+ Tuple(nameof(options.Base64Value), Optional(options.Base64Value)
+ .Map(x => Encoding.UTF8.GetString(Convert.FromBase64String(x)))))
+ .Choose(x => x.Item2.Map(v => Tuple(x.Item1, v)));
+
+ if (providedValues.Count == 1)
+ {
+ return providedValues.Single().Item2;
+ }
+ else if (providedValues.Count == 0)
+ {
+ throw new InvalidOperationException($"Messaging actor entry '{options.Name}'. One of the value properties must be set.");
+ }
+ else
+ {
+ throw new InvalidOperationException($"Messaging actor entry '{options.Name}' has multiple value properties set. Only one of these '{providedValues.Select(x => x.Item1)}' properties should be set.");
+ }
+ }
}
diff --git a/Tests/Messaging.Test/MessagePublisherTests.cs b/Tests/Messaging.Test/MessagePublisherTests.cs
new file mode 100644
index 0000000..d4004fd
--- /dev/null
+++ b/Tests/Messaging.Test/MessagePublisherTests.cs
@@ -0,0 +1,71 @@
+namespace Fossa.Messaging.Test;
+
+using Autofac;
+using Autofac.Extensions.DependencyInjection;
+using Fossa.Messaging.Messages.Events;
+using IdGen.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Time.Testing;
+using NSubstitute;
+using TIKSN.DependencyInjection;
+using TIKSN.Identity;
+using Xunit;
+
+[Trait("Category", "Integration")]
+public class MessagePublisherTests
+{
+ private readonly IServiceProvider serviceProvider;
+
+ public MessagePublisherTests()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddUserSecrets()
+ .Build();
+ var services = new ServiceCollection();
+ _ = services.AddMessaging(configuration, "Fossa", Seq("Messaging", "Test"));
+ _ = services.AddFrameworkCore();
+ _ = services.AddIdGen(9);
+
+ var fakeTimeProvider = new FakeTimeProvider(
+ new DateTimeOffset(2022, 9, 24, 0, 0, 0, TimeSpan.Zero));
+ _ = services.AddSingleton(fakeTimeProvider);
+
+ var serviceIdentityProvider = Substitute.For();
+ _ = serviceIdentityProvider
+ .GetIdentity().Returns(
+ new ServiceIdentity(
+ applicationName: "Fossa",
+ componentNames: Seq("Messaging", "Test"),
+ instanceId: ServiceInstanceId.Create(Ulid.NewUlid())));
+
+ _ = services.AddSingleton(serviceIdentityProvider);
+
+ ContainerBuilder containerBuilder = new();
+ _ = containerBuilder.RegisterModule();
+ containerBuilder.Populate(services);
+
+ this.serviceProvider = new AutofacServiceProvider(containerBuilder.Build());
+ }
+
+ [Fact]
+ public async Task GivenPublisherAndMessage_WhenMessageIsPublished_ThenDeliveryShouldSucceedAsync()
+ {
+ // Arrange
+ var messagePublisher = this.serviceProvider.GetRequiredService();
+ const string topic = "test";
+
+ var message = new CompanyDeletedProtoEvent { CompanyId = 123L };
+
+ // Act
+
+ var deliveryResult = await messagePublisher.PublishAsync(message, message.CompanyId, "Company", message.CompanyId, default).ConfigureAwait(true);
+
+ // Assert
+
+ Assert.NotNull(deliveryResult);
+ Assert.NotNull(deliveryResult.Key);
+ Assert.NotNull(deliveryResult.Value);
+ Assert.Equal(topic, deliveryResult.Topic);
+ }
+}
diff --git a/Tests/Messaging.Test/Messaging.Test.csproj b/Tests/Messaging.Test/Messaging.Test.csproj
index b574345..052366a 100644
--- a/Tests/Messaging.Test/Messaging.Test.csproj
+++ b/Tests/Messaging.Test/Messaging.Test.csproj
@@ -2,6 +2,7 @@
net10.0
+ ad336f2b-09b8-4c53-96cb-ed9913b583f1
diff --git a/build.cake b/build.cake
index 3954abe..180188e 100644
--- a/build.cake
+++ b/build.cake
@@ -49,6 +49,7 @@ Task("Test")
Blame = true,
Collectors = new string[] { "Code Coverage", "XPlat Code Coverage" },
Configuration = configuration,
+ Filter = !BuildSystem.IsLocalBuild ? "Category!=Integration" : null,
Loggers = new string[]
{
$"trx;LogFileName={project.GetFilenameWithoutExtension()}.trx",