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

Dynamic provider management #87

Merged
merged 11 commits into from
Aug 15, 2023
82 changes: 82 additions & 0 deletions src/TagzApp.Web/Services/Base/BaseProviderManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.Reflection;
using TagzApp.Communication.Extensions;

namespace TagzApp.Web.Services.Base;

public class BaseProviderManager
{
private readonly IServiceCollection _Services;
private readonly IConfiguration _Configuration;
private readonly ILogger<BaseProviderManager> _Logger;
protected IEnumerable<ISocialMediaProvider> _Providers;

public BaseProviderManager(IConfiguration configuration, ILogger<BaseProviderManager> logger,
IEnumerable<ISocialMediaProvider>? socialMediaProviders)
{
_Services = new ServiceCollection();
_Configuration = configuration;
_Logger = logger;
_Providers = socialMediaProviders != null && socialMediaProviders.Count() > 0
? socialMediaProviders : new List<ISocialMediaProvider>();
}

public void InitProviders()
{
if (!_Providers.Any())
{
LoadConfigurationProviders();
}
}

private void LoadConfigurationProviders()
{
List<IConfigureProvider> configProviders = new List<IConfigureProvider>();
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;

if (!string.IsNullOrWhiteSpace(path))
{
foreach (string dllPath in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories))
{
try
{
var assembly = Assembly.LoadFrom(dllPath);
var providerAssemblies = assembly.GetTypes()
.Where(t => typeof(IConfigureProvider).IsAssignableFrom(t) && !t.IsInterface);

if (providerAssemblies.Any())
{
foreach (var provider in providerAssemblies)
{
var providerInstance = Activator.CreateInstance(provider) as IConfigureProvider;

if (providerInstance != null)
{
configProviders.Add(providerInstance);
}
}
}
} catch(Exception ex) {
_Logger.LogWarning(ex, $"Skipping {dllPath} due to error");
}
}

ConfigureProviders(configProviders);
}
}

private void ConfigureProviders(IEnumerable<IConfigureProvider> configurationProviders)
{
var socialMediaProviders = new List<ISocialMediaProvider>();

foreach (var provider in configurationProviders)
{
provider.RegisterServices(_Services, _Configuration);
}

_Services.AddPolicies(_Configuration);

var sp = _Services.BuildServiceProvider();
socialMediaProviders.AddRange(sp.GetServices<ISocialMediaProvider>());
_Providers = socialMediaProviders;
}
}
14 changes: 7 additions & 7 deletions src/TagzApp.Web/Services/InMemoryMessagingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
using System.Collections.Immutable;
using TagzApp.Web.Data;
using TagzApp.Web.Hubs;
using TagzApp.Web.Services.Base;

namespace TagzApp.Web.Services;

public class InMemoryMessagingService : IHostedService
public class InMemoryMessagingService : BaseProviderManager, IHostedService
{

private InMemoryContentMessaging _Service = default;

private readonly IEnumerable<ISocialMediaProvider> _Providers;
private readonly IHubContext<MessageHub> _HubContext;
private readonly ILogger<InMemoryMessagingService> _Logger;


public InMemoryMessagingService(
IEnumerable<ISocialMediaProvider> providers,
IConfiguration configuration,
IHubContext<MessageHub> hubContext,
ILogger<InMemoryMessagingService> logger
)
ILogger<InMemoryMessagingService> logger,
IEnumerable<ISocialMediaProvider>? socialMediaProviders = null
):base(configuration, logger, socialMediaProviders)
{
_Providers = providers;
_HubContext = hubContext;
_Logger = logger;
}
Expand All @@ -36,7 +36,7 @@ ILogger<InMemoryMessagingService> logger

