Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move non-IConfiguration logic from DefaultEventBusConfigurator to MandatoryEventBusConfigurator #569

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 1 addition & 133 deletions src/Tingle.EventBus/Configuration/DefaultEventBusConfigurator.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Diagnostics.CodeAnalysis;
using Tingle.EventBus.Serialization;
using Tingle.EventBus.Transports;

namespace Tingle.EventBus.Configuration;
Expand All @@ -14,15 +12,12 @@ namespace Tingle.EventBus.Configuration;
[RequiresUnreferencedCode(MessageStrings.BindingUnreferencedCodeMessage)]
public class DefaultEventBusConfigurator : IEventBusConfigurator
{
private readonly IHostEnvironment environment;
private readonly IEventBusConfigurationProvider configurationProvider;

/// <summary>Creates an instance of <see cref="DefaultEventBusConfigurator"/>.</summary>
/// <param name="environment">The <see cref="IHostEnvironment"/> instance.</param>
/// <param name="configurationProvider">The <see cref="IEventBusConfigurationProvider"/> instance.</param>
public DefaultEventBusConfigurator(IHostEnvironment environment, IEventBusConfigurationProvider configurationProvider)
public DefaultEventBusConfigurator(IEventBusConfigurationProvider configurationProvider)
{
this.environment = environment ?? throw new ArgumentNullException(nameof(environment));
this.configurationProvider = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider));
}

Expand Down Expand Up @@ -51,132 +46,5 @@ public void Configure(EventRegistration registration, EventBusOptions options)
{
configuration.GetSection($"Consumers:{ecr.ConsumerType.FullName}").Bind(ecr);
}

// set transport name
ConfigureTransportName(registration, options);

// set event name and kind
ConfigureEventName(registration, options.Naming);
ConfigureEntityKind(registration);

// set the consumer names
ConfigureConsumerNames(registration, options.Naming);

// set the serializer
ConfigureSerializer(registration);
}

internal void ConfigureTransportName(EventRegistration reg, EventBusOptions options)
{
// If the event transport name has not been specified, attempt to get from the attribute
var type = reg.EventType;
reg.TransportName ??= type.GetCustomAttributes(false).OfType<EventTransportNameAttribute>().SingleOrDefault()?.Name;

// If the event transport name has not been set, try the default one
reg.TransportName ??= options.DefaultTransportName;

// Set the transport name from the default, if not set
if (string.IsNullOrWhiteSpace(reg.TransportName))
{
throw new InvalidOperationException($"Unable to set the transport for event '{type.FullName}'."
+ $" Either set the '{nameof(options.DefaultTransportName)}' option"
+ $" or use the '{typeof(EventTransportNameAttribute).FullName}' on the event.");
}

// Ensure the transport name set has been registered
if (!options.TransportMap.ContainsKey(reg.TransportName))
{
throw new InvalidOperationException($"Transport '{reg.TransportName}' on event '{type.FullName}' must be registered.");
}
}

internal void ConfigureEventName(EventRegistration reg, EventBusNamingOptions options)
{
// set the event name, if not set
if (string.IsNullOrWhiteSpace(reg.EventName))
{
var type = reg.EventType;
// prioritize the attribute if available, otherwise get the type name
var name = type.GetCustomAttributes(false).OfType<EventNameAttribute>().SingleOrDefault()?.EventName;
if (name == null)
{
var typeName = options.UseFullTypeNames ? type.FullName! : type.Name;
typeName = options.TrimCommonSuffixes(typeName);
name = typeName;
name = options.ApplyNamingConvention(name);
name = options.AppendScope(name);
name = options.ReplaceInvalidCharacters(name);
}
reg.EventName = name;
}
}

internal void ConfigureEntityKind(EventRegistration reg)
{
// set the entity kind, if not set and there is an attribute
if (reg.EntityKind == null)
{
var type = reg.EventType;
var kind = type.GetCustomAttributes(false).OfType<EntityKindAttribute>().SingleOrDefault()?.Kind;
if (kind != null)
{
reg.EntityKind = kind;
}
}
}

