Skip to content

Commit

Permalink
Rename to Maddox.NServiceBus and introduce the separation between con…
Browse files Browse the repository at this point in the history
…figuration manager and endpoint (#31)

* Some ideas

* Introduce configuration managers and endpoints as different classes with different responsibilities

* Rename to Maddox.NServiceBus

* Rename NuGet package
  • Loading branch information
mauroservienti authored Dec 14, 2023
1 parent 1610148 commit e81e25c
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 199 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<img src="assets/icon.png" width="100" />

# NServiceBoXes.Endpoints
# Maddox.NServiceBus

NServiceBoXes Endpoints simplify [NServiceBus endpoints](https://docs.particular.net/nservicebus/) configuration by providing for supported transports a corresponding NServiceBoXes endpoint with sensible defaults. For example, creating and starting a RabbitMQ endpoint could be as easy as:
Maddox simplifies [NServiceBus endpoints](https://docs.particular.net/nservicebus/) configuration by providing for supported transports a corresponding Maddox endpoint with sensible defaults. For example, creating and starting a RabbitMQ endpoint could be as easy as:

```csharp
var endpoint = new RabbitMqEndpoint("my-endpoint", connectionString: "host=localhost");
Expand All @@ -11,29 +11,29 @@ var endpointInstance = await endpoint.Start();

## Microsoft configuration extension support

NServiceBoXes endpoints can be configured through the [`Microsoft.Extensions.Configuration`](https://www.nuget.org/packages/Microsoft.Extensions.Configuration). The above-presented RabbitMQ endpoint can be configured as follows:
Maddox.NServiceBus can be configured through the [`Microsoft.Extensions.Configuration`](https://www.nuget.org/packages/Microsoft.Extensions.Configuration). The above-presented RabbitMQ endpoint can be configured as follows:

```csharp
Host.CreateDefaultBuilder()
.UseNServiceBus(hostBuilderContext => new RabbitMqEndpoint(hostBuilderContext.Configuration))
.Build();
```

The endpoint will retrieve values from the `IConfiguration` object instance. For more information, refer to the [NServiceBoXes.Endpoints configuration options docmentation](/docs).
The endpoint will retrieve values from the `IConfiguration` object instance. For more information, refer to the [Maddox.NServiceBus configuration options docmentation](/docs).

## Supported endpoints

- [NServiceBoXes.Endpoints.AmazonSQS](https://github.com/mauroservienti/NServiceBoXes.Endpoints.AmazonSQS)
- [NServiceBoXes.Endpoints.RabbitMQ](https://github.com/mauroservienti/NServiceBoXes.Endpoints.RabbitMQ)
- [Maddox.NServiceBus.AmazonSQS](https://github.com/mauroservienti/Maddox.NServiceBus.AmazonSQS)
- [Maddox.NServiceBus.RabbitMQ](https://github.com/mauroservienti/Maddox.NServiceBus.RabbitMQ)

## How to get it

- Pre-releases are available on [Feedz.io](https://feedz.io/) ([public feed](https://f.feedz.io/mauroservienti/pre-releases/nuget/index.json))
- Releases on [NuGet.org](https://www.nuget.org/packages?q=NServiceBoXes)
- Releases on [NuGet.org](https://www.nuget.org/packages?q=Maddox)

## NOTE

> This package is not meant to be used directly. It serves as a base package for other NServiceBoXes Endpoints, such as [NServiceBoXes.Endpoints.AmazonSQS](https://github.com/mauroservienti/NServiceBoXes.Endpoints.AmazonSQS), [NServiceBoXes.Endpoints.RabbitMQ](https://github.com/mauroservienti/NServiceBoXes.Endpoints.RabbitMQ), and many more.
> This package is not meant to be used directly. It serves as a base package for other Maddox.NServiceBus Endpoints, such as [Maddox.NServiceBus.AmazonSQS](https://github.com/mauroservienti/Maddox.NServiceBus.AmazonSQS) or [Maddox.NServiceBus.Endpoints.RabbitMQ](https://github.com/mauroservienti/Maddox.NServiceBus.RabbitMQ).
---

Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Configuration

NServiceBoXes endpoints can be configured through the [`Microsoft.Extensions.Configuration`](https://www.nuget.org/packages/Microsoft.Extensions.Configuration). All settings are defined in the `NServiceBus:EndpointConfiguration` section. Different endpoints supporting different transport may define additional settings. Refer to the endpoint-specific documentation for more details.
Maddox.NServiceBus endpoints can be configured through the [`Microsoft.Extensions.Configuration`](https://www.nuget.org/packages/Microsoft.Extensions.Configuration). All settings are defined in the `NServiceBus:EndpointConfiguration` section. Different endpoints supporting different transport may define additional settings. Refer to the endpoint-specific documentation for more details.

## Endpoint name

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using Microsoft.Extensions.Configuration;
using NServiceBus;
using NServiceBus.Configuration.AdvancedExtensibility;

namespace NServiceBoXes.Endpoints.Tests;
namespace Maddox.NServiceBus.Tests;

public class LearningEndpointTests
{
[Fact]
public void Basic_endpoint_respect_name_and_default_values()
{
var expectedEndpointName = "my-endpoint";
const string expectedEndpointName = "my-endpoint";
var endpoint = new LearningEndpoint(expectedEndpointName);
EndpointConfiguration endpointConfiguration = endpoint;

Expand All @@ -20,7 +21,7 @@ public void Basic_endpoint_respect_name_and_default_values()
[Fact]
public void Using_json_configuration_respect_settings()
{
var expectedEndpointName = "my-endpoint";
const string expectedEndpointName = "my-endpoint";

var config = new ConfigurationBuilder()
.AddJsonFile("endpoint.settings.json")
Expand All @@ -44,7 +45,7 @@ public void Using_json_configuration_respect_settings()
[Fact]
public void Using_env_var_configuration_respect_endpoint_name()
{
var expectedEndpointName = "my-endpoint";
const string expectedEndpointName = "my-endpoint";
Environment.SetEnvironmentVariable("NServiceBus:EndpointConfiguration:EndpointName", expectedEndpointName);

var config = new ConfigurationBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NServiceBoXes.Endpoints\NServiceBoXes.Endpoints.csproj" />
<ProjectReference Include="..\Maddox.NServiceBus\Maddox.NServiceBus.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBoXes.Endpoints", "NServiceBoXes.Endpoints\NServiceBoXes.Endpoints.csproj", "{96B64204-3CA7-4DCD-B2B1-472C68C8548E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Maddox.NServiceBus", "Maddox.NServiceBus\Maddox.NServiceBus.csproj", "{96B64204-3CA7-4DCD-B2B1-472C68C8548E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBoXes.Endpoints.Tests", "NServiceBoXes.Endpoints.Tests\NServiceBoXes.Endpoints.Tests.csproj", "{4A3D3957-A397-48E5-88A5-800DC7CE1705}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Maddox.NServiceBus.Tests", "Maddox.NServiceBus.Tests\Maddox.NServiceBus.Tests.csproj", "{4A3D3957-A397-48E5-88A5-800DC7CE1705}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
165 changes: 165 additions & 0 deletions src/Maddox.NServiceBus/EndpointConfigurationManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using Microsoft.Extensions.Configuration;
using NServiceBus.Serialization;
using NServiceBus.Transport;

namespace Maddox.NServiceBus;

public abstract class EndpointConfigurationManager<TTransport>
where TTransport : TransportDefinition
{
const string NServiceBusEndpointConfigurationSectionName = "NServiceBus:EndpointConfiguration";
Action<TTransport>? transportCustomization;
Func<IConfigurationSection?, TTransport>? transportFactory;
bool _useDefaultSerializer = true;
Action<SerializationExtensions<SystemJsonSerializer>>? _serializerCustomization;
EndpointConfiguration _endpointConfiguration = null!;

static void ConfigureAuditing(EndpointConfiguration endpointConfiguration, IConfigurationSection? endpointConfigurationSection)
{
var auditSection = endpointConfigurationSection?.GetSection("Auditing");
var enableAuditing = bool.Parse(auditSection?["Enabled"] ?? true.ToString());
if (!enableAuditing)
{
return;
}

var auditQueue = auditSection?["AuditQueue"] ?? "audit";
endpointConfiguration.AuditProcessedMessagesTo(auditQueue);
}

static void ConfigureRecoverability(EndpointConfiguration endpointConfiguration, IConfigurationSection? endpointConfigurationSection)
{
var recoverabilitySection = endpointConfigurationSection?.GetSection("Recoverability");

var errorQueue = recoverabilitySection?["ErrorQueue"] ?? "error";
endpointConfiguration.SendFailedMessagesTo(errorQueue);

var recoverabilityConfiguration = endpointConfiguration.Recoverability();

if (recoverabilitySection?.GetSection("Immediate") is { } immediateSection)
{
recoverabilityConfiguration.Immediate(
immediate =>
{
if(immediateSection["NumberOfRetries"] is {} numberOfRetries)
{
immediate.NumberOfRetries(int.Parse(numberOfRetries));
}
});
}

if(recoverabilitySection?.GetSection("Delayed") is { } delayedSection)
{
recoverabilityConfiguration.Delayed(
delayed =>
{
if(delayedSection["NumberOfRetries"] is { } numberOfRetries)
{
delayed.NumberOfRetries(int.Parse(numberOfRetries));
}
if (delayedSection["TimeIncrease"] is {} timeIncrease)
{;
delayed.TimeIncrease(TimeSpan.Parse(timeIncrease));
}
});
}
}

protected abstract TTransport CreateTransport(IConfigurationSection? transportConfigurationSection);

protected static void ApplyCommonTransportSettings(IConfigurationSection? transportConfigurationSection,
TransportDefinition transport)
{
if (transportConfigurationSection?["TransportTransactionMode"] is { } transportTransactionMode)
{
Enum.TryParse(transportTransactionMode, ignoreCase: false, out TransportTransactionMode ttm);
transport.TransportTransactionMode = ttm;
}
}

public void CustomizeTransport(Action<TTransport> transport)
{
this.transportCustomization = transport;
}

public void OverrideTransport(Func<IConfigurationSection?, TTransport> factory)
{
this.transportFactory = factory;
}

public SerializationExtensions<T> ReplaceDefaultSerializer<T>() where T : SerializationDefinition, new()
{
_useDefaultSerializer = false;
return _endpointConfiguration.UseSerialization<T>();
}

public void CustomizeDefaultSerializer(Action<SerializationExtensions<SystemJsonSerializer>>? serializerCustomization)
{
_serializerCustomization = serializerCustomization;
}

void Customize(EndpointConfiguration endpointConfiguration,
IConfigurationSection? endpointConfigurationSection)
{
ConfigureAuditing(endpointConfiguration, endpointConfigurationSection);
ConfigureRecoverability(endpointConfiguration, endpointConfigurationSection);

// create and configure the transport
var transport = transportFactory != null ? transportFactory(endpointConfigurationSection) : CreateTransport(endpointConfigurationSection?.GetSection("Transport"));
transportCustomization?.Invoke(transport);
endpointConfiguration.UseTransport(transport);

// TODO create and configure the persistence

if (_useDefaultSerializer)
{
var serializerConfiguration = endpointConfiguration.UseSerialization<SystemJsonSerializer>();
_serializerCustomization?.Invoke(serializerConfiguration);
}
}

static IConfigurationSection GetMandatoryEndpointConfigurationSection(IConfiguration configuration)
{
var endpointConfigurationSection = configuration.GetSection(NServiceBusEndpointConfigurationSectionName);
if (endpointConfigurationSection == null)
throw new Exception($"Cannot find the required '{NServiceBusEndpointConfigurationSectionName}' configuration section");

return endpointConfigurationSection;
}

static string GetMandatoryEndpointName(IConfigurationSection endpointConfigurationSection)
{
return endpointConfigurationSection["EndpointName"]
?? throw new ArgumentException(
"EndpointName cannot be null. Make sure the " +
$"{NServiceBusEndpointConfigurationSectionName}:EndpointName configuration value is set.");
}

public EndpointConfiguration CreateEndpointConfiguration(string endpointName, IConfiguration? configuration)
{
if (string.IsNullOrWhiteSpace(endpointName))
throw new ArgumentException("Endpoint name is required and cannot be empty", nameof(endpointName));

_endpointConfiguration = new EndpointConfiguration(endpointName);

var endpointConfigurationSection = configuration?.GetSection(NServiceBusEndpointConfigurationSectionName);
Customize(_endpointConfiguration, endpointConfigurationSection);

return _endpointConfiguration;
}

public EndpointConfiguration CreateEndpointConfiguration(IConfiguration configuration)
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));

var endpointConfigurationSection = GetMandatoryEndpointConfigurationSection(configuration);
var endpointName = GetMandatoryEndpointName(endpointConfigurationSection);

_endpointConfiguration = new EndpointConfiguration(endpointName);

Customize(_endpointConfiguration, endpointConfigurationSection);

return _endpointConfiguration;
}
}
14 changes: 14 additions & 0 deletions src/Maddox.NServiceBus/LearningEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Configuration;

namespace Maddox.NServiceBus;

public class LearningEndpoint : NServiceBusEndpoint<LearningEndpointConfigurationManager, LearningTransport>
{
public LearningEndpoint(IConfiguration configuration) : base(configuration)
{
}

public LearningEndpoint(string endpointName, IConfiguration? configuration = null) : base(endpointName, configuration)
{
}
}
25 changes: 25 additions & 0 deletions src/Maddox.NServiceBus/LearningEndpointConfigurationManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Microsoft.Extensions.Configuration;

namespace Maddox.NServiceBus;

public class LearningEndpointConfigurationManager : EndpointConfigurationManager<LearningTransport>
{
protected override LearningTransport CreateTransport(IConfigurationSection? transportConfigurationSection)
{
LearningTransport transport = new ();

ApplyCommonTransportSettings(transportConfigurationSection, transport);

if (transportConfigurationSection?["StorageDirectory"] is { } storageDirectory)
{
transport.StorageDirectory = storageDirectory;
}

if (transportConfigurationSection?["RestrictPayloadSize"] is { } restrictPayloadSize)
{
transport.RestrictPayloadSize = bool.Parse(restrictPayloadSize);
}

return transport;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

<PropertyGroup>
<Authors>Mauro Servienti</Authors>
<Description>NServiceBoXes Endpoints</Description>
<Description>Maddox.NServiceBus</Description>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageIcon>icon.png</PackageIcon>
<PackageTags>NServiceBus NServiceBoXes Endpoints</PackageTags>
<PackageTags>Maddox NServiceBus Endpoints</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
Expand Down
Loading

0 comments on commit e81e25c

Please sign in to comment.