Skip to content

Commit

Permalink
In Auto setup, when IsUninitialized, response http status code 409
Browse files Browse the repository at this point in the history
  • Loading branch information
infofromca committed Oct 4, 2024
1 parent dc94fdd commit 5c0cfbe
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 112 deletions.
117 changes: 5 additions & 112 deletions src/OrchardCore.Modules/OrchardCore.AutoSetup/AutoSetupMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OrchardCore.Abstractions.Setup;
using OrchardCore.AutoSetup.Extensions;
using OrchardCore.AutoSetup.Options;
using OrchardCore.AutoSetup.Services;
using OrchardCore.Environment.Shell;
using OrchardCore.Locking.Distributed;
using OrchardCore.Setup.Services;

namespace OrchardCore.AutoSetup;

Expand Down Expand Up @@ -131,13 +129,13 @@ public async Task InvokeAsync(HttpContext httpContext)
if (!settings.IsUninitialized())
{
await _shellHost.ReloadShellContextAsync(_shellSettings, eventSource: false);
httpContext.Response.Redirect(pathBase);
httpContext.Response.StatusCode = 409;

return;
}

var setupService = httpContext.RequestServices.GetRequiredService<ISetupService>();
if (await SetupTenantAsync(setupService, _setupOptions, _shellSettings))
var autoSetupService = httpContext.RequestServices.GetRequiredService<IAutoSetupService>();
if (await autoSetupService.SetupTenantAsync(_setupOptions, _shellSettings))
{
if (_setupOptions.IsDefault)
{
Expand All @@ -146,7 +144,7 @@ public async Task InvokeAsync(HttpContext httpContext)
{
if (_setupOptions != setupOptions)
{
await CreateTenantSettingsAsync(setupOptions);
await autoSetupService.CreateTenantSettingsAsync(setupOptions);
}
}
}
Expand All @@ -160,109 +158,4 @@ public async Task InvokeAsync(HttpContext httpContext)

await _next.Invoke(httpContext);
}

/// <summary>
/// Sets up a tenant.
/// </summary>
/// <param name="setupService">The setup service.</param>
/// <param name="setupOptions">The tenant setup options.</param>
/// <param name="shellSettings">The tenant shell settings.</param>
/// <returns>
/// Returns <c>true</c> if successfully setup.
/// </returns>
public async Task<bool> SetupTenantAsync(ISetupService setupService, TenantSetupOptions setupOptions, ShellSettings shellSettings)
{
var setupContext = await GetSetupContextAsync(setupOptions, setupService, shellSettings);

_logger.LogInformation("AutoSetup is initializing the site");

await setupService.SetupAsync(setupContext);

if (setupContext.Errors.Count == 0)
{
_logger.LogInformation("AutoSetup successfully provisioned the site '{SiteName}'.", setupOptions.SiteName);

return true;
}

var stringBuilder = new StringBuilder();
foreach (var error in setupContext.Errors)
{
stringBuilder.AppendLine($"{error.Key} : '{error.Value}'");
}

_logger.LogError("AutoSetup failed installing the site '{SiteName}' with errors: {Errors}", setupOptions.SiteName, stringBuilder);

return false;
}

/// <summary>
/// Creates a tenant shell settings.
/// </summary>
/// <param name="setupOptions">The setup options.</param>
/// <returns>The <see cref="ShellSettings"/>.</returns>
public async Task<ShellSettings> CreateTenantSettingsAsync(TenantSetupOptions setupOptions)
{
using var shellSettings = _shellSettingsManager
.CreateDefaultSettings()
.AsUninitialized()
.AsDisposable();

shellSettings.Name = setupOptions.ShellName;
shellSettings.RequestUrlHost = setupOptions.RequestUrlHost;
shellSettings.RequestUrlPrefix = setupOptions.RequestUrlPrefix;

shellSettings["ConnectionString"] = setupOptions.DatabaseConnectionString;
shellSettings["TablePrefix"] = setupOptions.DatabaseTablePrefix;
shellSettings["Schema"] = setupOptions.DatabaseSchema;
shellSettings["DatabaseProvider"] = setupOptions.DatabaseProvider;
shellSettings["Secret"] = Guid.NewGuid().ToString();
shellSettings["RecipeName"] = setupOptions.RecipeName;
shellSettings["FeatureProfile"] = setupOptions.FeatureProfile;

await _shellHost.UpdateShellSettingsAsync(shellSettings);

return shellSettings;
}