public Task StartAsync(CancellationToken cancellationToken)
{

InitProviders();
_Service = new InMemoryContentMessaging();
_Service.StartProviders(_Providers, cancellationToken);

Expand Down
84 changes: 18 additions & 66 deletions src/TagzApp.Web/ServicesExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,85 +8,37 @@ namespace TagzApp.Web;
public static class ServicesExtensions
{

public static IServiceCollection ConfigureProvider<T>(this IServiceCollection services, IConfiguration configuration) where T : IConfigureProvider, new()
{
public static IServiceCollection AddTagzAppHostedServices(this IServiceCollection services, IConfigurationRoot configuration)
{

var providerStart = (IConfigureProvider)(Activator.CreateInstance<T>());
providerStart.RegisterServices(services, configuration);
services.AddSingleton<InMemoryMessagingService>();
services.AddHostedService(s => s.GetRequiredService<InMemoryMessagingService>());

return services;
return services;

}

public static IServiceCollection ConfigureProvider(this IServiceCollection services, IConfigureProvider provider, IConfiguration configuration)
{

provider.RegisterServices(services, configuration);

return services;

}

public static IServiceCollection AddTagzAppHostedServices(this IServiceCollection services, IConfigurationRoot configuration)
{

services.AddSingleton<InMemoryMessagingService>();
services.AddHostedService(s => s.GetRequiredService<InMemoryMessagingService>());

// Register the providers
if (SocialMediaProviders.Any())
{
foreach (var item in SocialMediaProviders)
{
services.ConfigureProvider(item, configuration);
}
}
else
{
services.ConfigureProvider<TagzApp.Providers.Mastodon.StartMastodon>(configuration);
services.ConfigureProvider<TagzApp.Providers.Twitter.StartTwitter>(configuration);
services.ConfigureProvider<TagzApp.Providers.TwitchChat.StartTwitchChat>(configuration);
}

return services;

}
}

/// <summary>
/// A collection of externally configured providers
/// </summary>
public static List<IConfigureProvider> SocialMediaProviders { get; set; } = new();
public static AuthenticationBuilder AddExternalProvider(this AuthenticationBuilder builder, string name, IConfiguration configuration,
/// <summary>
/// A collection of externally configured providers
/// </summary>
public static AuthenticationBuilder AddExternalProvider(this AuthenticationBuilder builder, string name, IConfiguration configuration,
Action<IConfiguration> action)
{
{
var section = configuration.GetSection($"Authentication:{name}");
if (section is not null) action(section);
return builder;
}

public static AuthenticationBuilder AddExternalProvider(this AuthenticationBuilder builder, string name, IConfiguration configuration,
Action<string, string> action)
{
return builder.AddExternalProvider(name, configuration, (section) => {
var clientID = section["ClientID"];
var clientSecret = section["ClientSecret"];
if (!string.IsNullOrEmpty(clientID) && !string.IsNullOrEmpty(clientSecret))
{
action(clientID, clientSecret);
}
});
}

public static AuthenticationBuilder AddExternalProvider(this AuthenticationBuilder builder, string name, IConfiguration configuration,
Action<Action<Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions>> action)
{
{
return builder.AddExternalProvider(name, configuration, (section) => {
var clientID = section["ClientID"];
var clientSecret = section["ClientSecret"];
if (!string.IsNullOrEmpty(clientID) && !string.IsNullOrEmpty(clientSecret))
{
action(options =>
{
{
options.ClientId = clientID;
options.ClientSecret = clientSecret;
});
Expand All @@ -95,18 +47,18 @@ public static AuthenticationBuilder AddExternalProvider(this AuthenticationBuild
}

public static AuthenticationBuilder AddExternalProviders(this AuthenticationBuilder builder, IConfiguration configuration)
{
{

builder.AddExternalProvider("Microsoft", configuration , options => builder.AddMicrosoftAccount(options));
builder.AddExternalProvider("Microsoft", configuration, options => builder.AddMicrosoftAccount(options));
builder.AddExternalProvider("GitHub", configuration, options => builder.AddGitHub(options));
builder.AddExternalProvider("LinkedIn", configuration, options => builder.AddLinkedIn(options));

return builder;
return builder;

}
}

public static async Task InitializeSecurity(this IServiceProvider services)
{
{

using var scope = services.CreateScope();

Expand Down
Loading
Loading