Skip to content

Commit

Permalink
Adding feature management for modules (#534)
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofPajak authored Dec 1, 2024
1 parent a754a4a commit 7e1d24d
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 83 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<ItemGroup>
<PackageVersion Include="MaxMind.GeoIP2" Version="5.2.0" />
<PackageVersion Include="ExcelMapper" Version="5.2.593" />
<PackageVersion Include="Microsoft.FeatureManagement.AspNetCore" Version="4.0.0" />
<PackageVersion Include="NPOI" Version="2.7.2" />
<PackageVersion Include="Scryber.Core" Version="6.0.4-beta" />
<PackageVersion Include="Scryber.Core.OpenType" Version="6.1.0-beta" />
Expand Down

This file was deleted.

1 change: 1 addition & 0 deletions src/Core/Grand.Infrastructure/Grand.Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" />
<PackageReference Include="MediatR" />
<PackageReference Include="Microsoft.FeatureManagement.AspNetCore" />
<PackageReference Include="Scrutor" />
<PackageReference Include="StackExchange.Redis" />
<PackageReference Include="FluentValidation" />
Expand Down
34 changes: 23 additions & 11 deletions src/Core/Grand.Infrastructure/Modules/ModuleLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,43 @@ namespace Grand.Infrastructure.Modules;

public static class ModuleLoader
{
private const string MODULE_SECTION_PATH = "FeatureFlags:Modules";
private const string MODULE_SECTION_PATH = "FeatureManagement";
private const string MODULES_DIRECTORY = "Modules";
private const string LOGGER_NAME = "ModuleManager";

private static List<(string FeatureName, bool IsEnabled)> GetFeatureList(IConfiguration configuration)
{
var featureSection = configuration.GetSection(MODULE_SECTION_PATH);
if (!featureSection.Exists())
{
return new List<(string, bool)>();
}

return featureSection.GetChildren()
.Select(feature => (
FeatureName: feature.Key,
IsEnabled: bool.TryParse(feature.Value, out var isEnabled) && isEnabled
))
.ToList();
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static void LoadModules(IMvcCoreBuilder mvcCoreBuilder, IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
{
var modulesSection = configuration.GetSection(MODULE_SECTION_PATH);
if (!modulesSection.Exists())
var modules = GetFeatureList(configuration);
if (modules.Count == 0)
{
return;
}

var logger = CreateLogger(mvcCoreBuilder.Services);
var modules = modulesSection.Get<Dictionary<string, bool>>();

foreach (var (moduleName, isEnabled) in modules)
foreach (var module in modules)
{
if (!isEnabled)
if (!module.IsEnabled)
{
logger.LogInformation("Module '{ModuleName}' is disabled.", moduleName);
logger.LogInformation("Module '{ModuleName}' is disabled.", module.FeatureName);
continue;
}

LoadModule(moduleName, hostingEnvironment, mvcCoreBuilder, logger);
LoadModule(module.FeatureName, hostingEnvironment, mvcCoreBuilder, logger);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/Core/Grand.Infrastructure/StartupBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FeatureManagement;

namespace Grand.Infrastructure;

Expand Down Expand Up @@ -253,7 +254,6 @@ private static void RegisterConfigurations(IServiceCollection services, IConfigu
services.StartupConfig<BackendAPIConfig>(configuration.GetSection("BackendAPI"));
services.StartupConfig<FrontendAPIConfig>(configuration.GetSection("FrontendAPI"));
services.StartupConfig<DatabaseConfig>(configuration.GetSection("Database"));
services.StartupConfig<FeatureFlagsConfig>(configuration.GetSection("FeatureFlags"));
services.StartupConfig<AmazonConfig>(configuration.GetSection("Amazon"));
services.StartupConfig<AzureConfig>(configuration.GetSection("Azure"));
services.StartupConfig<ApplicationInsightsConfig>(configuration.GetSection("ApplicationInsights"));
Expand All @@ -270,6 +270,8 @@ private static void RegisterConfigurations(IServiceCollection services, IConfigu
/// <param name="configuration">Configuration root of the application</param>
public static void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddFeatureManagement();

//find startup configurations provided by other assemblies
var typeSearcher = new TypeSearcher();
services.AddSingleton<ITypeSearcher>(typeSearcher);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FeatureManagement;

namespace Grand.Module.Migration.Startup;

Expand All @@ -21,9 +22,8 @@ public void Configure(WebApplication application, IWebHostEnvironment webHostEnv
{
if (!DataSettingsManager.DatabaseIsInstalled())
return;

var featureFlagsConfig = application.Services.GetRequiredService<FeatureFlagsConfig>();
if (featureFlagsConfig.Modules.TryGetValue("Grand.Module.Migration", out var value) && value)
var featureManager = application.Services.GetRequiredService<IFeatureManager>();
if (featureManager.IsEnabledAsync("Grand.Module.Migration").Result)
{
var migrationProcess = application.Services.GetRequiredService<IMigrationProcess>();
migrationProcess.RunMigrationProcess();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.FeatureManagement;
using Microsoft.Net.Http.Headers;

namespace Grand.Web.Common.Infrastructure;
Expand Down Expand Up @@ -274,7 +275,7 @@ public static void UseDefaultSecurityHeaders(this WebApplication application)
/// <param name="application">Builder for configuring an application's request pipeline</param>
public static void UseInstallUrl(this WebApplication application)
{
application.UseMiddleware<InstallUrlMiddleware>();
application.UseMiddlewareForFeature<InstallUrlMiddleware>("Grand.Module.Installer");
}

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/Web/Grand.Web.Common/Middleware/InstallUrlMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Grand.Data;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FeatureManagement;

namespace Grand.Web.Common.Middleware;

Expand Down Expand Up @@ -35,8 +35,8 @@ public async Task InvokeAsync(HttpContext context)
//whether database is installed
if (!DataSettingsManager.DatabaseIsInstalled())
{
var configuration = context.RequestServices.GetService<IConfiguration>();
var isInstallerModuleEnabled = configuration.GetValue<bool>("FeatureFlags:Modules:Grand.Module.Installer");
var featureManager = context.RequestServices.GetRequiredService<IFeatureManager>();
var isInstallerModuleEnabled = await featureManager.IsEnabledAsync("Grand.Module.Installer");
if (!isInstallerModuleEnabled)
{
// Return a response indicating the installer module is not enabled
Expand Down
8 changes: 4 additions & 4 deletions src/Web/Grand.Web.Common/Startup/GrandCommonStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FeatureManagement;

namespace Grand.Web.Common.Startup;

Expand Down Expand Up @@ -71,7 +72,7 @@ public void Configure(WebApplication application, IWebHostEnvironment webHostEnv
var appConfig = application.Services.GetRequiredService<AppConfig>();
var performanceConfig = application.Services.GetRequiredService<PerformanceConfig>();
var securityConfig = application.Services.GetRequiredService<SecurityConfig>();
var featureFlagsConfig = application.Services.GetRequiredService<FeatureFlagsConfig>();
var featureManager = application.Services.GetRequiredService<IFeatureManager>();

//add HealthChecks
application.UseGrandHealthChecks();
Expand All @@ -92,9 +93,8 @@ public void Configure(WebApplication application, IWebHostEnvironment webHostEnv
//use static files feature
application.UseGrandStaticFiles(appConfig);

//check whether database is installed
if (featureFlagsConfig.Modules.TryGetValue("Grand.Module.Installer", out var value) && value)
application.UseInstallUrl();
//install middleware
application.UseInstallUrl();

//use HTTP session
application.UseSession();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
<Value>Use integrated Windows authentication</Value>
</LocaleResource>
<LocaleResource Name="Installed">
<Value>Application has been installed successfully, please restart application.</Value>
<Value>Application has been installed successfully. Please restart the application. We recommend disabling the feature flag in appsettings "Grand.Module.Installer" by setting it to false.</Value>
</LocaleResource>
<LocaleResource Name="AlreadyInstalled">
<Value>This database already exists and has GrandNode installed on it. Choose different database.</Value>
Expand Down
Loading

0 comments on commit 7e1d24d

Please sign in to comment.