diff --git a/README.md b/README.md
index c12e2e68..912d3915 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,52 @@ Currently supported message broker services:
If you would like to use a message broker service not listed above, please file an [issue](https://github.com/Project-MONAI/monai-deploy-messaging/issues) and contribute to the repository.
+---
+
+## Installation
+
+### 1. Configure the Service
+To use the MONAI Deploy Messaging library, install the [NuGet.Org](https://www.nuget.org/packages/Monai.Deploy.Messaging/) package and call the `AddMonaiDeployMessageBrokerSubscriberService(...)` and/or the `AddMonaiDeployMessageBrokerPublisherService(...)` method to register the dependencies:
+
+```csharp
+Host.CreateDefaultBuilder(args)
+ .ConfigureServices((hostContext, services) =>
+ {
+ ...
+ // Register the subscriber service
+ services.AddMonaiDeployMessageBrokerSubscriberService(hostContext.Configuration.GetSection("InformaticsGateway:messaging:publisherServiceAssemblyName").Value);
+
+ // Register the publisher service
+ services.AddMonaiDeployMessageBrokerPublisherService(hostContext.Configuration.GetSection("InformaticsGateway:messaging:subscriberServiceAssemblyName").Value);
+ ...
+ });
+```
+
+### 2. Install the Plug-in
+
+1. Create a subdirectory named `plug-ins` in the directory where your main application is installed.
+2. Download the zipped plug-in of your choice and extract the files to the `plug-ins` directory.
+3. Update `appsettings.json` and set the `publisherServiceAssemblyName` and the `subscriberServiceAssemblyName`, e.g.:
+ ```json
+ "messaging": {
+ "publisherServiceAssemblyName": "Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessagePublisherService, Monai.Deploy.Messaging.RabbitMQ",
+ "publisherSettings": {
+ ...
+ },
+ "subscriberServiceAssemblyName": "Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessageSubscriberService, Monai.Deploy.Messaging.RabbitMQ",
+ "subscriberSettings": {
+ ...
+ }
+ },
+ ```
+
+
+### 3. Restrict Acess to the Plug-ins Directory
+
+To avoid tampering of the plug-ins, it is recommended to set access rights to the plug-ins directory.
+
+---
+
## Releases
The MONAI Deploy Messaging library is released in NuGet format, which is available on both [NuGet.Org](https://www.nuget.org/packages/Monai.Deploy.Messaging/) and [GitHub](https://github.com/Project-MONAI/monai-deploy-messaging/packages/1365839).
diff --git a/src/.sonarlint/project-monai_monai-deploy-messaging/CSharp/SonarLint.xml b/src/.sonarlint/project-monai_monai-deploy-messaging/CSharp/SonarLint.xml
index 90bc98df..c6f7cc2a 100644
--- a/src/.sonarlint/project-monai_monai-deploy-messaging/CSharp/SonarLint.xml
+++ b/src/.sonarlint/project-monai_monai-deploy-messaging/CSharp/SonarLint.xml
@@ -86,4 +86,4 @@
-
\ No newline at end of file
+
diff --git a/src/Messaging/API/IMessageBrokerPublisherService.cs b/src/Messaging/API/IMessageBrokerPublisherService.cs
index a36ac8f7..54019cf1 100644
--- a/src/Messaging/API/IMessageBrokerPublisherService.cs
+++ b/src/Messaging/API/IMessageBrokerPublisherService.cs
@@ -3,7 +3,7 @@
using Monai.Deploy.Messaging.Messages;
-namespace Monai.Deploy.Messaging
+namespace Monai.Deploy.Messaging.API
{
public interface IMessageBrokerPublisherService : IDisposable
{
diff --git a/src/Messaging/API/IMessageBrokerSubscriberService.cs b/src/Messaging/API/IMessageBrokerSubscriberService.cs
index 240d8677..d35b8652 100644
--- a/src/Messaging/API/IMessageBrokerSubscriberService.cs
+++ b/src/Messaging/API/IMessageBrokerSubscriberService.cs
@@ -4,7 +4,7 @@
using Monai.Deploy.Messaging.Common;
using Monai.Deploy.Messaging.Messages;
-namespace Monai.Deploy.Messaging
+namespace Monai.Deploy.Messaging.API
{
public interface IMessageBrokerSubscriberService : IDisposable
{
diff --git a/src/Messaging/Configuration/MessageBrokerServiceConfiguration.cs b/src/Messaging/Configuration/MessageBrokerServiceConfiguration.cs
index a055d9ef..34407d1e 100644
--- a/src/Messaging/Configuration/MessageBrokerServiceConfiguration.cs
+++ b/src/Messaging/Configuration/MessageBrokerServiceConfiguration.cs
@@ -7,13 +7,16 @@ namespace Monai.Deploy.Messaging.Configuration
{
public class MessageBrokerServiceConfiguration
{
+ public const string DefaultPublisherAssemblyName = "Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessagePublisherService, Monai.Deploy.Messaging.RabbitMQ";
+ public const string DefaultSubscriberAssemblyName = "Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessageSubscriberService, Monai.Deploy.Messaging.RabbitMQ";
+
///
/// Gets or sets the a fully qualified type name of the message publisher service.
/// The spcified type must implement IMessageBrokerPublisherService interface.
/// The default message publisher service configured is RabbitMQ.
///
[ConfigurationKeyName("publisherServiceAssemblyName")]
- public string PublisherServiceAssemblyName { get; set; } = "Monai.Deploy.Messaging.RabbitMq.RabbitMqMessagePublisherService, Monai.Deploy.Messaging";
+ public string PublisherServiceAssemblyName { get; set; } = DefaultPublisherAssemblyName;
///
/// Gets or sets the a fully qualified type name of the message subscriber service.
@@ -21,7 +24,7 @@ public class MessageBrokerServiceConfiguration
/// The default message subscriber service configured is RabbitMQ.
///
[ConfigurationKeyName("subscriberServiceAssemblyName")]
- public string SubscriberServiceAssemblyName { get; set; } = "Monai.Deploy.Messaging.RabbitMq.RabbitMqMessageSubscriberService, Monai.Deploy.Messaging";
+ public string SubscriberServiceAssemblyName { get; set; } = DefaultSubscriberAssemblyName;
///
/// Gets or sets the message publisher specific settings.
diff --git a/src/Messaging/IServiceCollectionExtension.cs b/src/Messaging/IServiceCollectionExtension.cs
new file mode 100644
index 00000000..cae473b3
--- /dev/null
+++ b/src/Messaging/IServiceCollectionExtension.cs
@@ -0,0 +1,144 @@
+// SPDX-FileCopyrightText: © 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+using System.IO.Abstractions;
+using System.Reflection;
+using Ardalis.GuardClauses;
+using Microsoft.Extensions.DependencyInjection;
+using Monai.Deploy.Messaging.API;
+using Monai.Deploy.Messaging.Configuration;
+
+namespace Monai.Deploy.Messaging
+{
+ public static class IServiceCollectionExtensions
+ {
+ private static IFileSystem? s_fileSystem;
+
+ ///
+ /// Configures all dependencies required for the MONAI Deploy Message Broker Subscriber Service.
+ ///
+ /// Instance of .
+ /// Fully qualified type name of the service to use.
+ /// Instance of .
+ ///
+ public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService(this IServiceCollection services, string fullyQualifiedTypeName)
+ => AddMonaiDeployMessageBrokerSubscriberService(services, fullyQualifiedTypeName, new FileSystem());
+
+ ///
+ /// Configures all dependencies required for the MONAI Deploy Message Broker Subscriber Service.
+ ///
+ /// Instance of .
+ /// Fully qualified type name of the service to use.
+ /// Instance of .
+ /// Instance of .
+ ///
+ public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService(this IServiceCollection services, string fullyQualifiedTypeName, IFileSystem fileSystem)
+ => Add(services, fullyQualifiedTypeName, fileSystem);
+
+ ///
+ /// Configures all dependencies required for the MONAI Deploy Message Broker Publisher Service.
+ ///
+ /// Instance of .
+ /// Fully qualified type name of the service to use.
+ /// Instance of .
+ ///
+ public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService(this IServiceCollection services, string fullyQualifiedTypeName)
+ => AddMonaiDeployMessageBrokerPublisherService(services, fullyQualifiedTypeName, new FileSystem());
+
+ ///
+ /// Configures all dependencies required for the MONAI Deploy Message Broker Publisher Service.
+ ///
+ /// Instance of .
+ /// Fully qualified type name of the service to use.
+ /// Instance of .
+ /// Instance of .
+ ///
+ public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService(this IServiceCollection services, string fullyQualifiedTypeName, IFileSystem fileSystem)
+ => Add(services, fullyQualifiedTypeName, fileSystem);
+
+ private static IServiceCollection Add(this IServiceCollection services, string fullyQualifiedTypeName, IFileSystem fileSystem) where U : ServiceRegistrationBase
+ {
+ Guard.Against.NullOrWhiteSpace(fullyQualifiedTypeName, nameof(fullyQualifiedTypeName));
+ Guard.Against.Null(fileSystem, nameof(fileSystem));
+
+ s_fileSystem = fileSystem;
+
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
+
+ try
+ {
+ var serviceAssembly = LoadAssemblyFromDisk(GetAssemblyName(fullyQualifiedTypeName));
+ var serviceRegistrationType = serviceAssembly.GetTypes().FirstOrDefault(p => p.BaseType == typeof(U));
+
+ if (serviceRegistrationType is null || Activator.CreateInstance(serviceRegistrationType, fullyQualifiedTypeName) is not U serviceRegistrar)
+ {
+ throw new ConfigurationException($"Service registrar cannot be found for the configured plug-in '{fullyQualifiedTypeName}'.");
+ }
+
+ if (!IsSupportedType(fullyQualifiedTypeName, serviceAssembly))
+ {
+ throw new ConfigurationException($"The configured type '{fullyQualifiedTypeName}' does not implement the {typeof(T).Name} interface.");
+ }
+
+ return serviceRegistrar.Configure(services);
+ }
+ finally
+ {
+ AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
+ }
+ }
+
+ private static bool IsSupportedType(string fullyQualifiedTypeName, Assembly storageServiceAssembly)
+ {
+ Guard.Against.NullOrWhiteSpace(fullyQualifiedTypeName, nameof(fullyQualifiedTypeName));
+ Guard.Against.Null(storageServiceAssembly, nameof(storageServiceAssembly));
+
+ var storageServiceType = Type.GetType(fullyQualifiedTypeName, assemblyeName => storageServiceAssembly, null, false);
+
+ return storageServiceType is not null &&
+ storageServiceType.GetInterfaces().Contains(typeof(T));
+ }
+
+ private static string GetAssemblyName(string fullyQualifiedTypeName)
+ {
+ var assemblyNameParts = fullyQualifiedTypeName.Split(',', StringSplitOptions.None);
+ if (assemblyNameParts.Length < 2 || string.IsNullOrWhiteSpace(assemblyNameParts[1]))
+ {
+ throw new ConfigurationException($"The configured service type '{fullyQualifiedTypeName}' is not a valid fully qualified type name. E.g. {MessageBrokerServiceConfiguration.DefaultPublisherAssemblyName}")
+ {
+ HelpLink = "https://docs.microsoft.com/en-us/dotnet/standard/assembly/find-fully-qualified-name"
+ };
+ }
+
+ return assemblyNameParts[1].Trim();
+ }
+
+ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
+ {
+ Guard.Against.Null(args, nameof(args));
+
+ var requestedAssemblyName = new AssemblyName(args.Name);
+ return LoadAssemblyFromDisk(requestedAssemblyName.Name);
+ }
+
+ private static Assembly LoadAssemblyFromDisk(string assemblyName)
+ {
+ Guard.Against.NullOrWhiteSpace(assemblyName, nameof(assemblyName));
+ Guard.Against.Null(s_fileSystem, nameof(s_fileSystem));
+
+ if (!s_fileSystem.Directory.Exists(SR.PlugInDirectoryPath))
+ {
+ throw new ConfigurationException($"Plug-in directory '{SR.PlugInDirectoryPath}' cannot be found.");
+ }
+
+ var assemblyFilePath = s_fileSystem.Path.Combine(SR.PlugInDirectoryPath, $"{assemblyName}.dll");
+ if (!s_fileSystem.File.Exists(assemblyFilePath))
+ {
+ throw new ConfigurationException($"The configured plug-in '{assemblyFilePath}' cannot be found.");
+ }
+
+ var asesmblyeData = s_fileSystem.File.ReadAllBytes(assemblyFilePath);
+ return Assembly.Load(asesmblyeData);
+ }
+ }
+}
diff --git a/src/Messaging/InternalVisible.cs b/src/Messaging/InternalVisible.cs
index 94266994..7a314342 100644
--- a/src/Messaging/InternalVisible.cs
+++ b/src/Messaging/InternalVisible.cs
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.Test")]
+[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.Tests")]
+[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.RabbitMQ.Tests")]
diff --git a/src/Messaging/Messages/Message.cs b/src/Messaging/Messages/Message.cs
index 144d0ba2..8f44750f 100644
--- a/src/Messaging/Messages/Message.cs
+++ b/src/Messaging/Messages/Message.cs
@@ -40,7 +40,7 @@ public T ConvertTo()
var json = Encoding.UTF8.GetString(Body);
return JsonConvert.DeserializeObject(json)!;
}
- catch(Exception ex)
+ catch (Exception ex)
{
throw new MessageConversionException($"Error converting message to type {typeof(T)}", ex);
}
diff --git a/src/Messaging/Monai.Deploy.Messaging.csproj b/src/Messaging/Monai.Deploy.Messaging.csproj
index 1ef2b452..ef4b3635 100644
--- a/src/Messaging/Monai.Deploy.Messaging.csproj
+++ b/src/Messaging/Monai.Deploy.Messaging.csproj
@@ -33,9 +33,9 @@ SPDX-License-Identifier: Apache License 2.0
-
-
-
+
+
+
@@ -54,7 +54,8 @@ SPDX-License-Identifier: Apache License 2.0
-
+
+
\ No newline at end of file
diff --git a/src/Messaging/RabbitMq/IServiceCollectionExtension.cs b/src/Messaging/RabbitMq/IServiceCollectionExtension.cs
deleted file mode 100644
index bd6f9f51..00000000
--- a/src/Messaging/RabbitMq/IServiceCollectionExtension.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-FileCopyrightText: © 2022 MONAI Consortium
-// SPDX-License-Identifier: Apache License 2.0
-
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Monai.Deploy.Messaging.RabbitMq
-{
- public static class IServiceCollectionExtension
- {
- public static IServiceCollection UseRabbitMq(this IServiceCollection services)
- {
- services.AddSingleton();
-
- return services;
- }
- }
-}
diff --git a/src/Messaging/SR.cs b/src/Messaging/SR.cs
new file mode 100644
index 00000000..5f51387d
--- /dev/null
+++ b/src/Messaging/SR.cs
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: © 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+namespace Monai.Deploy.Messaging
+{
+ internal static class SR
+ {
+ public const string PlugInDirectoryName = "plug-ins";
+ public static readonly string PlugInDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SR.PlugInDirectoryName);
+ }
+}
diff --git a/src/Messaging/ServiceRegistrationBase.cs b/src/Messaging/ServiceRegistrationBase.cs
new file mode 100644
index 00000000..40654310
--- /dev/null
+++ b/src/Messaging/ServiceRegistrationBase.cs
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: © 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+using Ardalis.GuardClauses;
+using Microsoft.Extensions.DependencyInjection;
+using Monai.Deploy.Messaging.Configuration;
+
+namespace Monai.Deploy.Messaging
+{
+ public abstract class SubscriberServiceRegistrationBase : ServiceRegistrationBase
+ {
+ protected SubscriberServiceRegistrationBase(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName)
+ {
+ }
+ }
+
+ public abstract class PublisherServiceRegistrationBase : ServiceRegistrationBase
+ {
+ protected PublisherServiceRegistrationBase(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName)
+ {
+ }
+ }
+
+ public abstract class ServiceRegistrationBase
+ {
+ protected string FullyQualifiedAssemblyName { get; }
+ protected string AssemblyFilename { get; }
+
+ protected ServiceRegistrationBase(string fullyQualifiedAssemblyName)
+ {
+ Guard.Against.NullOrWhiteSpace(fullyQualifiedAssemblyName, nameof(fullyQualifiedAssemblyName));
+ FullyQualifiedAssemblyName = fullyQualifiedAssemblyName;
+ AssemblyFilename = ParseAssemblyName();
+ }
+
+ private string ParseAssemblyName()
+ {
+ var assemblyNameParts = FullyQualifiedAssemblyName.Split(',', StringSplitOptions.None);
+ if (assemblyNameParts.Length < 2 || string.IsNullOrWhiteSpace(assemblyNameParts[1]))
+ {
+ throw new ConfigurationException($"Storage service '{FullyQualifiedAssemblyName}' is invalid. Please provide a fully qualified name.")
+ {
+ HelpLink = "https://docs.microsoft.com/en-us/dotnet/standard/assembly/find-fully-qualified-name"
+ };
+ }
+
+ return assemblyNameParts[1].Trim();
+ }
+
+ public abstract IServiceCollection Configure(IServiceCollection services);
+ }
+}
diff --git a/src/Messaging/Test/EventBaseTest.cs b/src/Messaging/Tests/EventBaseTest.cs
similarity index 98%
rename from src/Messaging/Test/EventBaseTest.cs
rename to src/Messaging/Tests/EventBaseTest.cs
index c082d015..b2773162 100644
--- a/src/Messaging/Test/EventBaseTest.cs
+++ b/src/Messaging/Tests/EventBaseTest.cs
@@ -7,7 +7,7 @@
using Monai.Deploy.Messaging.Events;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
internal class StringClass : EventBase
{
diff --git a/src/Messaging/Test/ExportCompleteEventTest.cs b/src/Messaging/Tests/ExportCompleteEventTest.cs
similarity index 81%
rename from src/Messaging/Test/ExportCompleteEventTest.cs
rename to src/Messaging/Tests/ExportCompleteEventTest.cs
index 99364819..d56be9cc 100644
--- a/src/Messaging/Test/ExportCompleteEventTest.cs
+++ b/src/Messaging/Tests/ExportCompleteEventTest.cs
@@ -7,7 +7,7 @@
using Monai.Deploy.Messaging.Events;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
public class ExportCompleteEventTest
{
@@ -22,14 +22,14 @@ public void ShallGenerateExportCompleteMessageTestFromExportRequestMessage()
ExportTaskId = Guid.NewGuid().ToString(),
MessageId = Guid.NewGuid().ToString(),
WorkflowInstanceId = Guid.NewGuid().ToString(),
- };
- exportRequestMessage.Files = new List()
- {
- Guid.NewGuid().ToString(),
- Guid.NewGuid().ToString(),
- Guid.NewGuid().ToString(),
- Guid.NewGuid().ToString(),
- Guid.NewGuid().ToString(),
+ Files = new List()
+ {
+ Guid.NewGuid().ToString(),
+ Guid.NewGuid().ToString(),
+ Guid.NewGuid().ToString(),
+ Guid.NewGuid().ToString(),
+ Guid.NewGuid().ToString(),
+ }
};
var errors = new List()
@@ -47,7 +47,7 @@ public void ShallGenerateExportCompleteMessageTestFromExportRequestMessage()
Assert.Equal(exportRequestMessage.WorkflowInstanceId, exportCompleteMessage.WorkflowInstanceId);
Assert.Equal(exportRequestMessage.ExportTaskId, exportCompleteMessage.ExportTaskId);
- Assert.Equal(string.Join(System.Environment.NewLine, errors), exportCompleteMessage.Message);
+ Assert.Equal(string.Join(Environment.NewLine, errors), exportCompleteMessage.Message);
Assert.Equal(ExportStatus.Success, exportCompleteMessage.Status);
}
diff --git a/src/Messaging/Tests/IServiceCollectionExtensionsTests.cs b/src/Messaging/Tests/IServiceCollectionExtensionsTests.cs
new file mode 100644
index 00000000..732f35f6
--- /dev/null
+++ b/src/Messaging/Tests/IServiceCollectionExtensionsTests.cs
@@ -0,0 +1,206 @@
+// SPDX-FileCopyrightText: © 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+using System;
+using System.IO;
+using System.IO.Abstractions.TestingHelpers;
+using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Monai.Deploy.Messaging.API;
+using Monai.Deploy.Messaging.Common;
+using Monai.Deploy.Messaging.Configuration;
+using Monai.Deploy.Messaging.Messages;
+using Moq;
+using Xunit;
+
+namespace Monai.Deploy.Messaging.Tests
+{
+#pragma warning disable CS8604 // Possible null reference argument.
+
+ public class IServiceCollectionExtensionsTests
+ {
+ [Theory(DisplayName = "AddMonaiDeployMessageBrokerServices throws when type name is invalid")]
+ [InlineData("mytype")]
+ [InlineData("mytype,, myversion")]
+ [InlineData("mytype, myassembly, myversion")]
+ public void AddMonaiDeployMessageBrokerServices_ThrowsOnInvalidTypeName(string typeName)
+ {
+ var serviceCollection = new Mock();
+
+ Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(typeName, new MockFileSystem()));
+ Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(typeName, new MockFileSystem()));
+ }
+
+ [Fact(DisplayName = "AddMonaiDeployMessageBrokerServices throws if the plug-ins directory is missing")]
+ public void AddMonaiDeployMessageBrokerServices_ThrowsIfPlugInsDirectoryIsMissing()
+ {
+ var typeName = typeof(SomeClass).AssemblyQualifiedName;
+ var serviceCollection = new Mock();
+ var exception = Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(typeName, new MockFileSystem()));
+ Assert.NotNull(exception);
+ Assert.Equal($"Plug-in directory '{SR.PlugInDirectoryPath}' cannot be found.", exception.Message);
+
+ exception = Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(typeName, new MockFileSystem()));
+ Assert.NotNull(exception);
+ Assert.Equal($"Plug-in directory '{SR.PlugInDirectoryPath}' cannot be found.", exception.Message);
+ }
+
+ [Fact(DisplayName = "AddMonaiDeployMessageBrokerServices throws if the plug-in dll is missing")]
+ public void AddMonaiDeployMessageBrokerServices_ThrowsIfPlugInDllIsMissing()
+ {
+ var badType = typeof(SomeClass);
+ var typeName = badType.AssemblyQualifiedName;
+ var fileSystem = new MockFileSystem();
+ fileSystem.Directory.CreateDirectory(SR.PlugInDirectoryPath);
+ var serviceCollection = new Mock();
+ var exception = Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(typeName, fileSystem));
+ Assert.NotNull(exception);
+ Assert.Equal($"The configured plug-in '{SR.PlugInDirectoryPath}{Path.DirectorySeparatorChar}{badType.Assembly.ManifestModule.Name}' cannot be found.", exception.Message);
+
+ exception = Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(typeName, fileSystem));
+ Assert.NotNull(exception);
+ Assert.Equal($"The configured plug-in '{SR.PlugInDirectoryPath}{Path.DirectorySeparatorChar}{badType.Assembly.ManifestModule.Name}' cannot be found.", exception.Message);
+ }
+
+ [Fact(DisplayName = "AddMonaiDeployMessageBrokerServices throws if service registrar cannot be found in the assembly")]
+ public void AddMonaiDeployMessageBrokerServices_ThrowsIfServiceRegistrarCannotBeFoundInTheAssembly()
+ {
+ var badType = typeof(Assert);
+ var typeName = badType.AssemblyQualifiedName;
+ var assemblyData = GetAssemblyeBytes(badType.Assembly);
+ var assemblyFilePath = Path.Combine(SR.PlugInDirectoryPath, badType.Assembly.ManifestModule.Name);
+ var fileSystem = new MockFileSystem();
+ fileSystem.Directory.CreateDirectory(SR.PlugInDirectoryPath);
+ fileSystem.File.WriteAllBytes(assemblyFilePath, assemblyData);
+ var serviceCollection = new Mock();
+ var exception = Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(typeName, fileSystem));
+ Assert.NotNull(exception);
+ Assert.Equal($"Service registrar cannot be found for the configured plug-in '{typeName}'.", exception.Message);
+
+ exception = Assert.Throws(() => serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(typeName, fileSystem));
+ Assert.NotNull(exception);
+ Assert.Equal($"Service registrar cannot be found for the configured plug-in '{typeName}'.", exception.Message);
+ }
+
+ [Fact(DisplayName = "AddMonaiDeployMessageBrokerServices throws if service type is not supported")]
+ public void AddMonaiDeployMessageBrokerServices_ThrowsIfServiceTypeIsNotSupported()
+ {
+ var badType = typeof(SomeClass);
+ var typeName = badType.AssemblyQualifiedName;
+ var assemblyData = GetAssemblyeBytes(badType.Assembly);
+ var assemblyFilePath = Path.Combine(SR.PlugInDirectoryPath, badType.Assembly.ManifestModule.Name);
+ var fileSystem = new MockFileSystem();
+ fileSystem.Directory.CreateDirectory(SR.PlugInDirectoryPath);
+ fileSystem.File.WriteAllBytes(assemblyFilePath, assemblyData);
+ var serviceCollection = new Mock();
+ serviceCollection.Setup(p => p.Clear());
+ var exception = Record.Exception(() => serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(typeName, fileSystem));
+ Assert.NotNull(exception);
+ Assert.Equal($"The configured type '{typeName}' does not implement the {typeof(IMessageBrokerPublisherService).Name} interface.", exception.Message);
+
+ exception = Record.Exception(() => serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(typeName, fileSystem));
+ Assert.NotNull(exception);
+ Assert.Equal($"The configured type '{typeName}' does not implement the {typeof(IMessageBrokerSubscriberService).Name} interface.", exception.Message);
+ }
+
+ [Fact(DisplayName = "AddMonaiDeployMessageBrokerPublisherService configures all services as expected")]
+ public void AddMonaiDeployMessageBrokerPublisherService_ConfiuresServicesAsExpected()
+ {
+ var badType = typeof(GoodPublisherService);
+ var typeName = badType.AssemblyQualifiedName;
+ var assemblyData = GetAssemblyeBytes(badType.Assembly);
+ var assemblyFilePath = Path.Combine(SR.PlugInDirectoryPath, badType.Assembly.ManifestModule.Name);
+ var fileSystem = new MockFileSystem();
+ fileSystem.Directory.CreateDirectory(SR.PlugInDirectoryPath);
+ fileSystem.File.WriteAllBytes(assemblyFilePath, assemblyData);
+ var serviceCollection = new Mock();
+ serviceCollection.Setup(p => p.Clear());
+ var exception = Record.Exception(() => serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(typeName, fileSystem));
+ Assert.Null(exception);
+ serviceCollection.Verify(p => p.Clear(), Times.Once());
+ }
+
+ [Fact(DisplayName = "AddMonaiDeployMessageBrokerSubscriberService configures all services as expected")]
+ public void AddMonaiDeployMessageBrokerSubscriberService_ConfiuresServicesAsExpected()
+ {
+ var badType = typeof(GoodSubscriberService);
+ var typeName = badType.AssemblyQualifiedName;
+ var assemblyData = GetAssemblyeBytes(badType.Assembly);
+ var assemblyFilePath = Path.Combine(SR.PlugInDirectoryPath, badType.Assembly.ManifestModule.Name);
+ var fileSystem = new MockFileSystem();
+ fileSystem.Directory.CreateDirectory(SR.PlugInDirectoryPath);
+ fileSystem.File.WriteAllBytes(assemblyFilePath, assemblyData);
+ var serviceCollection = new Mock();
+ serviceCollection.Setup(p => p.Clear());
+ var exception = Record.Exception(() => serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(typeName, fileSystem));
+ Assert.Null(exception);
+ serviceCollection.Verify(p => p.Clear(), Times.Once());
+ }
+
+ private static byte[] GetAssemblyeBytes(Assembly assembly)
+ {
+ return File.ReadAllBytes(assembly.Location);
+ }
+ }
+
+ internal class TestSubscriberServiceRegistrar : SubscriberServiceRegistrationBase
+ {
+ public TestSubscriberServiceRegistrar(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName)
+ {
+ }
+
+ public override IServiceCollection Configure(IServiceCollection services)
+ {
+ services.Clear();
+ return services;
+ }
+ }
+
+ internal class TestPublisherServiceRegistrar : PublisherServiceRegistrationBase
+ {
+ public TestPublisherServiceRegistrar(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName)
+ {
+ }
+
+ public override IServiceCollection Configure(IServiceCollection services)
+ {
+ services.Clear();
+ return services;
+ }
+ }
+
+ internal class GoodPublisherService : IMessageBrokerPublisherService
+ {
+ public string Name => throw new NotImplementedException();
+
+ public void Dispose() => throw new NotImplementedException();
+
+ public Task Publish(string topic, Message message) => throw new NotImplementedException();
+ }
+
+ internal class GoodSubscriberService : IMessageBrokerSubscriberService
+ {
+ public string Name => throw new NotImplementedException();
+
+ public void Acknowledge(MessageBase message) => throw new NotImplementedException();
+
+ public void Dispose() => throw new NotImplementedException();
+
+ public void Reject(MessageBase message, bool requeue = true) => throw new NotImplementedException();
+
+ public void Subscribe(string topic, string queue, Action messageReceivedCallback, ushort prefetchCount = 0) => throw new NotImplementedException();
+
+ public void Subscribe(string[] topics, string queue, Action messageReceivedCallback, ushort prefetchCount = 0) => throw new NotImplementedException();
+
+ public void SubscribeAsync(string topic, string queue, Func messageReceivedCallback, ushort prefetchCount = 0) => throw new NotImplementedException();
+
+ public void SubscribeAsync(string[] topics, string queue, Func messageReceivedCallback, ushort prefetchCount = 0) => throw new NotImplementedException();
+ }
+
+ internal class SomeClass
+ {
+ }
+
+#pragma warning restore CS8604 // Possible null reference argument.
+}
diff --git a/src/Messaging/Test/JsonMessageTest.cs b/src/Messaging/Tests/JsonMessageTest.cs
similarity index 98%
rename from src/Messaging/Test/JsonMessageTest.cs
rename to src/Messaging/Tests/JsonMessageTest.cs
index 1bab7b79..2985501c 100644
--- a/src/Messaging/Test/JsonMessageTest.cs
+++ b/src/Messaging/Tests/JsonMessageTest.cs
@@ -6,7 +6,7 @@
using Monai.Deploy.Messaging.Messages;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
public class DummyTypeOne
{
@@ -27,10 +27,10 @@ public void ConvertsThrowsError()
var jsonMessage = new JsonMessage(data, Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
var message = jsonMessage.ToMessage();
-
Assert.Throws(() => message.ConvertTo());
Assert.Throws(() => message.ConvertToJsonMessage());
}
+
[Fact(DisplayName = "Converts JsonMessage to Message")]
public void ConvertsJsonMessageToMessage()
{
diff --git a/src/Messaging/Test/Monai.Deploy.Messaging.Test.csproj b/src/Messaging/Tests/Monai.Deploy.Messaging.Tests.csproj
similarity index 84%
rename from src/Messaging/Test/Monai.Deploy.Messaging.Test.csproj
rename to src/Messaging/Tests/Monai.Deploy.Messaging.Tests.csproj
index d3833e52..90c93b4b 100644
--- a/src/Messaging/Test/Monai.Deploy.Messaging.Test.csproj
+++ b/src/Messaging/Tests/Monai.Deploy.Messaging.Tests.csproj
@@ -8,14 +8,15 @@
-
-
+
+
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Messaging/Tests/ServiceRegistrationBaseTests.cs b/src/Messaging/Tests/ServiceRegistrationBaseTests.cs
new file mode 100644
index 00000000..169731c2
--- /dev/null
+++ b/src/Messaging/Tests/ServiceRegistrationBaseTests.cs
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: © 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Monai.Deploy.Messaging.Configuration;
+using Xunit;
+
+namespace Monai.Deploy.Messaging.Tests
+{
+ internal class TestServiceRegistration : ServiceRegistrationBase
+ {
+ public TestServiceRegistration(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName)
+ {
+ }
+
+ public override IServiceCollection Configure(IServiceCollection services) => throw new NotImplementedException();
+ }
+
+ public class ServiceRegistrationBaseTests
+ {
+ [Theory(DisplayName = "ParseAssemblyName - throws if fully qualified assembly name is invalid")]
+ [InlineData("mytype")]
+ [InlineData("mytype,, myversion")]
+ public void ParseAssemblyName_ThrowIfFullyQualifiedAssemblyNameIsInvalid(string assemblyeName)
+ {
+ Assert.Throws(() => new TestPublisherServiceRegistrar(assemblyeName));
+ }
+ }
+}
diff --git a/src/Messaging/Test/TaskCallbackEventTest.cs b/src/Messaging/Tests/TaskCallbackEventTest.cs
similarity index 97%
rename from src/Messaging/Test/TaskCallbackEventTest.cs
rename to src/Messaging/Tests/TaskCallbackEventTest.cs
index ca670a26..9d1eb11b 100644
--- a/src/Messaging/Test/TaskCallbackEventTest.cs
+++ b/src/Messaging/Tests/TaskCallbackEventTest.cs
@@ -6,7 +6,7 @@
using Monai.Deploy.Messaging.Events;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
public class TaskCallbackEventTest
{
diff --git a/src/Messaging/Test/TaskDispatchEventTest.cs b/src/Messaging/Tests/TaskDispatchEventTest.cs
similarity index 99%
rename from src/Messaging/Test/TaskDispatchEventTest.cs
rename to src/Messaging/Tests/TaskDispatchEventTest.cs
index 5841c695..ab9573fc 100644
--- a/src/Messaging/Test/TaskDispatchEventTest.cs
+++ b/src/Messaging/Tests/TaskDispatchEventTest.cs
@@ -7,7 +7,7 @@
using Monai.Deploy.Messaging.Events;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
public class TaskDispatchEventTest
{
diff --git a/src/Messaging/Test/TaskUpdateEventTest.cs b/src/Messaging/Tests/TaskUpdateEventTest.cs
similarity index 96%
rename from src/Messaging/Test/TaskUpdateEventTest.cs
rename to src/Messaging/Tests/TaskUpdateEventTest.cs
index 4777d13b..76d3282c 100644
--- a/src/Messaging/Test/TaskUpdateEventTest.cs
+++ b/src/Messaging/Tests/TaskUpdateEventTest.cs
@@ -6,7 +6,7 @@
using Monai.Deploy.Messaging.Events;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
public class TaskUpdateEventTest
{
diff --git a/src/Messaging/Test/WorkflowRequestMessageTest.cs b/src/Messaging/Tests/WorkflowRequestMessageTest.cs
similarity index 97%
rename from src/Messaging/Test/WorkflowRequestMessageTest.cs
rename to src/Messaging/Tests/WorkflowRequestMessageTest.cs
index faf911d3..faff97a9 100644
--- a/src/Messaging/Test/WorkflowRequestMessageTest.cs
+++ b/src/Messaging/Tests/WorkflowRequestMessageTest.cs
@@ -7,7 +7,7 @@
using Monai.Deploy.Messaging.Events;
using Xunit;
-namespace Monai.Deploy.Messaging.Test
+namespace Monai.Deploy.Messaging.Tests
{
public class WorkflowRequestMessageTest
{
diff --git a/src/Monai.Deploy.Messaging.sln b/src/Monai.Deploy.Messaging.sln
index eaf1c488..022c3c41 100644
--- a/src/Monai.Deploy.Messaging.sln
+++ b/src/Monai.Deploy.Messaging.sln
@@ -10,7 +10,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A73CC5
AssemblyInfo.cs = AssemblyInfo.cs
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.Messaging.Test", "Messaging\Test\Monai.Deploy.Messaging.Test.csproj", "{33516A8E-BCBF-4B63-A358-C3A3B03F63A5}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{50EFF021-4EA8-4D89-8312-306C4EAD8494}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.Messaging.RabbitMQ", "Plugins\RabbitMQ\Monai.Deploy.Messaging.RabbitMQ.csproj", "{AE474A08-0325-486F-A895-6E792EAE8349}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.Messaging.RabbitMQ.Tests", "Plugins\RabbitMQ\Tests\Monai.Deploy.Messaging.RabbitMQ.Tests.csproj", "{64BEE9D2-88B4-4035-A1C8-E00413B5E206}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.Messaging.Tests", "Messaging\Tests\Monai.Deploy.Messaging.Tests.csproj", "{08E0CD28-7CCD-4718-BCFF-15EF416C4D8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -22,14 +28,26 @@ Global
{0EF89B52-A68A-4F5A-B5C6-49D6AC3582C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0EF89B52-A68A-4F5A-B5C6-49D6AC3582C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0EF89B52-A68A-4F5A-B5C6-49D6AC3582C8}.Release|Any CPU.Build.0 = Release|Any CPU
- {33516A8E-BCBF-4B63-A358-C3A3B03F63A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {33516A8E-BCBF-4B63-A358-C3A3B03F63A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {33516A8E-BCBF-4B63-A358-C3A3B03F63A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {33516A8E-BCBF-4B63-A358-C3A3B03F63A5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE474A08-0325-486F-A895-6E792EAE8349}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE474A08-0325-486F-A895-6E792EAE8349}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE474A08-0325-486F-A895-6E792EAE8349}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE474A08-0325-486F-A895-6E792EAE8349}.Release|Any CPU.Build.0 = Release|Any CPU
+ {64BEE9D2-88B4-4035-A1C8-E00413B5E206}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {64BEE9D2-88B4-4035-A1C8-E00413B5E206}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {64BEE9D2-88B4-4035-A1C8-E00413B5E206}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {64BEE9D2-88B4-4035-A1C8-E00413B5E206}.Release|Any CPU.Build.0 = Release|Any CPU
+ {08E0CD28-7CCD-4718-BCFF-15EF416C4D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {08E0CD28-7CCD-4718-BCFF-15EF416C4D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {08E0CD28-7CCD-4718-BCFF-15EF416C4D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {08E0CD28-7CCD-4718-BCFF-15EF416C4D8F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {AE474A08-0325-486F-A895-6E792EAE8349} = {50EFF021-4EA8-4D89-8312-306C4EAD8494}
+ {64BEE9D2-88B4-4035-A1C8-E00413B5E206} = {50EFF021-4EA8-4D89-8312-306C4EAD8494}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E1105263-9CBF-45AA-BAC3-BD8504C1B962}
EndGlobalSection
diff --git a/src/Messaging/Configuration/ConfigurationKeys.cs b/src/Plugins/RabbitMQ/ConfigurationKeys.cs
similarity index 78%
rename from src/Messaging/Configuration/ConfigurationKeys.cs
rename to src/Plugins/RabbitMQ/ConfigurationKeys.cs
index 0bed43ac..93068429 100644
--- a/src/Messaging/Configuration/ConfigurationKeys.cs
+++ b/src/Plugins/RabbitMQ/ConfigurationKeys.cs
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium
// SPDX-License-Identifier: Apache License 2.0
-namespace Monai.Deploy.Messaging.Configuration
+namespace Monai.Deploy.Messaging.RabbitMQ
{
internal static class ConfigurationKeys
{
@@ -10,10 +10,9 @@ internal static class ConfigurationKeys
public static readonly string Password = "password";
public static readonly string VirtualHost = "virtualHost";
public static readonly string Exchange = "exchange";
- public static readonly string ExportRequestQueue = "exportRequestQueue";
public static readonly string UseSSL = "useSSL";
public static readonly string Port = "port";
public static readonly string[] PublisherRequiredKeys = new[] { EndPoint, Username, Password, VirtualHost, Exchange };
- public static readonly string[] SubscriberRequiredKeys = new[] { EndPoint, Username, Password, VirtualHost, Exchange, ExportRequestQueue };
+ public static readonly string[] SubscriberRequiredKeys = new[] { EndPoint, Username, Password, VirtualHost, Exchange };
}
}
diff --git a/src/Plugins/RabbitMQ/InternalVisible.cs b/src/Plugins/RabbitMQ/InternalVisible.cs
new file mode 100644
index 00000000..4c3d09be
--- /dev/null
+++ b/src/Plugins/RabbitMQ/InternalVisible.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.RabbitMQ.Tests")]
diff --git a/src/Messaging/Common/Log.cs b/src/Plugins/RabbitMQ/Logger.cs
similarity index 91%
rename from src/Messaging/Common/Log.cs
rename to src/Plugins/RabbitMQ/Logger.cs
index 0bc9795d..7d4b0b9d 100644
--- a/src/Messaging/Common/Log.cs
+++ b/src/Plugins/RabbitMQ/Logger.cs
@@ -1,25 +1,26 @@
// SPDX-FileCopyrightText: © 2022 MONAI Consortium
// SPDX-License-Identifier: Apache License 2.0
+using System;
using Microsoft.Extensions.Logging;
-namespace Monai.Deploy.Messaging.Common
+namespace Monai.Deploy.Messaging.RabbitMQ
{
- public static partial class Log
+ public static partial class Logger
{
internal static readonly string LoggingScopeMessageApplication = "Message ID={0}. Application ID={1}.";
[LoggerMessage(EventId = 10000, Level = LogLevel.Information, Message = "Publishing message to {endpoint}/{virtualHost}. Exchange={exchange}, Routing Key={topic}.")]
- public static partial void PublshingRabbitMq(this ILogger logger, string endpoint, string virtualHost, string exchange, string topic);
+ public static partial void PublshingRabbitMQ(this ILogger logger, string endpoint, string virtualHost, string exchange, string topic);
[LoggerMessage(EventId = 10001, Level = LogLevel.Information, Message = "{ServiceName} connecting to {endpoint}/{virtualHost}.")]
- public static partial void ConnectingToRabbitMq(this ILogger logger, string serviceNAme, string endpoint, string virtualHost);
+ public static partial void ConnectingToRabbitMQ(this ILogger logger, string serviceNAme, string endpoint, string virtualHost);
[LoggerMessage(EventId = 10002, Level = LogLevel.Information, Message = "Message received from queue {queue} for {topic}.")]
public static partial void MessageReceivedFromQueue(this ILogger logger, string queue, string topic);
[LoggerMessage(EventId = 10003, Level = LogLevel.Information, Message = "Listening for messages from {endpoint}/{virtualHost}. Exchange={exchange}, Queue={queue}, Routing Key={topic}.")]
- public static partial void SubscribeToRabbitMqQueue(this ILogger logger, string endpoint, string virtualHost, string exchange, string queue, string topic);
+ public static partial void SubscribeToRabbitMQQueue(this ILogger logger, string endpoint, string virtualHost, string exchange, string queue, string topic);
[LoggerMessage(EventId = 10004, Level = LogLevel.Information, Message = "Sending message acknowledgement for message {messageId}.")]
public static partial void SendingAcknowledgement(this ILogger logger, string messageId);
diff --git a/src/Plugins/RabbitMQ/Monai.Deploy.Messaging.RabbitMQ.csproj b/src/Plugins/RabbitMQ/Monai.Deploy.Messaging.RabbitMQ.csproj
new file mode 100644
index 00000000..8493e3c5
--- /dev/null
+++ b/src/Plugins/RabbitMQ/Monai.Deploy.Messaging.RabbitMQ.csproj
@@ -0,0 +1,39 @@
+
+
+
+ netstandard2.1
+ enable
+ latest
+ Monai.Deploy.Messaging.RabbitMQ
+ false
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+
+ Monai.Deploy.Messaging.RabbitMQ
+ 0.1.0
+ MONAI Consortium
+ MONAI Consortium
+ true
+ RabbitMQ plug-in for MONAI Deploy Messaging Service.
+ MONAI Consortium
+ https://github.com/Project-MONAI/monai-deploy-messaging
+ https://github.com/Project-MONAI/monai-deploy-messaging
+ Apache-2.0
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Plugins/RabbitMQ/PublisherServiceRegistration.cs b/src/Plugins/RabbitMQ/PublisherServiceRegistration.cs
new file mode 100644
index 00000000..66528fbb
--- /dev/null
+++ b/src/Plugins/RabbitMQ/PublisherServiceRegistration.cs
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: © 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+using Microsoft.Extensions.DependencyInjection;
+using Monai.Deploy.Messaging.API;
+
+namespace Monai.Deploy.Messaging.RabbitMQ
+{
+ public class PublisherServiceRegistration : PublisherServiceRegistrationBase
+ {
+ public PublisherServiceRegistration(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName)
+ {
+ }
+
+ public override IServiceCollection Configure(IServiceCollection services)
+ {
+ return services
+ .AddSingleton()
+ .AddSingleton();
+ }
+ }
+}
diff --git a/src/Plugins/RabbitMQ/README.md b/src/Plugins/RabbitMQ/README.md
new file mode 100644
index 00000000..e67db370
--- /dev/null
+++ b/src/Plugins/RabbitMQ/README.md
@@ -0,0 +1,44 @@
+# RabbitMQ for MONAI Deploy
+
+## Overview
+
+The RabbitMQ plug-in for MONAI Deploy is based on the [RabbitMQ](https://www.rabbitmq.com/) solution.
+
+## Configuration
+
+
+The following configurations are required to run the MinIO plug-in.
+
+
+### Default Configurations for the Message Publisher
+
+The `publisherServiceAssemblyName` should be set to `Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessagePublisherService, Monai.Deploy.Messaging.RabbitMQ`.
+
+The following configurations are required for the publisher service.
+
+| Key | Description | Sample Value |
+| ----------- | -------------------------------------------------------------------------------- | ------------ |
+| endpoint | Host name/IP and port. | localhost |
+| username | Username | username |
+| password | Password | password |
+| virtualHost | Name of the virtual host | monaideploy |
+| exchange | Name of the exchange | monaideploy |
+| useSSL | (optional) use secured connection or not | false |
+| port | (optional) port number with default of 5672 for plaint-text and 5671 for secured | 5672 |
+
+
+### Default Configurations for the Message Subscriber
+
+The `subscriberServiceAssemblyName` should be set to `Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessageSubscriberService, Monai.Deploy.Messaging.RabbitMQ`.
+
+The following configurations are required for the subscriber service.
+
+| Key | Description | Sample Value |
+| ----------- | -------------------------------------------------------------------------------- | ------------ |
+| endpoint | Host name/IP and port. | localhost |
+| username | Username | username |
+| password | Password | password |
+| virtualHost | Name of the virtual host | monaideploy |
+| exchange | Name of the exchange | monaideploy |
+| useSSL | (optional) use secured connection or not | false |
+| port | (optional) port number with default of 5672 for plaint-text and 5671 for secured | 5672 |
diff --git a/src/Messaging/RabbitMq/RabbitMqConnectionFactory.cs b/src/Plugins/RabbitMQ/RabbitMqConnectionFactory.cs
similarity index 83%
rename from src/Messaging/RabbitMq/RabbitMqConnectionFactory.cs
rename to src/Plugins/RabbitMQ/RabbitMqConnectionFactory.cs
index 9cdda71a..4d38de12 100644
--- a/src/Messaging/RabbitMq/RabbitMqConnectionFactory.cs
+++ b/src/Plugins/RabbitMQ/RabbitMqConnectionFactory.cs
@@ -1,18 +1,19 @@
// SPDX-FileCopyrightText: © 2022 MONAI Consortium
// SPDX-License-Identifier: Apache License 2.0
+using System;
using System.Collections.Concurrent;
+using System.Linq;
+using System.Net.Security;
using System.Security.Cryptography;
using System.Text;
using Ardalis.GuardClauses;
using Microsoft.Extensions.Logging;
-using Monai.Deploy.Messaging.Common;
using RabbitMQ.Client;
-using System.Net.Security;
-namespace Monai.Deploy.Messaging.RabbitMq
+namespace Monai.Deploy.Messaging.RabbitMQ
{
- public interface IRabbitMqConnectionFactory
+ public interface IRabbitMQConnectionFactory
{
///
/// Creates a new channel for RabbitMQ client.
@@ -24,39 +25,38 @@ public interface IRabbitMqConnectionFactory
/// Password
/// Virtual host
/// Encrypt communication
- /// Port Number
+ /// Port Number
/// Instance of .
- IModel CreateChannel(string hostName, string username, string password, string virtualHost, string useSSL, string portnumber);
+ IModel CreateChannel(string hostName, string username, string password, string virtualHost, string useSSL, string portNumber);
}
- public class RabbitMqConnectionFactory : IRabbitMqConnectionFactory, IDisposable
+ public class RabbitMQConnectionFactory : IRabbitMQConnectionFactory, IDisposable
{
private readonly ConcurrentDictionary> _connectionFactoriess;
private readonly ConcurrentDictionary> _connections;
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private bool _disposedValue;
- public RabbitMqConnectionFactory(ILogger logger)
+ public RabbitMQConnectionFactory(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_connectionFactoriess = new ConcurrentDictionary>();
_connections = new ConcurrentDictionary>();
}
- public IModel CreateChannel(string hostName, string username, string password, string virtualHost, string useSSL, string portnumber )
+ public IModel CreateChannel(string hostName, string username, string password, string virtualHost, string useSSL, string portNumber)
{
Guard.Against.NullOrWhiteSpace(hostName, nameof(hostName));
Guard.Against.NullOrWhiteSpace(username, nameof(username));
Guard.Against.NullOrWhiteSpace(password, nameof(password));
Guard.Against.NullOrWhiteSpace(virtualHost, nameof(virtualHost));
-
var key = $"{hostName}{username}{HashPassword(password)}{virtualHost}";
var connection = _connections.AddOrUpdate(key,
x =>
{
- return CreatConnection(hostName, username, password, virtualHost, key, useSSL, portnumber);
+ return CreatConnection(hostName, username, password, virtualHost, key, useSSL, portNumber);
},
(updateKey, updateConnection) =>
{
@@ -66,26 +66,28 @@ public IModel CreateChannel(string hostName, string username, string password, s
}
else
{
- return CreatConnection(hostName, username, password, virtualHost, key, useSSL, portnumber);
+ return CreatConnection(hostName, username, password, virtualHost, key, useSSL, portNumber);
}
});
return connection.Value.CreateModel();
}
- private Lazy CreatConnection(string hostName, string username, string password, string virtualHost, string key, string useSSL, string portnumber)
+ private Lazy CreatConnection(string hostName, string username, string password, string virtualHost, string key, string useSSL, string portNumber)
{
- int port;
- Boolean SslEnabled;
- Boolean.TryParse(useSSL, out SslEnabled);
- if (!Int32.TryParse(portnumber, out port))
+ if (!bool.TryParse(useSSL, out var sslEnabled))
+ {
+ sslEnabled = false;
+ }
+
+ if (!Int32.TryParse(portNumber, out var port))
{
- port = SslEnabled ? 5671 : 5672; // 5671 is default port for SSL/TLS , 5672 is default port for PLAIN.
+ port = sslEnabled ? 5671 : 5672; // 5671 is default port for SSL/TLS , 5672 is default port for PLAIN.
}
- SslOption sslOptions = new SslOption
+ var sslOptions = new SslOption
{
- Enabled = SslEnabled,
+ Enabled = sslEnabled,
ServerName = hostName,
AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNameMismatch | SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable
};
diff --git a/src/Messaging/RabbitMq/RabbitMqMessagePublisherService.cs b/src/Plugins/RabbitMQ/RabbitMqMessagePublisherService.cs
similarity index 80%
rename from src/Messaging/RabbitMq/RabbitMqMessagePublisherService.cs
rename to src/Plugins/RabbitMQ/RabbitMqMessagePublisherService.cs
index 0fdf5ec3..8c1e6865 100644
--- a/src/Messaging/RabbitMq/RabbitMqMessagePublisherService.cs
+++ b/src/Plugins/RabbitMQ/RabbitMqMessagePublisherService.cs
@@ -1,37 +1,39 @@
// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium
// SPDX-License-Identifier: Apache License 2.0
+using System;
using System.Globalization;
+using System.Threading.Tasks;
using Ardalis.GuardClauses;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using Monai.Deploy.Messaging.Common;
+using Monai.Deploy.Messaging.API;
using Monai.Deploy.Messaging.Configuration;
using Monai.Deploy.Messaging.Messages;
using RabbitMQ.Client;
-namespace Monai.Deploy.Messaging.RabbitMq
+namespace Monai.Deploy.Messaging.RabbitMQ
{
- public class RabbitMqMessagePublisherService : IMessageBrokerPublisherService
+ public class RabbitMQMessagePublisherService : IMessageBrokerPublisherService
{
private const int PersistentDeliveryMode = 2;
- private readonly ILogger _logger;
- private readonly IRabbitMqConnectionFactory _rabbitMqConnectionFactory;
+ private readonly ILogger _logger;
+ private readonly IRabbitMQConnectionFactory _rabbitMqConnectionFactory;
private readonly string _endpoint;
private readonly string _username;
private readonly string _password;
private readonly string _virtualHost;
private readonly string _exchange;
- private readonly string _useSSL = string.Empty;
- private readonly string _portNumber = string.Empty;
+ private readonly string _useSSL;
+ private readonly string _portNumber;
private bool _disposedValue;
public string Name => "Rabbit MQ Publisher";
- public RabbitMqMessagePublisherService(IOptions options,
- ILogger logger,
- IRabbitMqConnectionFactory rabbitMqConnectionFactory)
+ public RabbitMQMessagePublisherService(IOptions options,
+ ILogger logger,
+ IRabbitMQConnectionFactory rabbitMqConnectionFactory)
{
Guard.Against.Null(options, nameof(options));
@@ -46,15 +48,23 @@ public RabbitMqMessagePublisherService(IOptions _logger;
+ private readonly ILogger _logger;
private readonly string _endpoint;
private readonly string _virtualHost;
private readonly string _exchange;
- private readonly string _useSSL = string.Empty;
- private readonly string _portNumber = string.Empty;
+ private readonly string _useSSL;
+ private readonly string _portNumber;
private readonly IModel _channel;
private bool _disposedValue;
public string Name => "Rabbit MQ Subscriber";
- public RabbitMqMessageSubscriberService(IOptions options,
- ILogger logger,
- IRabbitMqConnectionFactory rabbitMqConnectionFactory)
+ public RabbitMQMessageSubscriberService(IOptions options,
+ ILogger logger,
+ IRabbitMQConnectionFactory rabbitMqConnectionFactory)
{
Guard.Against.Null(options, nameof(options));
@@ -42,18 +46,26 @@ public RabbitMqMessageSubscriberService(IOptions
{
- using var loggerScope = _logger.BeginScope(string.Format(CultureInfo.InvariantCulture, Log.LoggingScopeMessageApplication, eventArgs.BasicProperties.MessageId, eventArgs.BasicProperties.AppId));
+ using var loggerScope = _logger.BeginScope(string.Format(CultureInfo.InvariantCulture, Logger.LoggingScopeMessageApplication, eventArgs.BasicProperties.MessageId, eventArgs.BasicProperties.AppId));
_logger.MessageReceivedFromQueue(queueDeclareResult.QueueName, eventArgs.RoutingKey);
@@ -116,7 +128,7 @@ public void Subscribe(string[] topics, string queue, Action messageReceivedCallback, ushort prefetchCount = 0)
@@ -133,7 +145,7 @@ public void SubscribeAsync(string[] topics, string queue, Func
{
- using var loggerScope = _logger.BeginScope(string.Format(CultureInfo.InvariantCulture, Log.LoggingScopeMessageApplication, eventArgs.BasicProperties.MessageId, eventArgs.BasicProperties.AppId));
+ using var loggerScope = _logger.BeginScope(string.Format(CultureInfo.InvariantCulture, Logger.LoggingScopeMessageApplication, eventArgs.BasicProperties.MessageId, eventArgs.BasicProperties.AppId));
_logger.MessageReceivedFromQueue(queueDeclareResult.QueueName, eventArgs.RoutingKey);
@@ -162,7 +174,7 @@ public void SubscribeAsync(string[] topics, string queue, Func();
+ services.AddSingleton();
+ return services;
+ }
+ }
+}
diff --git a/src/Plugins/RabbitMQ/Tests/Monai.Deploy.Messaging.RabbitMQ.Tests.csproj b/src/Plugins/RabbitMQ/Tests/Monai.Deploy.Messaging.RabbitMQ.Tests.csproj
new file mode 100644
index 00000000..5d441c17
--- /dev/null
+++ b/src/Plugins/RabbitMQ/Tests/Monai.Deploy.Messaging.RabbitMQ.Tests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
diff --git a/src/Messaging/Test/RabbitMq/RabbitMqMessagePublisherServiceTest.cs b/src/Plugins/RabbitMQ/Tests/RabbitMqMessagePublisherServiceTest.cs
similarity index 82%
rename from src/Messaging/Test/RabbitMq/RabbitMqMessagePublisherServiceTest.cs
rename to src/Plugins/RabbitMQ/Tests/RabbitMqMessagePublisherServiceTest.cs
index dedb9763..e6fb5915 100644
--- a/src/Messaging/Test/RabbitMq/RabbitMqMessagePublisherServiceTest.cs
+++ b/src/Plugins/RabbitMQ/Tests/RabbitMqMessagePublisherServiceTest.cs
@@ -1,41 +1,38 @@
// SPDX-FileCopyrightText: © 2022 MONAI Consortium
// SPDX-License-Identifier: Apache License 2.0
-using System;
-using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Monai.Deploy.Messaging.Configuration;
using Monai.Deploy.Messaging.Messages;
-using Monai.Deploy.Messaging.RabbitMq;
using Moq;
using RabbitMQ.Client;
using Xunit;
-namespace Monai.Deploy.Messaging.Test.RabbitMq
+namespace Monai.Deploy.Messaging.RabbitMQ.Tests
{
- public class RabbitMqMessagePublisherServiceTest
+ public class RabbitMQMessagePublisherServiceTest
{
private readonly IOptions _options;
- private readonly Mock> _logger;
- private readonly Mock _connectionFactory;
+ private readonly Mock> _logger;
+ private readonly Mock _connectionFactory;
private readonly Mock _model;
- public RabbitMqMessagePublisherServiceTest()
+ public RabbitMQMessagePublisherServiceTest()
{
_options = Options.Create(new MessageBrokerServiceConfiguration());
- _logger = new Mock>();
- _connectionFactory = new Mock();
+ _logger = new Mock>();
+ _connectionFactory = new Mock();
_model = new Mock();
- _connectionFactory.Setup(p => p.CreateChannel(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(),It.IsAny(),It.IsAny()))
+ _connectionFactory.Setup(p => p.CreateChannel(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
.Returns(_model.Object);
}
[Fact(DisplayName = "Fails to validate when required keys are missing")]
public void FailsToValidateWhenRequiredKeysAreMissing()
{
- Assert.Throws(() => new RabbitMqMessagePublisherService(_options, _logger.Object, _connectionFactory.Object));
+ Assert.Throws(() => new RabbitMQMessagePublisherService(_options, _logger.Object, _connectionFactory.Object));
}
[Fact(DisplayName = "Publishes a message")]
@@ -56,7 +53,7 @@ public async Task PublishesAMessage()
It.IsAny(),
It.IsAny>()));
- var service = new RabbitMqMessagePublisherService(_options, _logger.Object, _connectionFactory.Object);
+ var service = new RabbitMQMessagePublisherService(_options, _logger.Object, _connectionFactory.Object);
var jsonMessage = new JsonMessage("hello world", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
var message = jsonMessage.ToMessage();
diff --git a/src/Messaging/Test/RabbitMq/RabbitMqMessageSubscriberServiceTest.cs b/src/Plugins/RabbitMQ/Tests/RabbitMqMessageSubscriberServiceTest.cs
similarity index 85%
rename from src/Messaging/Test/RabbitMq/RabbitMqMessageSubscriberServiceTest.cs
rename to src/Plugins/RabbitMQ/Tests/RabbitMqMessageSubscriberServiceTest.cs
index 4c9c0d6e..01da9a6e 100644
--- a/src/Messaging/Test/RabbitMq/RabbitMqMessageSubscriberServiceTest.cs
+++ b/src/Plugins/RabbitMQ/Tests/RabbitMqMessageSubscriberServiceTest.cs
@@ -1,44 +1,40 @@
// SPDX-FileCopyrightText: © 2022 MONAI Consortium
// SPDX-License-Identifier: Apache License 2.0
-using System;
-using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Monai.Deploy.Messaging.Configuration;
using Monai.Deploy.Messaging.Messages;
-using Monai.Deploy.Messaging.RabbitMq;
using Moq;
using RabbitMQ.Client;
using Xunit;
-namespace Monai.Deploy.Messaging.Test.RabbitMq
+namespace Monai.Deploy.Messaging.RabbitMQ.Tests
{
- public class RabbitMqMessageSubscriberServiceTest
+ public class RabbitMQMessageSubscriberServiceTest
{
private readonly IOptions _options;
- private readonly Mock> _logger;
- private readonly Mock _connectionFactory;
+ private readonly Mock> _logger;
+ private readonly Mock _connectionFactory;
private readonly Mock _model;
- public RabbitMqMessageSubscriberServiceTest()
+ public RabbitMQMessageSubscriberServiceTest()
{
_options = Options.Create(new MessageBrokerServiceConfiguration());
- _logger = new Mock>();
- _connectionFactory = new Mock();
+ _logger = new Mock>();
+ _connectionFactory = new Mock();
_model = new Mock();
_connectionFactory.Setup(p => p.CreateChannel(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
.Returns(_model.Object);
-
}
[Fact(DisplayName = "Fails to validate when required keys are missing")]
public void FailsToValidateWhenRequiredKeysAreMissing()
{
- Assert.Throws(() => new RabbitMqMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object));
+ Assert.Throws(() => new RabbitMQMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object));
}
[Fact(DisplayName = "Cleanup connections on Dispose")]
@@ -49,9 +45,8 @@ public void CleanupOnDispose()
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Password, "password");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.VirtualHost, "virtual-host");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Exchange, "exchange");
- _options.Value.SubscriberSettings.Add(ConfigurationKeys.ExportRequestQueue, "export-request-queue");
- var service = new RabbitMqMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
+ var service = new RabbitMQMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
service.Dispose();
_model.Verify(p => p.Close(), Times.Once());
@@ -66,7 +61,6 @@ public void SubscribesToATopic()
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Password, "password");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.VirtualHost, "virtual-host");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Exchange, "exchange");
- _options.Value.SubscriberSettings.Add(ConfigurationKeys.ExportRequestQueue, "export-request-queue");
var jsonMessage = new JsonMessage("hello world", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "1");
var message = jsonMessage.ToMessage();
@@ -75,7 +69,7 @@ public void SubscribesToATopic()
basicProperties.SetupGet(p => p.AppId).Returns(jsonMessage.ApplicationId);
basicProperties.SetupGet(p => p.ContentType).Returns(jsonMessage.ContentType);
basicProperties.SetupGet(p => p.CorrelationId).Returns(jsonMessage.CorrelationId);
- basicProperties.SetupGet(p => p.Headers["CreationDateTime"]).Returns(Encoding.UTF8.GetBytes(jsonMessage.CreationDateTime.ToString("o", System.Globalization.CultureInfo.InvariantCulture)));
+ basicProperties.SetupGet(p => p.Headers["CreationDateTime"]).Returns(Encoding.UTF8.GetBytes(jsonMessage.CreationDateTime.ToString("o", CultureInfo.InvariantCulture)));
_model.Setup(p => p.QueueDeclare(
It.IsAny(),
@@ -107,7 +101,7 @@ public void SubscribesToATopic()
consumer.HandleBasicDeliver(tag, Convert.ToUInt64(jsonMessage.DeliveryTag, CultureInfo.InvariantCulture), false, "exchange", "topic", basicProperties.Object, new ReadOnlyMemory(message.Body));
});
- var service = new RabbitMqMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
+ var service = new RabbitMQMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
service.Subscribe("topic", "queue", (args) =>
{
@@ -123,7 +117,7 @@ public void SubscribesToATopic()
service.SubscribeAsync("topic", "queue", async (args) =>
{
- await System.Threading.Tasks.Task.Run(() =>
+ await Task.Run(() =>
{
Assert.Equal(message.ApplicationId, args.Message.ApplicationId);
Assert.Equal(message.ContentType, args.Message.ContentType);
@@ -145,7 +139,6 @@ public void AcknowledgeAMessage()
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Password, "password");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.VirtualHost, "virtual-host");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Exchange, "exchange");
- _options.Value.SubscriberSettings.Add(ConfigurationKeys.ExportRequestQueue, "export-request-queue");
var jsonMessage = new JsonMessage("hello world", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "1");
var message = jsonMessage.ToMessage();
@@ -154,7 +147,7 @@ public void AcknowledgeAMessage()
It.IsAny(),
It.IsAny()));
- var service = new RabbitMqMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
+ var service = new RabbitMQMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
service.Acknowledge(message);
@@ -169,7 +162,6 @@ public void RejectAMessage()
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Password, "password");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.VirtualHost, "virtual-host");
_options.Value.SubscriberSettings.Add(ConfigurationKeys.Exchange, "exchange");
- _options.Value.SubscriberSettings.Add(ConfigurationKeys.ExportRequestQueue, "export-request-queue");
var jsonMessage = new JsonMessage("hello world", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "1");
var message = jsonMessage.ToMessage();
@@ -179,7 +171,7 @@ public void RejectAMessage()
It.IsAny(),
It.IsAny()));
- var service = new RabbitMqMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
+ var service = new RabbitMQMessageSubscriberService(_options, _logger.Object, _connectionFactory.Object);
service.Reject(message);
diff --git a/src/Plugins/RabbitMQ/Tests/ServiceRegistrationTest.cs b/src/Plugins/RabbitMQ/Tests/ServiceRegistrationTest.cs
new file mode 100644
index 00000000..37016998
--- /dev/null
+++ b/src/Plugins/RabbitMQ/Tests/ServiceRegistrationTest.cs
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: � 2022 MONAI Consortium
+// SPDX-License-Identifier: Apache License 2.0
+
+using System.IO.Abstractions.TestingHelpers;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Moq;
+using Xunit;
+
+namespace Monai.Deploy.Messaging.RabbitMQ.Tests
+{
+#pragma warning disable CS8604 // Possible null reference argument.
+
+ public class PublisherServiceRegistrationTest : ServiceRegistrationTest
+ {
+ [Fact(DisplayName = "Shall be able to Add MinIO as default storage service")]
+ public void ShallAddRabbitMQAsDefaultMessagingService()
+ {
+ var serviceCollection = new Mock();
+ serviceCollection.Setup(p => p.Add(It.IsAny()));
+
+ var returnedServiceCollection = serviceCollection.Object.AddMonaiDeployMessageBrokerPublisherService(ServiceType.AssemblyQualifiedName, FileSystem);
+
+ Assert.Same(serviceCollection.Object, returnedServiceCollection);
+
+ serviceCollection.Verify(p => p.Add(It.IsAny()), Times.Exactly(2));
+ }
+ }
+
+ public class SubscriberServiceRegistrationTest : ServiceRegistrationTest
+ {
+ [Fact(DisplayName = "Shall be able to Add MinIO as default storage service")]
+ public void ShallAddRabbitMQAsDefaultMessagingService()
+ {
+ var serviceCollection = new Mock();
+ serviceCollection.Setup(p => p.Add(It.IsAny()));
+
+ var returnedServiceCollection = serviceCollection.Object.AddMonaiDeployMessageBrokerSubscriberService(ServiceType.AssemblyQualifiedName, FileSystem);
+
+ Assert.Same(serviceCollection.Object, returnedServiceCollection);
+
+ serviceCollection.Verify(p => p.Add(It.IsAny()), Times.Exactly(2));
+ }
+ }
+
+ public abstract class ServiceRegistrationTest
+ {
+ protected Type ServiceType { get; }
+
+ protected MockFileSystem FileSystem { get; }
+
+ protected ServiceRegistrationTest()
+ {
+ ServiceType = typeof(T);
+ FileSystem = new MockFileSystem();
+ var assemblyFilePath = Path.Combine(SR.PlugInDirectoryPath, ServiceType.Assembly.ManifestModule.Name);
+ var assemblyData = GetAssemblyeBytes(ServiceType.Assembly);
+ FileSystem.Directory.CreateDirectory(SR.PlugInDirectoryPath);
+ FileSystem.File.WriteAllBytes(assemblyFilePath, assemblyData);
+ }
+
+ private static byte[] GetAssemblyeBytes(Assembly assembly)
+ {
+ return File.ReadAllBytes(assembly.Location);
+ }
+
+ protected void AddOptions(Dictionary settings, string[] requiredKeys)
+ {
+ foreach (var key in requiredKeys)
+ {
+ if (settings.ContainsKey(key)) continue;
+
+ settings.Add(key, Guid.NewGuid().ToString());
+ }
+ }
+ }
+
+#pragma warning restore CS8604 // Possible null reference argument.
+}
diff --git a/src/Plugins/package.sh b/src/Plugins/package.sh
new file mode 100755
index 00000000..2d05a95a
--- /dev/null
+++ b/src/Plugins/package.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# SPDX-FileCopyrightText: © 2022 MONAI Consortium
+# SPDX-License-Identifier: Apache License 2.0
+
+export PACKAGEDIR="${PWD}/release"
+
+[ -d $PACKAGEDIR ] && rm -rf $PACKAGEDIR && echo "Removing $PACKAGEDIR..."
+
+
+find . -type f -name '*.csproj' ! -name '*.Tests.csproj' -exec bash -c '
+ for project do
+ echo Processing $project...
+ projectName=$(basename -s .csproj $project)
+ projectPACKAGEDIR="${PACKAGEDIR}/${projectName}"
+ zipPath="${PACKAGEDIR}/${projectName}.zip"
+ mkdir -p $projectPACKAGEDIR
+ echo Publishing $project...
+ dotnet publish $project -c Release -o $projectPACKAGEDIR --nologo
+ pushd $projectPACKAGEDIR
+ rm -f Microsoft*.dll System*.dll JetBrains*.dll
+ zip -r $zipPath *
+ popd
+ done
+' _ {} +
\ No newline at end of file