internal void ConfigureConsumerNames(EventRegistration reg, EventBusNamingOptions options)
{
// ensure we have the event name set
if (string.IsNullOrWhiteSpace(reg.EventName))
{
throw new InvalidOperationException($"The {nameof(reg.EventName)} for must be set before setting names of the consumer.");
}

// prefix is either the one provided or the application name
var prefix = options.ConsumerNamePrefix ?? environment.ApplicationName;

foreach (var ecr in reg.Consumers)
{
// set the consumer name, if not set
if (string.IsNullOrWhiteSpace(ecr.ConsumerName))
{
var type = ecr.ConsumerType;
// prioritize the attribute if available, otherwise get the type name
var name = type.GetCustomAttributes(false).OfType<ConsumerNameAttribute>().SingleOrDefault()?.ConsumerName;
if (name == null)
{
var typeName = options.UseFullTypeNames ? type.FullName! : type.Name;
typeName = options.TrimCommonSuffixes(typeName);
name = options.ConsumerNameSource switch
{
ConsumerNameSource.TypeName => typeName,
ConsumerNameSource.Prefix => prefix,
ConsumerNameSource.PrefixAndTypeName => $"{prefix}.{typeName}",
_ => throw new InvalidOperationException($"'{nameof(options.ConsumerNameSource)}.{options.ConsumerNameSource}' is not supported"),
};
name = options.ApplyNamingConvention(name);
name = options.AppendScope(name);
name = options.ReplaceInvalidCharacters(name);
}
ecr.ConsumerName = name;
}
}
}

internal void ConfigureSerializer(EventRegistration reg)
{
// If the event serializer has not been specified, attempt to get from the attribute
var attrs = reg.EventType.GetCustomAttributes(false);
reg.EventSerializerType ??= attrs.OfType<EventSerializerAttribute>().SingleOrDefault()?.SerializerType;
reg.EventSerializerType ??= typeof(IEventSerializer); // use the default when not provided

// Ensure the serializer is either default or it implements IEventSerializer
if (reg.EventSerializerType != typeof(IEventSerializer)
&& !typeof(IEventSerializer).IsAssignableFrom(reg.EventSerializerType))
{
throw new InvalidOperationException($"The type '{reg.EventSerializerType.FullName}' is used as a serializer "
+ $"but does not implement '{typeof(IEventSerializer).FullName}'");
}
}
}
162 changes: 162 additions & 0 deletions src/Tingle.EventBus/Configuration/MandatoryEventBusConfigurator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Tingle.EventBus.Serialization;
using Tingle.EventBus.Transports;

namespace Tingle.EventBus.Configuration;