/// <summary>
/// Gets a setup context from the configuration.
/// </summary>
/// <param name="options">The tenant setup options.</param>
/// <param name="setupService">The setup service.</param>
/// <param name="shellSettings">The tenant shell settings.</param>
/// <returns> The <see cref="SetupContext"/> used to setup the site.</returns>
private static async Task<SetupContext> GetSetupContextAsync(TenantSetupOptions options, ISetupService setupService, ShellSettings shellSettings)
{
var recipes = await setupService.GetSetupRecipesAsync();

var recipe = recipes.SingleOrDefault(r => r.Name == options.RecipeName);

var setupContext = new SetupContext
{
Recipe = recipe,
ShellSettings = shellSettings,
Errors = new Dictionary<string, string>()
};

if (shellSettings.IsDefaultShell())
{
// The 'Default' shell is first created by the infrastructure,
// so the following 'Autosetup' options need to be passed.
shellSettings.RequestUrlHost = options.RequestUrlHost;
shellSettings.RequestUrlPrefix = options.RequestUrlPrefix;
}

setupContext.Properties[SetupConstants.AdminEmail] = options.AdminEmail;
setupContext.Properties[SetupConstants.AdminPassword] = options.AdminPassword;
setupContext.Properties[SetupConstants.AdminUsername] = options.AdminUsername;
setupContext.Properties[SetupConstants.DatabaseConnectionString] = options.DatabaseConnectionString;
setupContext.Properties[SetupConstants.DatabaseProvider] = options.DatabaseProvider;
setupContext.Properties[SetupConstants.DatabaseTablePrefix] = options.DatabaseTablePrefix;
setupContext.Properties[SetupConstants.DatabaseSchema] = options.DatabaseSchema;
setupContext.Properties[SetupConstants.SiteName] = options.SiteName;
setupContext.Properties[SetupConstants.SiteTimeZone] = options.SiteTimeZone;

return setupContext;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System.Text;
using Microsoft.Extensions.Logging;
using OrchardCore.Abstractions.Setup;
using OrchardCore.AutoSetup.Options;
using OrchardCore.Environment.Shell;
using OrchardCore.Setup.Services;

namespace OrchardCore.AutoSetup.Services;
public class AutoSetupService : IAutoSetupService
{
/// <summary>
/// The shell host.
/// </summary>
private readonly IShellHost _shellHost;

/// <summary>
/// The shell settings manager.
/// </summary>
private readonly IShellSettingsManager _shellSettingsManager;

/// <summary>
/// The setup service.
/// </summary>
private readonly ISetupService _setupService;

/// <summary>
/// The logger.
/// </summary>
private readonly ILogger _logger;

public AutoSetupService(
IShellHost shellHost,
IShellSettingsManager shellSettingsManager,
ISetupService setupService,
ILogger<AutoSetupService> logger
)
{
_shellHost = shellHost;
_shellSettingsManager = shellSettingsManager;
_setupService = setupService;
_logger = logger;
}

/// <summary>
/// Sets up a tenant.
/// </summary>
/// <param name="setupOptions">The tenant setup options.</param>
/// <param name="shellSettings">The tenant shell settings.</param>
/// <returns>
/// Returns <c>true</c> if successfully setup.
/// </returns>
public async Task<bool> SetupTenantAsync(TenantSetupOptions setupOptions, ShellSettings shellSettings)
{
var setupContext = await GetSetupContextAsync(setupOptions, shellSettings);

_logger.LogInformation("AutoSetup is initializing the site");

await _setupService.SetupAsync(setupContext);

if (setupContext.Errors.Count == 0)
{
_logger.LogInformation("AutoSetup successfully provisioned the site '{SiteName}'.", setupOptions.SiteName);

return true;
}

var stringBuilder = new StringBuilder();
foreach (var error in setupContext.Errors)
{
stringBuilder.AppendLine($"{error.Key} : '{error.Value}'");
}

_logger.LogError("AutoSetup failed installing the site '{SiteName}' with errors: {Errors}", setupOptions.SiteName, stringBuilder);

return false;
}

/// <summary>
/// Creates a tenant shell settings.
/// </summary>
/// <param name="setupOptions">The setup options.</param>
/// <returns>The <see cref="ShellSettings"/>.</returns>
public async Task<ShellSettings> CreateTenantSettingsAsync(TenantSetupOptions setupOptions)
{
using var shellSettings = _shellSettingsManager
.CreateDefaultSettings()
.AsUninitialized()
.AsDisposable();

shellSettings.Name = setupOptions.ShellName;
shellSettings.RequestUrlHost = setupOptions.RequestUrlHost;
shellSettings.RequestUrlPrefix = setupOptions.RequestUrlPrefix;

shellSettings["ConnectionString"] = setupOptions.DatabaseConnectionString;
shellSettings["TablePrefix"] = setupOptions.DatabaseTablePrefix;
shellSettings["Schema"] = setupOptions.DatabaseSchema;
shellSettings["DatabaseProvider"] = setupOptions.DatabaseProvider;
shellSettings["Secret"] = Guid.NewGuid().ToString();
shellSettings["RecipeName"] = setupOptions.RecipeName;
shellSettings["FeatureProfile"] = setupOptions.FeatureProfile;

await _shellHost.UpdateShellSettingsAsync(shellSettings);

return shellSettings;
}

/// <summary>
/// Gets a setup context from the configuration.
/// </summary>
/// <param name="options">The tenant setup options.</param>
/// <param name="shellSettings">The tenant shell settings.</param>
/// <returns> The <see cref="SetupContext"/> used to setup the site.</returns>
public async Task<SetupContext> GetSetupContextAsync(TenantSetupOptions options, ShellSettings shellSettings)
{
var recipes = await _setupService.GetSetupRecipesAsync();

var recipe = recipes.SingleOrDefault(r => r.Name == options.RecipeName);

var setupContext = new SetupContext
{
Recipe = recipe,
ShellSettings = shellSettings,
Errors = new Dictionary<string, string>()
};

if (shellSettings.IsDefaultShell())
{
// The 'Default' shell is first created by the infrastructure,
// so the following 'Autosetup' options need to be passed.
shellSettings.RequestUrlHost = options.RequestUrlHost;
shellSettings.RequestUrlPrefix = options.RequestUrlPrefix;
}

setupContext.Properties[SetupConstants.AdminEmail] = options.AdminEmail;
setupContext.Properties[SetupConstants.AdminPassword] = options.AdminPassword;
setupContext.Properties[SetupConstants.AdminUsername] = options.AdminUsername;
setupContext.Properties[SetupConstants.DatabaseConnectionString] = options.DatabaseConnectionString;
setupContext.Properties[SetupConstants.DatabaseProvider] = options.DatabaseProvider;
setupContext.Properties[SetupConstants.DatabaseTablePrefix] = options.DatabaseTablePrefix;
setupContext.Properties[SetupConstants.DatabaseSchema] = options.DatabaseSchema;
setupContext.Properties[SetupConstants.SiteName] = options.SiteName;
setupContext.Properties[SetupConstants.SiteTimeZone] = options.SiteTimeZone;

return setupContext;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using OrchardCore.AutoSetup.Options;
using OrchardCore.Environment.Shell;
using OrchardCore.Setup.Services;

namespace OrchardCore.AutoSetup.Services;
public interface IAutoSetupService
{
Task<ShellSettings> CreateTenantSettingsAsync(TenantSetupOptions setupOptions);
Task<SetupContext> GetSetupContextAsync(TenantSetupOptions options, ShellSettings shellSettings);
Task<bool> SetupTenantAsync(TenantSetupOptions setupOptions, ShellSettings shellSettings);
}
3 changes: 3 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.AutoSetup/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OrchardCore.AutoSetup.Options;
using OrchardCore.AutoSetup.Services;
using OrchardCore.Environment.Shell;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;
Expand Down Expand Up @@ -67,6 +68,8 @@ public override void ConfigureServices(IServiceCollection services)
{
services.Configure<AutoSetupOptions>(o => o.ConfigurationExists = true);
}

services.AddScoped<IAutoSetupService, AutoSetupService>();
}

/// <summary>
Expand Down

0 comments on commit 5c0cfbe

Please sign in to comment.