/// <summary>
/// Mandatory implementation of <see cref="IEventBusConfigurator"/>.
/// </summary>
internal class MandatoryEventBusConfigurator : IEventBusConfigurator
{
private readonly IHostEnvironment environment;

/// <summary>Creates an instance of <see cref="MandatoryEventBusConfigurator"/>.</summary>
/// <param name="environment">The <see cref="IHostEnvironment"/> instance.</param>
public MandatoryEventBusConfigurator(IHostEnvironment environment)
{
this.environment = environment ?? throw new ArgumentNullException(nameof(environment));
}

/// <inheritdoc/>
public void Configure(EventBusOptions options) { }

/// <inheritdoc/>
public void Configure<TOptions>(IConfiguration configuration, TOptions options) where TOptions : EventBusTransportOptions { }

/// <inheritdoc/>
public void Configure(EventRegistration registration, EventBusOptions options)
{
if (registration is null) throw new ArgumentNullException(nameof(registration));
if (options is null) throw new ArgumentNullException(nameof(options));

// set transport name
ConfigureTransportName(registration, options);

// set event name and kind
ConfigureEventName(registration, options.Naming);
ConfigureEntityKind(registration);

// set the consumer names
ConfigureConsumerNames(registration, options.Naming);

// set the serializer
ConfigureSerializer(registration);
}

internal void ConfigureTransportName(EventRegistration reg, EventBusOptions options)
{
// If the event transport name has not been specified, attempt to get from the attribute
var type = reg.EventType;
reg.TransportName ??= type.GetCustomAttributes(false).OfType<EventTransportNameAttribute>().SingleOrDefault()?.Name;

// If the event transport name has not been set, try the default one
reg.TransportName ??= options.DefaultTransportName;

// Set the transport name from the default, if not set
if (string.IsNullOrWhiteSpace(reg.TransportName))
{
throw new InvalidOperationException($"Unable to set the transport for event '{type.FullName}'."
+ $" Either set the '{nameof(options.DefaultTransportName)}' option"
+ $" or use the '{typeof(EventTransportNameAttribute).FullName}' on the event.");
}

// Ensure the transport name set has been registered
if (!options.TransportMap.ContainsKey(reg.TransportName))
{
throw new InvalidOperationException($"Transport '{reg.TransportName}' on event '{type.FullName}' must be registered.");
}
}

internal void ConfigureEventName(EventRegistration reg, EventBusNamingOptions options)
{
// set the event name, if not set
if (string.IsNullOrWhiteSpace(reg.EventName))
{
var type = reg.EventType;
// prioritize the attribute if available, otherwise get the type name
var name = type.GetCustomAttributes(false).OfType<EventNameAttribute>().SingleOrDefault()?.EventName;
if (name == null)
{
var typeName = options.UseFullTypeNames ? type.FullName! : type.Name;
typeName = options.TrimCommonSuffixes(typeName);
name = typeName;
name = options.ApplyNamingConvention(name);
name = options.AppendScope(name);
name = options.ReplaceInvalidCharacters(name);
}
reg.EventName = name;
}
}

internal void ConfigureEntityKind(EventRegistration reg)
{
// set the entity kind, if not set and there is an attribute
if (reg.EntityKind == null)
{
var type = reg.EventType;
var kind = type.GetCustomAttributes(false).OfType<EntityKindAttribute>().SingleOrDefault()?.Kind;
if (kind != null)
{
reg.EntityKind = kind;
}
}
}

internal void ConfigureConsumerNames(EventRegistration reg, EventBusNamingOptions options)
{
// ensure we have the event name set
if (string.IsNullOrWhiteSpace(reg.EventName))
{
throw new InvalidOperationException($"The {nameof(reg.EventName)} for must be set before setting names of the consumer.");
}

// prefix is either the one provided or the application name
var prefix = options.ConsumerNamePrefix ?? environment.ApplicationName;

foreach (var ecr in reg.Consumers)
{
// set the consumer name, if not set
if (string.IsNullOrWhiteSpace(ecr.ConsumerName))
{
var type = ecr.ConsumerType;
// prioritize the attribute if available, otherwise get the type name
var name = type.GetCustomAttributes(false).OfType<ConsumerNameAttribute>().SingleOrDefault()?.ConsumerName;
if (name == null)
{
var typeName = options.UseFullTypeNames ? type.FullName! : type.Name;
typeName = options.TrimCommonSuffixes(typeName);
name = options.ConsumerNameSource switch
{
ConsumerNameSource.TypeName => typeName,
ConsumerNameSource.Prefix => prefix,
ConsumerNameSource.PrefixAndTypeName => $"{prefix}.{typeName}",
_ => throw new InvalidOperationException($"'{nameof(options.ConsumerNameSource)}.{options.ConsumerNameSource}' is not supported"),
};
name = options.ApplyNamingConvention(name);
name = options.AppendScope(name);
name = options.ReplaceInvalidCharacters(name);
}
ecr.ConsumerName = name;
}
}
}

internal void ConfigureSerializer(EventRegistration reg)
{
// If the event serializer has not been specified, attempt to get from the attribute
var attrs = reg.EventType.GetCustomAttributes(false);
reg.EventSerializerType ??= attrs.OfType<EventSerializerAttribute>().SingleOrDefault()?.SerializerType;
reg.EventSerializerType ??= typeof(IEventSerializer); // use the default when not provided

// Ensure the serializer is either default or it implements IEventSerializer
if (reg.EventSerializerType != typeof(IEventSerializer)
&& !typeof(IEventSerializer).IsAssignableFrom(reg.EventSerializerType))
{
throw new InvalidOperationException($"The type '{reg.EventSerializerType.FullName}' is used as a serializer "
+ $"but does not implement '{typeof(IEventSerializer).FullName}'");
}
}
}
1 change: 1 addition & 0 deletions src/Tingle.EventBus/DependencyInjection/EventBusBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public EventBusBuilder(IServiceCollection services)
Services.TryAddSingleton<IEventBusConfigurationProvider, DefaultEventBusConfigurationProvider>();
Services.TryAddSingleton<IEventIdGenerator, DefaultEventIdGenerator>();
Services.TryAddTransient<IEventPublisher, EventPublisher>();
Services.AddSingleton<IEventBusConfigurator, MandatoryEventBusConfigurator>(); // can be multiple do not use TryAdd*(...)
}

/// <summary>
Expand Down
Loading
Loading