From 0974ec60e50e1fb43ae8ea0fb783a59c9e40670a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sun, 23 Apr 2023 20:14:13 +0200 Subject: [PATCH 01/30] Adding maintenance task execution capabilities --- .../Constants/DocumentCollections.cs | 6 + .../Constants/FeatureNames.cs | 8 ++ ...intenanceTaskExecutionContextExtensions.cs | 7 ++ .../Indexes/MaintenanceTaskExecutionIndex.cs | 27 +++++ .../Lombiq.Hosting.Tenants.Maintenance.csproj | 22 ++++ .../Maintenance/UpdateTenantUrl/Startup.cs | 27 +++++ .../UpdateTenantUrl/TenantUrlHelper.cs | 22 ++++ ...pdateShellRequestUrlMaintenanceProvider.cs | 44 +++++++ .../UpdateSiteUrlMaintenanceProvider.cs | 36 ++++++ .../UpdateTenantUrlMaintenanceOptions.cs | 8 ++ .../Manifest.cs | 25 ++++ .../Migrations.cs | 28 +++++ .../Models/MaintenanceTaskExecutionContext.cs | 8 ++ .../Models/MaintenanceTaskExecutionData.cs | 12 ++ .../Services/IMaintenanceManager.cs | 21 ++++ .../Services/IMaintenanceProvider.cs | 32 +++++ .../Services/MaintenanceManager.cs | 109 ++++++++++++++++++ .../Services/MaintenanceProviderBase.cs | 14 +++ .../Services/MaintenanceRunnerService.cs | 38 ++++++ Lombiq.Hosting.Tenants.Maintenance/Startup.cs | 22 ++++ 20 files changed, 516 insertions(+) create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Constants/DocumentCollections.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Manifest.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Migrations.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionContext.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Startup.cs diff --git a/Lombiq.Hosting.Tenants.Maintenance/Constants/DocumentCollections.cs b/Lombiq.Hosting.Tenants.Maintenance/Constants/DocumentCollections.cs new file mode 100644 index 00000000..86ef07bd --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Constants/DocumentCollections.cs @@ -0,0 +1,6 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Constants; + +public static class DocumentCollections +{ + public const string Maintenance = nameof(Maintenance); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs new file mode 100644 index 00000000..eae12390 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs @@ -0,0 +1,8 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Constants; + +public static class FeatureNames +{ + public const string Module = "Lombiq.Hosting.Tenants.Maintenance"; + public const string Maintenance = Module; + public const string Maintenance_UpdateTenantUrl = Maintenance + ".UpdateTenantUrl"; +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs new file mode 100644 index 00000000..c318d596 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs @@ -0,0 +1,7 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Models; + +public static class MaintenanceTaskExecutionContextExtensions +{ + public static bool WasLatestExecutionSuccessful(this MaintenanceTaskExecutionContext execution) => + execution.LatestExecution?.IsSuccess == true; +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs b/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs new file mode 100644 index 00000000..9f2550cf --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs @@ -0,0 +1,27 @@ +using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Models; +using YesSql.Indexes; + +namespace Lombiq.Hosting.Tenants.Maintenance.Indexes; + +public class MaintenanceTaskExecutionIndex : MapIndex +{ + public string MaintenanceId { get; set; } + public DateTime ExecutionTimeUtc { get; set; } + public bool IsSuccess { get; set; } +} + +public class MaintenanceTaskExecutionIndexProvider : IndexProvider +{ + public MaintenanceTaskExecutionIndexProvider() => + CollectionName = DocumentCollections.Maintenance; + + public override void Describe(DescribeContext context) => + context.For() + .Map(execution => new MaintenanceTaskExecutionIndex + { + MaintenanceId = execution.MaintenanceId, + ExecutionTimeUtc = execution.ExecutionTimeUtc, + IsSuccess = execution.IsSuccess, + }); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj new file mode 100644 index 00000000..35afd08f --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + true + enable + enable + + + + + + + + + + + + + + + diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs new file mode 100644 index 00000000..1d98435b --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs @@ -0,0 +1,27 @@ +using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Modules; + +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; + +[Feature(Constants.FeatureNames.Maintenance_UpdateTenantUrl)] +public class Startup : StartupBase +{ + private readonly IShellConfiguration _shellConfiguration; + + public Startup(IShellConfiguration shellConfiguration) => + _shellConfiguration = shellConfiguration; + + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + + var options = new UpdateTenantUrlMaintenanceOptions(); + var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:UpdateTenantUrl"); + configSection.Bind(options); + services.Configure(configSection); + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs new file mode 100644 index 00000000..b018a9bb --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs @@ -0,0 +1,22 @@ +using OrchardCore.Environment.Shell; + +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; + +internal static class TenantUrlHelper +{ + public static string GetTenantUrl(UpdateTenantUrlMaintenanceOptions options, ShellSettings shellSettings) + { +#pragma warning disable CA1308 // Normalize strings to uppercase + var evaluatedTenantUrl = options.TenantUrl.Replace( + "{TenantName}", + shellSettings.Name.ToLowerInvariant()); +#pragma warning restore CA1308 // Normalize strings to uppercase + var defaultTenantUrl = + string.IsNullOrEmpty(options.DefaultTenantUrl) + ? evaluatedTenantUrl + : options.DefaultTenantUrl; + return shellSettings.IsDefaultShell() + ? defaultTenantUrl + : evaluatedTenantUrl; + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs new file mode 100644 index 00000000..01273de3 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs @@ -0,0 +1,44 @@ +using Lombiq.Hosting.Tenants.Maintenance.Models; +using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.Options; +using OrchardCore.Environment.Shell; +using OrchardCore.Environment.Shell.Models; + +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; + +public class UpdateShellRequestUrlMaintenanceProvider : MaintenanceProviderBase +{ + private readonly ShellSettings _shellSettings; + private readonly IOptions _options; + private readonly IShellSettingsManager _shellSettingsManager; + + public UpdateShellRequestUrlMaintenanceProvider( + ShellSettings shellSettings, + IOptions options, + IShellSettingsManager shellSettingsManager) + { + _shellSettings = shellSettings; + _options = options; + _shellSettingsManager = shellSettingsManager; + } + + public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) + { + if (!_options.Value.Enabled || !_shellSettings.IsDefaultShell()) return Task.FromResult(false); + + return Task.FromResult(!context.WasLatestExecutionSuccessful()); + } + + public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) + { + var allShellSettings = await _shellSettingsManager.LoadSettingsAsync(); + foreach (var shellSettings in allShellSettings.Where(settings => settings.State == TenantState.Running)) + { + string tenantUrl = TenantUrlHelper.GetTenantUrl(_options.Value, shellSettings); + shellSettings.RequestUrlHost = tenantUrl; + await _shellSettingsManager.SaveSettingsAsync(shellSettings); + } + + context.ReloadShellAfterMaintenanceCompletion = true; + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs new file mode 100644 index 00000000..6bcd30de --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -0,0 +1,36 @@ +using Lombiq.Hosting.Tenants.Maintenance.Models; +using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.Options; +using OrchardCore.Environment.Shell; +using OrchardCore.Settings; + +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; + +public class UpdateSiteUrlMaintenanceProvider : MaintenanceProviderBase +{ + private readonly ISiteService _siteService; + private readonly ShellSettings _shellSettings; + private readonly IOptions _options; + + public UpdateSiteUrlMaintenanceProvider( + ISiteService siteService, + ShellSettings shellSettings, + IOptions options) + { + _siteService = siteService; + _shellSettings = shellSettings; + _options = options; + } + + public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => + !_options.Value.Enabled + ? Task.FromResult(false) + : Task.FromResult(!context.WasLatestExecutionSuccessful()); + + public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) + { + var siteSettings = await _siteService.LoadSiteSettingsAsync(); + siteSettings.BaseUrl = TenantUrlHelper.GetTenantUrl(_options.Value, _shellSettings); + await _siteService.UpdateSiteSettingsAsync(siteSettings); + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs new file mode 100644 index 00000000..fe4c65f4 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs @@ -0,0 +1,8 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; + +public class UpdateTenantUrlMaintenanceOptions +{ + public bool Enabled { get; set; } + public string DefaultTenantUrl { get; set; } + public string TenantUrl { get; set; } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs new file mode 100644 index 00000000..fce21e66 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs @@ -0,0 +1,25 @@ +using OrchardCore.Modules.Manifest; +using static Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames; + +[assembly: Module( + Name = "Lombiq Hosting - Tenants Maintenance", + Author = "Lombiq Technologies", + Website = "https://github.com/Lombiq/Hosting-Tenants", + Version = "0.0.1" +)] + +[assembly: Feature( + Id = Maintenance, + Name = "Lombiq Hosting - Tenants Maintenance", + Description = "Provides maintenance operations for tenants.", + Category = "Hosting", + Dependencies = new[] { "OrchardCore.Tenants" } +)] + +[assembly: Feature( + Id = Maintenance_UpdateTenantUrl, + Name = "Lombiq Hosting - Tenants Maintenance - Update Tenant URL", + Description = "Updates the URL of the tenant (e.g., when the production database is copied to staging).", + Category = "Maintenance", + Dependencies = new[] { Maintenance } +)] diff --git a/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs b/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs new file mode 100644 index 00000000..0df56e05 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs @@ -0,0 +1,28 @@ +using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Indexes; +using OrchardCore.Data.Migration; +using YesSql.Sql; + +namespace Lombiq.Hosting.Tenants.Maintenance; + +public class Migrations : DataMigration +{ + public int Create() + { + SchemaBuilder.CreateMapIndexTable( + table => table + .Column(nameof(MaintenanceTaskExecutionIndex.MaintenanceId)) + .Column(nameof(MaintenanceTaskExecutionIndex.ExecutionTimeUtc)) + .Column(nameof(MaintenanceTaskExecutionIndex.IsSuccess)), + collection: DocumentCollections.Maintenance); + + SchemaBuilder.AlterIndexTable( + table => table + .CreateIndex( + $"IDX_{nameof(MaintenanceTaskExecutionIndex)}_{nameof(MaintenanceTaskExecutionIndex.MaintenanceId)}", + nameof(MaintenanceTaskExecutionIndex.MaintenanceId)), + collection: DocumentCollections.Maintenance); + + return 1; + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionContext.cs b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionContext.cs new file mode 100644 index 00000000..cad2a2b9 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionContext.cs @@ -0,0 +1,8 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Models; + +public class MaintenanceTaskExecutionContext +{ + public MaintenanceTaskExecutionData LatestExecution { get; set; } + public MaintenanceTaskExecutionData CurrentExecution { get; set; } + public bool ReloadShellAfterMaintenanceCompletion { get; set; } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs new file mode 100644 index 00000000..9411cdc9 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs @@ -0,0 +1,12 @@ +using OrchardCore.Entities; + +namespace Lombiq.Hosting.Tenants.Maintenance.Models; + +public class MaintenanceTaskExecutionData : Entity +{ + public int Id { get; set; } + public string MaintenanceId { get; set; } + public DateTime ExecutionTimeUtc { get; set; } + public bool IsSuccess { get; set; } + public string Error { get; set; } +} \ No newline at end of file diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs new file mode 100644 index 00000000..734f7ab8 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs @@ -0,0 +1,21 @@ +using Lombiq.Hosting.Tenants.Maintenance.Models; + +namespace Lombiq.Hosting.Tenants.Maintenance.Services; + +/// +/// This service is responsible for executing maintenance tasks. +/// +public interface IMaintenanceManager +{ + /// + /// Returns the latest execution of a maintenance task by its ID. + /// + /// The ID of the maintenance task. + /// The latest execution of the maintenance task. + public Task GetLatestExecutionByMaintenanceIdAsync(string maintenanceId); + + /// + /// Executes all maintenance tasks if needed. + /// + public Task ExecuteMaintenanceTasksAsync(); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs new file mode 100644 index 00000000..d71708c6 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs @@ -0,0 +1,32 @@ +using Lombiq.Hosting.Tenants.Maintenance.Models; + +namespace Lombiq.Hosting.Tenants.Maintenance.Services; + +/// +/// A provider for a particular maintenance task. +/// +public interface IMaintenanceProvider +{ + /// + /// The ID of the maintenance task. + /// + string Id { get; } + + /// + /// The order of the maintenance task. The lower the number the earlier the task will be executed. + /// + int Order { get; } + + /// + /// Determines whether the maintenance task should be executed. + /// + /// Provides information about the current execution. + /// True if the maintenance task should be executed, false otherwise. + Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context); + + /// + /// Executes the maintenance task. + /// + /// Provides information about the current execution. + Task ExecuteAsync(MaintenanceTaskExecutionContext context); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs new file mode 100644 index 00000000..5ebb3c00 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs @@ -0,0 +1,109 @@ +using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Indexes; +using Lombiq.Hosting.Tenants.Maintenance.Models; +using Microsoft.Extensions.Logging; +using OrchardCore.Environment.Shell; +using OrchardCore.Modules; +using YesSql; + +namespace Lombiq.Hosting.Tenants.Maintenance.Services; + +public class MaintenanceManager : IMaintenanceManager +{ + private readonly IClock _clock; + private readonly ILogger _logger; + private readonly IEnumerable _maintenanceProviders; + private readonly ISession _session; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + + public MaintenanceManager( + IClock clock, + ILogger logger, + IEnumerable maintenanceProviders, + ISession session, + IShellHost shellHost, + ShellSettings shellSettings) + { + _clock = clock; + _logger = logger; + _maintenanceProviders = maintenanceProviders; + _session = session; + _shellHost = shellHost; + _shellSettings = shellSettings; + } + + public Task GetLatestExecutionByMaintenanceIdAsync(string maintenanceId) => + _session.Query(collection: DocumentCollections.Maintenance) + .Where(execution => execution.MaintenanceId == maintenanceId) + .OrderByDescending(execution => execution.ExecutionTimeUtc) + .FirstOrDefaultAsync(); + + public async Task ExecuteMaintenanceTasksAsync() + { + var orderedProviders = _maintenanceProviders.OrderBy(p => p.Order); + foreach (var provider in orderedProviders) + { + var currentExecution = new MaintenanceTaskExecutionData + { + MaintenanceId = provider.Id, + ExecutionTimeUtc = _clock.UtcNow, + }; + var context = new MaintenanceTaskExecutionContext + { + // GetLatestExecutionTask = () => GetLatestExecutionByMaintenanceIdAsync(provider.Id), + LatestExecution = await GetLatestExecutionByMaintenanceIdAsync(provider.Id), + CurrentExecution = currentExecution, + }; + + await ExecuteMaintenanceTaskIfNeededAsync(provider, context, currentExecution); + } + } + + private async Task ExecuteMaintenanceTaskIfNeededAsync( + IMaintenanceProvider provider, + MaintenanceTaskExecutionContext context, + MaintenanceTaskExecutionData execution) + { + _logger.LogDebug("Executing maintenance task {MaintenanceId}, if needed.", provider.Id); + + if (await provider.ShouldExecuteAsync(context)) + { + try + { + await provider.ExecuteAsync(context); + execution.IsSuccess = string.IsNullOrEmpty(execution.Error); + if (execution.IsSuccess) + { + _logger.LogDebug("Maintenance task {MaintenanceId} executed successfully.", provider.Id); + } + else + { + _logger.LogError( + "Maintenance task {MaintenanceId} executed with error: {Error}", + provider.Id, + execution.Error); + } + } + catch (Exception exception) when (!exception.IsFatal()) + { + execution.IsSuccess = false; + execution.Error = exception.ToString(); + + _logger.LogError( + exception, + "Maintenance task {MaintenanceId} failed to execute due to an exception.", + provider.Id); + } + + _session.Save(execution, collection: DocumentCollections.Maintenance); + await _session.SaveChangesAsync(); + + if (context.ReloadShellAfterMaintenanceCompletion) await _shellHost.ReloadShellContextAsync(_shellSettings); + } + else + { + _logger.LogDebug("Maintenance task {MaintenanceId} is not needed.", provider.Id); + } + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs new file mode 100644 index 00000000..0a3a9878 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs @@ -0,0 +1,14 @@ +using Lombiq.Hosting.Tenants.Maintenance.Models; + +namespace Lombiq.Hosting.Tenants.Maintenance.Services; + +public abstract class MaintenanceProviderBase : IMaintenanceProvider +{ + public virtual string Id => GetType().Name; + public virtual int Order => 0; + + public virtual Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => + Task.FromResult(!context.WasLatestExecutionSuccessful()); + + public abstract Task ExecuteAsync(MaintenanceTaskExecutionContext context); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs new file mode 100644 index 00000000..340ad564 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OrchardCore.Environment.Shell; +using OrchardCore.Environment.Shell.Models; +using OrchardCore.Modules; + +namespace Lombiq.Hosting.Tenants.Maintenance.Services; + +public class MaintenanceRunnerService : ModularTenantEvents +{ + private readonly ShellSettings _shellSettings; + private readonly ILogger _logger; + private readonly IShellHost _shellHost; + + public MaintenanceRunnerService( + ShellSettings shellSettings, + ILogger logger, + IShellHost shellHost) + { + _shellSettings = shellSettings; + _logger = logger; + _shellHost = shellHost; + } + + public override async Task ActivatedAsync() + { + if (_shellSettings.State != TenantState.Running) return; + + // Getting the scope here is important because the shell might not be fully initialized yet. + var shellScope = await _shellHost.GetScopeAsync(_shellSettings.Name); + var maintenanceManager = shellScope.ServiceProvider.GetService(); + + _logger.LogInformation( + "Executing maintenance tasks on shell '{ShellName}'.", + _shellSettings.Name); + await maintenanceManager.ExecuteMaintenanceTasksAsync(); + } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs new file mode 100644 index 00000000..233d5a87 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs @@ -0,0 +1,22 @@ +using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Indexes; +using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Data; +using OrchardCore.Data.Migration; +using OrchardCore.Modules; +using YesSql.Indexes; + +namespace Lombiq.Hosting.Tenants.Maintenance; + +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(o => o.Collections.Add(DocumentCollections.Maintenance)); + services.AddScoped(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + } +} From 7f2219f5350135bbbfce075deae929c0fa4b60b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sun, 23 Apr 2023 20:33:43 +0200 Subject: [PATCH 02/30] Adding documentation and following open-source guidelines --- .../Indexes/MaintenanceTaskExecutionIndex.cs | 1 + Lombiq.Hosting.Tenants.Maintenance/License.md | 13 +++++ .../Lombiq.Hosting.Tenants.Maintenance.csproj | 26 +++++++++- ...pdateShellRequestUrlMaintenanceProvider.cs | 2 + .../UpdateSiteUrlMaintenanceProvider.cs | 1 + .../Migrations.cs | 1 + .../Models/MaintenanceTaskExecutionData.cs | 1 + .../NuGetIcon.png | Bin 0 -> 4657 bytes Lombiq.Hosting.Tenants.Maintenance/Readme.md | 49 ++++++++++++++++++ .../Services/IMaintenanceManager.cs | 1 + .../Services/IMaintenanceProvider.cs | 1 + .../Services/MaintenanceManager.cs | 4 ++ .../Services/MaintenanceProviderBase.cs | 1 + .../Services/MaintenanceRunnerService.cs | 1 + 14 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 Lombiq.Hosting.Tenants.Maintenance/License.md create mode 100644 Lombiq.Hosting.Tenants.Maintenance/NuGetIcon.png create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Readme.md diff --git a/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs b/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs index 9f2550cf..3d23b51d 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Indexes/MaintenanceTaskExecutionIndex.cs @@ -1,5 +1,6 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Models; +using System; using YesSql.Indexes; namespace Lombiq.Hosting.Tenants.Maintenance.Indexes; diff --git a/Lombiq.Hosting.Tenants.Maintenance/License.md b/Lombiq.Hosting.Tenants.Maintenance/License.md new file mode 100644 index 00000000..ff974d8e --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/License.md @@ -0,0 +1,13 @@ +Copyright © 2022, [Lombiq Technologies Ltd.](https://lombiq.com) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj index 35afd08f..9a14965b 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj +++ b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj @@ -3,8 +3,19 @@ net6.0 true - enable - enable + $(DefaultItemExcludes);.git*;node_modules\**;Tests\** + + + + Lombiq Hosting - Tenants Maintenance for Orchard Core + Lombiq Technologies + Copyright © 2022, Lombiq Technologies Ltd. + Lombiq Hosting - Tenants Maintenance for Orchard Core: With the help of this module you can execute maintenance tasks on tenants. + NuGetIcon.png + OrchardCore;Lombiq;AspNetCore;Multitenancy;SaaS;Maintenance + https://github.com/Lombiq/Hosting-Tenants + https://github.com/Lombiq/Hosting-Tenants/blob/dev/Lombiq.Hosting.Tenants.Maintenance/Readme.md + License.md @@ -19,4 +30,15 @@ + + + + + + + + + + + diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs index 01273de3..d0130924 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell; using OrchardCore.Environment.Shell.Models; +using System.Linq; +using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs index 6bcd30de..ef9c77a1 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell; using OrchardCore.Settings; +using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs b/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs index 0df56e05..29e563b8 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Migrations.cs @@ -1,6 +1,7 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Indexes; using OrchardCore.Data.Migration; +using System; using YesSql.Sql; namespace Lombiq.Hosting.Tenants.Maintenance; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs index 9411cdc9..09a611be 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs @@ -1,4 +1,5 @@ using OrchardCore.Entities; +using System; namespace Lombiq.Hosting.Tenants.Maintenance.Models; diff --git a/Lombiq.Hosting.Tenants.Maintenance/NuGetIcon.png b/Lombiq.Hosting.Tenants.Maintenance/NuGetIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..162a00508d8041833604d427216238c1b23b2d47 GIT binary patch literal 4657 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 literal 0 HcmV?d00001 diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md new file mode 100644 index 00000000..3fc9bfbb --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -0,0 +1,49 @@ +# Lombiq Hosting - Tenant Maintenance for Orchard Core + +## About + +With the help of this module you can execute maintenance tasks on tenants. + +## Documentation + +Please see the below features for more information. + +### `Lombiq.Hosting.Tenants.Maintenance` + +This is the core functionality required to execute maintenance tasks on tenants. It is available on any tenant. To make your application execute maintenance tasks, you need to add the following to your `Startup.cs`: + +```csharp +public void ConfigureServices(IServiceCollection services) => + services.AddOrchardCms( + builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.Maintenance)); +``` + +To add new maintenance tasks, you need to implement the `IMaintenanceProvider` interface and register it as a service. + +### `Lombiq.Hosting.Tenants.Maintenance.UpdateTenantUrl` + +It's a maintenance task that updates the tenants' URL based on the app configuration. It is available on any tenant. To make your application execute this task, you need to add the following to your `Startup.cs`: + +```csharp +public void ConfigureServices(IServiceCollection services) => + services.AddOrchardCms( + builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.UpdateTenantUrl)); +```~~~~ + +To configure the task, you need to add the following to your `appsettings.json`: + +```json +{ + "OrchardCore": { + "Lombiq_Hosting_Tenants_Maintenance": { + "UpdateTenantUrl": { + "Enabled": true, + "DefaultTenantUrl": "mydomain.com" + "TenantUrl": "{TenantName}.mydomain.com" + } + }, + } +} +``` + +**NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name.~~~~ \ No newline at end of file diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs index 734f7ab8..cdee1a75 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceManager.cs @@ -1,4 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Models; +using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Services; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs index d71708c6..1e860add 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs @@ -1,4 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Models; +using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Services; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs index 5ebb3c00..9106bc21 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs @@ -4,6 +4,10 @@ using Microsoft.Extensions.Logging; using OrchardCore.Environment.Shell; using OrchardCore.Modules; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using YesSql; namespace Lombiq.Hosting.Tenants.Maintenance.Services; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs index 0a3a9878..39982451 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs @@ -1,4 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Models; +using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Services; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs index 340ad564..a07576f1 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs @@ -3,6 +3,7 @@ using OrchardCore.Environment.Shell; using OrchardCore.Environment.Shell.Models; using OrchardCore.Modules; +using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Services; From fe285fc41adafd4e9695ce2777d275018de1516e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sun, 23 Apr 2023 20:58:37 +0200 Subject: [PATCH 03/30] Fixing analyzer violations --- Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs | 2 +- .../Maintenance/UpdateTenantUrl/Startup.cs | 2 +- Lombiq.Hosting.Tenants.Maintenance/Manifest.cs | 2 +- .../Models/MaintenanceTaskExecutionData.cs | 2 +- .../Services/IMaintenanceProvider.cs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs index eae12390..ceb993a9 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs @@ -4,5 +4,5 @@ public static class FeatureNames { public const string Module = "Lombiq.Hosting.Tenants.Maintenance"; public const string Maintenance = Module; - public const string Maintenance_UpdateTenantUrl = Maintenance + ".UpdateTenantUrl"; + public const string UpdateTenantUrl = Maintenance + "." + nameof(UpdateTenantUrl); } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs index 1d98435b..a947f151 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs @@ -6,7 +6,7 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; -[Feature(Constants.FeatureNames.Maintenance_UpdateTenantUrl)] +[Feature(Constants.FeatureNames.UpdateTenantUrl)] public class Startup : StartupBase { private readonly IShellConfiguration _shellConfiguration; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs index fce21e66..a6d0c6aa 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs @@ -17,7 +17,7 @@ )] [assembly: Feature( - Id = Maintenance_UpdateTenantUrl, + Id = UpdateTenantUrl, Name = "Lombiq Hosting - Tenants Maintenance - Update Tenant URL", Description = "Updates the URL of the tenant (e.g., when the production database is copied to staging).", Category = "Maintenance", diff --git a/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs index 09a611be..8b1ee726 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Models/MaintenanceTaskExecutionData.cs @@ -10,4 +10,4 @@ public class MaintenanceTaskExecutionData : Entity public DateTime ExecutionTimeUtc { get; set; } public bool IsSuccess { get; set; } public string Error { get; set; } -} \ No newline at end of file +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs index 1e860add..1da0352e 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs @@ -9,12 +9,12 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Services; public interface IMaintenanceProvider { /// - /// The ID of the maintenance task. + /// Gets the ID of the maintenance task. /// string Id { get; } /// - /// The order of the maintenance task. The lower the number the earlier the task will be executed. + /// Gets the order of the maintenance task. The lower the number the earlier the task will be executed. /// int Order { get; } From c8ad7f24fa01ed84509dbad3080e1d9c50b3179c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 24 Apr 2023 07:04:01 +0200 Subject: [PATCH 04/30] Adding project to solution --- Lombiq.Hosting.Tenants.sln | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lombiq.Hosting.Tenants.sln b/Lombiq.Hosting.Tenants.sln index 9f7daaaf..633eae70 100644 --- a/Lombiq.Hosting.Tenants.sln +++ b/Lombiq.Hosting.Tenants.sln @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lombiq.Hosting.Tenants.Medi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI", "Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI\Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI.csproj", "{4BE4E5C8-7DB6-49D2-855E-7220B039DDF4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lombiq.Hosting.Tenants.Maintenance", "Lombiq.Hosting.Tenants.Maintenance\Lombiq.Hosting.Tenants.Maintenance.csproj", "{6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +59,10 @@ Global {4BE4E5C8-7DB6-49D2-855E-7220B039DDF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {4BE4E5C8-7DB6-49D2-855E-7220B039DDF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {4BE4E5C8-7DB6-49D2-855E-7220B039DDF4}.Release|Any CPU.Build.0 = Release|Any CPU + {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From b2dfc44c86eb1d941e0d48d850fdfc4cae945ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 24 Apr 2023 07:54:35 +0200 Subject: [PATCH 05/30] Fixing not recognized word --- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index 3fc9bfbb..67eb7e13 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -38,8 +38,8 @@ To configure the task, you need to add the following to your `appsettings.json`: "Lombiq_Hosting_Tenants_Maintenance": { "UpdateTenantUrl": { "Enabled": true, - "DefaultTenantUrl": "mydomain.com" - "TenantUrl": "{TenantName}.mydomain.com" + "DefaultTenantUrl": "domain.com" + "TenantUrl": "{TenantName}.domain.com" } }, } From f21674b74c3f5c08699ed6c3ada1d664fa25ae4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 24 Apr 2023 08:05:14 +0200 Subject: [PATCH 06/30] Fixing markdown linter issues --- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index 67eb7e13..9394dd65 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -46,4 +46,4 @@ To configure the task, you need to add the following to your `appsettings.json`: } ``` -**NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name.~~~~ \ No newline at end of file +**NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name automatically. From 07bcc19e7f269ca6c8f49e348a07fad58acf269a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 25 Apr 2023 16:54:16 +0200 Subject: [PATCH 07/30] Fixing Readme --- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index 9394dd65..d3d32631 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -28,7 +28,7 @@ It's a maintenance task that updates the tenants' URL based on the app configura public void ConfigureServices(IServiceCollection services) => services.AddOrchardCms( builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.UpdateTenantUrl)); -```~~~~ +``` To configure the task, you need to add the following to your `appsettings.json`: @@ -38,10 +38,10 @@ To configure the task, you need to add the following to your `appsettings.json`: "Lombiq_Hosting_Tenants_Maintenance": { "UpdateTenantUrl": { "Enabled": true, - "DefaultTenantUrl": "domain.com" + "DefaultTenantUrl": "domain.com", "TenantUrl": "{TenantName}.domain.com" } - }, + } } } ``` From 1273e7a7e4e551acd805cdfd09320813656af971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 8 May 2023 15:04:29 +0200 Subject: [PATCH 08/30] Fixing analyzer violation --- .../Extensions/MaintenanceTaskExecutionContextExtensions.cs | 4 +++- .../UpdateShellRequestUrlMaintenanceProvider.cs | 1 + .../UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs | 1 + .../Services/MaintenanceProviderBase.cs | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs index c318d596..25897e7b 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Extensions/MaintenanceTaskExecutionContextExtensions.cs @@ -1,4 +1,6 @@ -namespace Lombiq.Hosting.Tenants.Maintenance.Models; +using Lombiq.Hosting.Tenants.Maintenance.Models; + +namespace Lombiq.Hosting.Tenants.Maintenance.Extensions; public static class MaintenanceTaskExecutionContextExtensions { diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs index d0130924..af7744db 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs @@ -1,3 +1,4 @@ +using Lombiq.Hosting.Tenants.Maintenance.Extensions; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Options; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs index ef9c77a1..2e2143be 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -1,3 +1,4 @@ +using Lombiq.Hosting.Tenants.Maintenance.Extensions; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Options; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs index 39982451..f6e9317f 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceProviderBase.cs @@ -1,3 +1,4 @@ +using Lombiq.Hosting.Tenants.Maintenance.Extensions; using Lombiq.Hosting.Tenants.Maintenance.Models; using System.Threading.Tasks; From 114e6a3165f45144e602b6d0d3be602058a8124a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 8 May 2023 20:04:27 +0200 Subject: [PATCH 09/30] Temporarily disabling maintenance providers --- .editorconfig | 310 ++++++++++++++++++ .../Maintenance/UpdateTenantUrl/Startup.cs | 9 +- 2 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..1791262b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,310 @@ +# WARNING: Only edit this file in the Lombiq .NET Analyzers repository's "Lombiq.Analyzers" folder. A copy of this file +# anywhere else will be overwritten. + +# Common .NET conventions, code formatting and naming convention rules. Check out possible configs here: +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference +# If you found some issue then try to fix it, which you can also do from VS Quick Actions: +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#automatically-configure-code-styles +# Add new file types at the bottom. For new rules for C# files follow the structure that VS generates. + +# All files +[*] + +# Basics +charset = utf-8 +guidelines = 120 1px solid a0ffc000, 150 1px solid 80ff0000 + +# Indentation and spacing +indent_size = 4 +indent_style = space +trim_trailing_whitespace = true + + +# New line preferences +end_of_line = crlf +insert_final_newline = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_auto_properties = true:warning + +# C# files +[*.cs] + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:warning +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_property = false:warning + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:warning +dotnet_style_predefined_type_for_member_access = true:warning + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = always:warning + +# Expression-level preferences +dotnet_style_coalesce_expression = true:warning +dotnet_style_collection_initializer = true:warning +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_object_initializer = true:warning +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_return = false +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_simplified_interpolation = true:warning + +# Field preferences +dotnet_style_readonly_field = true:warning + +# Parameter preferences +dotnet_code_quality_unused_parameters = non_pubic:suggestion + +#### C# Style Rules #### + +#IDE* rules are managed here. + +# Note that currently both IDE* rules and csharp_style_* rules are necessary, because only IDE rules will be enforced +# during build, see: https://github.com/dotnet/roslyn/issues/44201. + +# Default severity for analyzer diagnostics for all categories (escalated to build warnings). See +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/categories for the list of categories. +dotnet_analyzer_diagnostic.category-Design.severity = warning +dotnet_analyzer_diagnostic.category-Documentation.severity = warning +dotnet_analyzer_diagnostic.category-Globalization.severity = warning +dotnet_analyzer_diagnostic.category-Interoperability.severity = warning +dotnet_analyzer_diagnostic.category-Maintainability.severity = warning +dotnet_analyzer_diagnostic.category-Naming.severity = warning +dotnet_analyzer_diagnostic.category-Performance.severity = warning +dotnet_analyzer_diagnostic.category-SingleFile.severity = warning +dotnet_analyzer_diagnostic.category-Reliability.severity = warning +dotnet_analyzer_diagnostic.category-Security.severity = warning +dotnet_analyzer_diagnostic.category-Style.severity = warning +dotnet_analyzer_diagnostic.category-Usage.severity = warning +dotnet_analyzer_diagnostic.category-CodeQuality.severity = warning + +# IDE0011: Add braces to 'if' statement. +# The "when-multiline:warning" config is not actually for cases when the if body is in another line so we have to turn +# this off completely, see: https://github.com/dotnet/roslyn/issues/40912. +dotnet_diagnostic.IDE0011.severity = none +# IDE0050: Convert to tuple +# Quite dangerous as we most frequently use anonymous types to interface with other APIs (like generating routes) and +# those can fail on this silently in runtime while building correctly. +dotnet_diagnostic.IDE0050.severity = none +# IDE0052: Private member can be removed as the value assigned to it is never used. +# We use S4487 for that. +dotnet_diagnostic.IDE0052.severity = none +# IDE0072: Populate switch +# Also signals on switches with discards, by design: https://github.com/dotnet/roslyn/issues/48876. +dotnet_diagnostic.IDE0072.severity = none +# "Namespace 'Foo' does not match folder structure, expected 'Foo'" +dotnet_diagnostic.IDE0130.severity = none + +# While these are in the Style category, they need to be explicitly set for some reason. +# IDE0079 Remove unnecessary suppression +dotnet_diagnostic.IDE0079.severity = warning +dotnet_diagnostic.IDE0022.severity = warning + +# 'var' preferences +dotnet_diagnostic.IDE0007.severity = warning +dotnet_diagnostic.IDE0008.severity = none + +# We don't want to decorate every public member with XML comments, only public APIs - so turn CS1591 off. +# CS1591 Missing XML Comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = none + +#### C# Coding Conventions #### + +# 'var' preferences +# These won't take effect during build due to this bug: +# https://github.com/dotnet/roslyn/issues/44250 +csharp_style_var_elsewhere = true:warning +# If this is not turned off then there will be messages for e.g. integers too, like in for loops. +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = true:warning + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:warning +csharp_style_expression_bodied_constructors = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = true:warning +csharp_style_expression_bodied_methods = true:warning +csharp_style_expression_bodied_operators = true:warning +csharp_style_expression_bodied_properties = true:warning + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_prefer_switch_expression = true:warning + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:warning + +# Modifier preferences +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_simple_using_statement = true:warning +# IDE0160: Convert to block-scoped namespace +csharp_style_namespace_declarations = file_scoped:warning + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:warning +csharp_style_deconstructed_variable_declaration = false:silent +csharp_style_inlined_variable_declaration = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning +csharp_style_throw_expression = true:warning +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:none + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = no_change +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = * +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = * +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = * +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +# Disabling "catch a more specific exception type" suggestion which is overwhelmingly a false positive. +dotnet_diagnostic.CA1031.severity = silent +# Disabling "do not nest type" suggestion. +dotnet_diagnostic.CA1034.severity = silent +# Disabling "CA1810: Initialize reference type static fields inline" as RSPEC-3963 is used instead. +dotnet_diagnostic.CA1810.severity = none + +# Don't apply "CA1822: Mark members as static" to the public API +dotnet_code_quality.ca1822.api_surface = private, internal +csharp_prefer_braces = true:silent + +# Various config files +[*.{config,csproj,json,props,targets}] + +indent_size = 2 + +# Markdown files +[*.md] + +trim_trailing_whitespace = false + +# JavaScript files +[*.js] + +# Placeholder, no unique rules for JS files at the moment. + + +# SCSS files +[*.scss] + +# Placeholder, no unique rules for SCSS files at the moment. + +# PowerShell files +[*.ps1] + +# Placeholder, no unique rules for PS files at the moment. diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs index a947f151..6620b2f5 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs @@ -1,4 +1,4 @@ -using Lombiq.Hosting.Tenants.Maintenance.Services; +using Lombiq.Hosting.Tenants.Maintenance.Constants; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Environment.Shell.Configuration; @@ -6,7 +6,7 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; -[Feature(Constants.FeatureNames.UpdateTenantUrl)] +[Feature(FeatureNames.UpdateTenantUrl)] public class Startup : StartupBase { private readonly IShellConfiguration _shellConfiguration; @@ -16,8 +16,9 @@ public Startup(IShellConfiguration shellConfiguration) => public override void ConfigureServices(IServiceCollection services) { - services.AddScoped(); - services.AddScoped(); + // Temporarily commenting out. + //// services.AddScoped(); + //// services.AddScoped(); var options = new UpdateTenantUrlMaintenanceOptions(); var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:UpdateTenantUrl"); From a2ee6810096297e7ef8b3996b5891ad2556c9b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 9 May 2023 07:26:46 +0200 Subject: [PATCH 10/30] Temporarily commenting out more code --- Lombiq.Hosting.Tenants.Maintenance/Startup.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs index 233d5a87..feb0d1ee 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs @@ -1,6 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Indexes; -using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Data; using OrchardCore.Data.Migration; @@ -16,7 +15,9 @@ public override void ConfigureServices(IServiceCollection services) services.Configure(o => o.Collections.Add(DocumentCollections.Maintenance)); services.AddScoped(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + + // Temporarily commenting out. + //// services.AddScoped(); + //// services.AddScoped(); } } From 44882f45bd08428bda6a6e995cad0ab1b05fac28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 9 May 2023 15:26:41 +0200 Subject: [PATCH 11/30] Restoring commented code --- .../Maintenance/UpdateTenantUrl/Startup.cs | 6 +++--- Lombiq.Hosting.Tenants.Maintenance/Startup.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs index 6620b2f5..46a9d316 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs @@ -1,4 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Environment.Shell.Configuration; @@ -16,9 +17,8 @@ public Startup(IShellConfiguration shellConfiguration) => public override void ConfigureServices(IServiceCollection services) { - // Temporarily commenting out. - //// services.AddScoped(); - //// services.AddScoped(); + services.AddScoped(); + services.AddScoped(); var options = new UpdateTenantUrlMaintenanceOptions(); var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:UpdateTenantUrl"); diff --git a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs index feb0d1ee..f43b3abc 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs @@ -1,5 +1,6 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Indexes; +using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Data; using OrchardCore.Data.Migration; @@ -16,8 +17,7 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddSingleton(); - // Temporarily commenting out. - //// services.AddScoped(); - //// services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } From 93b3fe2065764cbec28ebf1a7b23db4745ed72be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 9 May 2023 17:25:46 +0200 Subject: [PATCH 12/30] Disabling runner temporarily --- Lombiq.Hosting.Tenants.Maintenance/Startup.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs index f43b3abc..feb0d1ee 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs @@ -1,6 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Indexes; -using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Data; using OrchardCore.Data.Migration; @@ -17,7 +16,8 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + // Temporarily commenting out. + //// services.AddScoped(); + //// services.AddScoped(); } } From c9e7766c8068d627ad1668974af989ffcebd12d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 9 May 2023 17:54:55 +0200 Subject: [PATCH 13/30] Readding maintenance runner --- Lombiq.Hosting.Tenants.Maintenance/Startup.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs index feb0d1ee..f43b3abc 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs @@ -1,5 +1,6 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Indexes; +using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Data; using OrchardCore.Data.Migration; @@ -16,8 +17,7 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddSingleton(); - // Temporarily commenting out. - //// services.AddScoped(); - //// services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } From 16a45fae764350c45ec640792ab2964a44465ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 9 May 2023 18:24:21 +0200 Subject: [PATCH 14/30] Emptying out runner --- .../Services/MaintenanceRunnerService.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs index a07576f1..22c1752b 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs @@ -1,4 +1,3 @@ -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OrchardCore.Environment.Shell; using OrchardCore.Environment.Shell.Models; @@ -10,8 +9,10 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Services; public class MaintenanceRunnerService : ModularTenantEvents { private readonly ShellSettings _shellSettings; +#pragma warning disable S4487 private readonly ILogger _logger; private readonly IShellHost _shellHost; +#pragma warning restore S4487 public MaintenanceRunnerService( ShellSettings shellSettings, @@ -23,17 +24,18 @@ public MaintenanceRunnerService( _shellHost = shellHost; } - public override async Task ActivatedAsync() + public override Task ActivatedAsync() { - if (_shellSettings.State != TenantState.Running) return; + if (_shellSettings.State != TenantState.Running) return Task.CompletedTask; + return Task.CompletedTask; // Getting the scope here is important because the shell might not be fully initialized yet. - var shellScope = await _shellHost.GetScopeAsync(_shellSettings.Name); - var maintenanceManager = shellScope.ServiceProvider.GetService(); - - _logger.LogInformation( - "Executing maintenance tasks on shell '{ShellName}'.", - _shellSettings.Name); - await maintenanceManager.ExecuteMaintenanceTasksAsync(); + //// var shellScope = await _shellHost.GetScopeAsync(_shellSettings.Name); + //// var maintenanceManager = shellScope.ServiceProvider.GetService(); + //// + //// _logger.LogInformation( + //// "Executing maintenance tasks on shell '{ShellName}'.", + //// _shellSettings.Name); + //// await maintenanceManager.ExecuteMaintenanceTasksAsync(); } } From 595edd4f5645dd62ed110c272cff65ee5775d091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 9 May 2023 18:46:05 +0200 Subject: [PATCH 15/30] Adding GetScopeAsync line --- .../Services/MaintenanceRunnerService.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs index 22c1752b..0f5c15c1 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs @@ -24,13 +24,16 @@ public MaintenanceRunnerService( _shellHost = shellHost; } - public override Task ActivatedAsync() + public override async Task ActivatedAsync() { - if (_shellSettings.State != TenantState.Running) return Task.CompletedTask; - return Task.CompletedTask; + if (_shellSettings.State != TenantState.Running) return; +#pragma warning disable IDE0059 +#pragma warning disable S1481 + var shellScope = await _shellHost.GetScopeAsync(_shellSettings.Name); +#pragma warning restore S1481 +#pragma warning restore IDE0059 // Getting the scope here is important because the shell might not be fully initialized yet. - //// var shellScope = await _shellHost.GetScopeAsync(_shellSettings.Name); //// var maintenanceManager = shellScope.ServiceProvider.GetService(); //// //// _logger.LogInformation( From 908bc34820fc884f380f1749b27c3b92ffee16ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Wed, 10 May 2023 15:51:47 +0200 Subject: [PATCH 16/30] Not using IShellHost --- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 4 ++-- .../Services/MaintenanceRunnerService.cs | 23 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index d3d32631..f89b687e 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -2,7 +2,7 @@ ## About -With the help of this module you can execute maintenance tasks on tenants. +With the help of this module you can execute maintenance tasks on tenants. These tasks can be anything that you want to run on tenants, like updating the tenants' URL based on the app configuration. ## Documentation @@ -30,7 +30,7 @@ public void ConfigureServices(IServiceCollection services) => builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.UpdateTenantUrl)); ``` -To configure the task, you need to add the following to your `appsettings.json`: +To configure the task, you need to add the following to your _appsettings.json_: ```json { diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs index 0f5c15c1..9138a681 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs @@ -9,36 +9,27 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Services; public class MaintenanceRunnerService : ModularTenantEvents { private readonly ShellSettings _shellSettings; -#pragma warning disable S4487 private readonly ILogger _logger; - private readonly IShellHost _shellHost; -#pragma warning restore S4487 + private readonly IMaintenanceManager _maintenanceManager; public MaintenanceRunnerService( ShellSettings shellSettings, ILogger logger, - IShellHost shellHost) + IMaintenanceManager maintenanceManager) { _shellSettings = shellSettings; _logger = logger; - _shellHost = shellHost; + _maintenanceManager = maintenanceManager; } public override async Task ActivatedAsync() { if (_shellSettings.State != TenantState.Running) return; -#pragma warning disable IDE0059 -#pragma warning disable S1481 - var shellScope = await _shellHost.GetScopeAsync(_shellSettings.Name); -#pragma warning restore S1481 -#pragma warning restore IDE0059 // Getting the scope here is important because the shell might not be fully initialized yet. - //// var maintenanceManager = shellScope.ServiceProvider.GetService(); - //// - //// _logger.LogInformation( - //// "Executing maintenance tasks on shell '{ShellName}'.", - //// _shellSettings.Name); - //// await maintenanceManager.ExecuteMaintenanceTasksAsync(); + _logger.LogInformation( + "Executing maintenance tasks on shell '{ShellName}'.", + _shellSettings.Name); + await _maintenanceManager.ExecuteMaintenanceTasksAsync(); } } From b6a1d5675d27a4064dcc50bef5facad7eecd210e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sat, 13 May 2023 09:21:23 +0200 Subject: [PATCH 17/30] Separating tenant URL maintenance tasks --- .../Constants/FeatureNames.cs | 4 ++- .../Startup.cs | 14 ++++----- .../TenantUrlHelper.cs | 4 +-- .../TenantUrlMaintenanceOptions.cs | 7 +++++ .../UpdateShellRequestUrl/Startup.cs | 13 +++++++++ ...ateShellRequestUrlsMaintenanceProvider.cs} | 19 +++++------- .../Maintenance/UpdateSiteUrl/Startup.cs | 13 +++++++++ .../UpdateSiteUrlMaintenanceProvider.cs | 13 +++------ .../UpdateTenantUrlMaintenanceOptions.cs | 8 ----- .../Manifest.cs | 23 +++++++++++++-- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 29 +++++++++++-------- 11 files changed, 92 insertions(+), 55 deletions(-) rename Lombiq.Hosting.Tenants.Maintenance/Maintenance/{UpdateTenantUrl => TenantUrlMaintenanceCore}/Startup.cs (53%) rename Lombiq.Hosting.Tenants.Maintenance/Maintenance/{UpdateTenantUrl => TenantUrlMaintenanceCore}/TenantUrlHelper.cs (77%) create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs rename Lombiq.Hosting.Tenants.Maintenance/Maintenance/{UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs => UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs} (70%) create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs rename Lombiq.Hosting.Tenants.Maintenance/Maintenance/{UpdateTenantUrl => UpdateSiteUrl}/UpdateSiteUrlMaintenanceProvider.cs (66%) delete mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs diff --git a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs index ceb993a9..290ddf3f 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs @@ -4,5 +4,7 @@ public static class FeatureNames { public const string Module = "Lombiq.Hosting.Tenants.Maintenance"; public const string Maintenance = Module; - public const string UpdateTenantUrl = Maintenance + "." + nameof(UpdateTenantUrl); + public const string TenantUrlMaintenanceCore = Maintenance + "." + nameof(TenantUrlMaintenanceCore); + public const string UpdateSiteUrl = Maintenance + "." + nameof(UpdateSiteUrl); + public const string UpdateShellRequestUrls = Maintenance + "." + nameof(UpdateShellRequestUrls); } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs similarity index 53% rename from Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs rename to Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs index 46a9d316..e3ae7dfb 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs @@ -1,13 +1,12 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; -using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; -[Feature(FeatureNames.UpdateTenantUrl)] +[Feature(FeatureNames.TenantUrlMaintenanceCore)] public class Startup : StartupBase { private readonly IShellConfiguration _shellConfiguration; @@ -17,12 +16,9 @@ public Startup(IShellConfiguration shellConfiguration) => public override void ConfigureServices(IServiceCollection services) { - services.AddScoped(); - services.AddScoped(); - - var options = new UpdateTenantUrlMaintenanceOptions(); - var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:UpdateTenantUrl"); + var options = new TenantUrlMaintenanceOptions(); + var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:TenantUrlMaintenance"); configSection.Bind(options); - services.Configure(configSection); + services.Configure(configSection); } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs similarity index 77% rename from Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs rename to Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs index b018a9bb..36c47c7b 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/TenantUrlHelper.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs @@ -1,10 +1,10 @@ using OrchardCore.Environment.Shell; -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; internal static class TenantUrlHelper { - public static string GetTenantUrl(UpdateTenantUrlMaintenanceOptions options, ShellSettings shellSettings) + public static string GetTenantUrl(TenantUrlMaintenanceOptions options, ShellSettings shellSettings) { #pragma warning disable CA1308 // Normalize strings to uppercase var evaluatedTenantUrl = options.TenantUrl.Replace( diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs new file mode 100644 index 00000000..78099982 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs @@ -0,0 +1,7 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; + +public class TenantUrlMaintenanceOptions +{ + public string DefaultTenantUrl { get; set; } + public string TenantUrl { get; set; } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs new file mode 100644 index 00000000..51d07003 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs @@ -0,0 +1,13 @@ +using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Modules; + +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; + +[Feature(FeatureNames.UpdateShellRequestUrls)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) => + services.AddScoped(); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs similarity index 70% rename from Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs rename to Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs index af7744db..666b3d3d 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateShellRequestUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs @@ -1,4 +1,5 @@ using Lombiq.Hosting.Tenants.Maintenance.Extensions; +using Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Options; @@ -7,17 +8,17 @@ using System.Linq; using System.Threading.Tasks; -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; -public class UpdateShellRequestUrlMaintenanceProvider : MaintenanceProviderBase +public class UpdateShellRequestUrlsMaintenanceProvider : MaintenanceProviderBase { private readonly ShellSettings _shellSettings; - private readonly IOptions _options; + private readonly IOptions _options; private readonly IShellSettingsManager _shellSettingsManager; - public UpdateShellRequestUrlMaintenanceProvider( + public UpdateShellRequestUrlsMaintenanceProvider( ShellSettings shellSettings, - IOptions options, + IOptions options, IShellSettingsManager shellSettingsManager) { _shellSettings = shellSettings; @@ -25,12 +26,8 @@ public UpdateShellRequestUrlMaintenanceProvider( _shellSettingsManager = shellSettingsManager; } - public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) - { - if (!_options.Value.Enabled || !_shellSettings.IsDefaultShell()) return Task.FromResult(false); - - return Task.FromResult(!context.WasLatestExecutionSuccessful()); - } + public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => + Task.FromResult(_shellSettings.IsDefaultShell() && !context.WasLatestExecutionSuccessful()); public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs new file mode 100644 index 00000000..c8ebd16e --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs @@ -0,0 +1,13 @@ +using Lombiq.Hosting.Tenants.Maintenance.Constants; +using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Modules; + +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateSiteUrl; + +[Feature(FeatureNames.UpdateSiteUrl)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) => + services.AddScoped(); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs similarity index 66% rename from Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs rename to Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs index 2e2143be..a41b37a7 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateSiteUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -1,4 +1,4 @@ -using Lombiq.Hosting.Tenants.Maintenance.Extensions; +using Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Options; @@ -6,29 +6,24 @@ using OrchardCore.Settings; using System.Threading.Tasks; -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateSiteUrl; public class UpdateSiteUrlMaintenanceProvider : MaintenanceProviderBase { private readonly ISiteService _siteService; private readonly ShellSettings _shellSettings; - private readonly IOptions _options; + private readonly IOptions _options; public UpdateSiteUrlMaintenanceProvider( ISiteService siteService, ShellSettings shellSettings, - IOptions options) + IOptions options) { _siteService = siteService; _shellSettings = shellSettings; _options = options; } - public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => - !_options.Value.Enabled - ? Task.FromResult(false) - : Task.FromResult(!context.WasLatestExecutionSuccessful()); - public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { var siteSettings = await _siteService.LoadSiteSettingsAsync(); diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs deleted file mode 100644 index fe4c65f4..00000000 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateTenantUrl/UpdateTenantUrlMaintenanceOptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateTenantUrl; - -public class UpdateTenantUrlMaintenanceOptions -{ - public bool Enabled { get; set; } - public string DefaultTenantUrl { get; set; } - public string TenantUrl { get; set; } -} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs index a6d0c6aa..19b5be7e 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs @@ -17,9 +17,26 @@ )] [assembly: Feature( - Id = UpdateTenantUrl, - Name = "Lombiq Hosting - Tenants Maintenance - Update Tenant URL", - Description = "Updates the URL of the tenant (e.g., when the production database is copied to staging).", + Id = TenantUrlMaintenanceCore, + Name = "Lombiq Hosting - Tenants Maintenance - Tenant URL Maintenance Core", + Description = "Provides the core functionality for updating the URL of a tenant (e.g., reading tenant URLs from configuration).", Category = "Maintenance", Dependencies = new[] { Maintenance } )] + +[assembly: Feature( + Id = UpdateSiteUrl, + Name = "Lombiq Hosting - Tenants Maintenance - Update Site URL", + Description = "Updates the URL of the site in the site settings (e.g., when the production database is copied to staging).", + Category = "Maintenance", + Dependencies = new[] { TenantUrlMaintenanceCore } +)] + +[assembly: Feature( + Id = UpdateShellRequestUrls, + Name = "Lombiq Hosting - Tenants Maintenance - Update Shell Request URLs", + Description = "Updates the shell request URLs of each tenant (e.g., when the production database is copied to staging). It's executed only on the default tenant.", + Category = "Maintenance", + DefaultTenantOnly = true, + Dependencies = new[] { TenantUrlMaintenanceCore } +)] diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index f89b687e..cfc764ee 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -20,24 +20,15 @@ public void ConfigureServices(IServiceCollection services) => To add new maintenance tasks, you need to implement the `IMaintenanceProvider` interface and register it as a service. -### `Lombiq.Hosting.Tenants.Maintenance.UpdateTenantUrl` +### `Lombiq.Hosting.Tenants.Maintenance.TenantUrlMaintenanceCore` -It's a maintenance task that updates the tenants' URL based on the app configuration. It is available on any tenant. To make your application execute this task, you need to add the following to your `Startup.cs`: - -```csharp -public void ConfigureServices(IServiceCollection services) => - services.AddOrchardCms( - builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.UpdateTenantUrl)); -``` - -To configure the task, you need to add the following to your _appsettings.json_: +Provides the core functionality for updating the tenants' URL based on the app configuration. It's a dependency of the `UpdateSiteUrl` and `UpdateShellRequestUrls` maintenance tasks and the configuration options affect them as well. The following configuration options are available to set the tenant URLs: ```json { "OrchardCore": { "Lombiq_Hosting_Tenants_Maintenance": { - "UpdateTenantUrl": { - "Enabled": true, + "TenantUrlMaintenance": { "DefaultTenantUrl": "domain.com", "TenantUrl": "{TenantName}.domain.com" } @@ -47,3 +38,17 @@ To configure the task, you need to add the following to your _appsettings.json_: ``` **NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name automatically. + +### `Lombiq.Hosting.Tenants.Maintenance.UpdateSiteUrl` + +It's a maintenance task that updates the site's base URL in the site settings based on the app configuration (see `Lombiq.Hosting.Tenants.Maintenance.TenantUrlMaintenanceCore`). It is available on any tenant. To make your application execute this task, you need to add the following to your `Startup.cs`: + +```csharp +public void ConfigureServices(IServiceCollection services) => + services.AddOrchardCms( + builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.UpdateTenantUrl)); +``` + +### `Lombiq.Hosting.Tenants.Maintenance.UpdateShellRequestUrls` + +It's a maintenance task that updates the shell's request URLs in each tenant's shell settings based on the app configuration (see `Lombiq.Hosting.Tenants.Maintenance.TenantUrlMaintenanceCore`). It is available only for the default tenant. From 69a12ea36867852998407fce15d96da60d06be46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sat, 13 May 2023 16:42:55 +0200 Subject: [PATCH 18/30] Fixing NRE if the config is empty --- .../UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs index a41b37a7..cf54b07a 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -1,3 +1,4 @@ +using Lombiq.Hosting.Tenants.Maintenance.Extensions; using Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; @@ -24,6 +25,9 @@ public UpdateSiteUrlMaintenanceProvider( _options = options; } + public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => + Task.FromResult(!string.IsNullOrEmpty(_options.Value.TenantUrl) && !context.WasLatestExecutionSuccessful()); + public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { var siteSettings = await _siteService.LoadSiteSettingsAsync(); From 0c7e71adb725e0d89c177dc0924d245de2dc69e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sat, 13 May 2023 17:29:37 +0200 Subject: [PATCH 19/30] Randomly updating a totally irrelevant code line for fun --- .../Controllers/TenantLoginController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs b/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs index 9fc195d1..f480eebd 100644 --- a/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs +++ b/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs @@ -77,7 +77,7 @@ public async Task Index(string password) } await _userSignInManager.SignInAsync(adminUser, isPersistent: false); - _logger.LogInformation(1, "An admin user logged in from the Default tenant."); + _logger.LogInformation("An admin user logged in from the Default tenant."); return RedirectToAction("Index", "Admin", new { area = "OrchardCore.Admin" }); } From 221ec4b783beabc3990529d7c00cd3807c6b08d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sat, 13 May 2023 17:37:13 +0200 Subject: [PATCH 20/30] Disabling analyzer warning --- .../Controllers/TenantLoginController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs b/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs index f480eebd..f1cb0700 100644 --- a/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs +++ b/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs @@ -77,7 +77,9 @@ public async Task Index(string password) } await _userSignInManager.SignInAsync(adminUser, isPersistent: false); - _logger.LogInformation("An admin user logged in from the Default tenant."); +#pragma warning disable CA1848 // Use the LoggerMessage delegates + _logger.LogInformation(1, "An admin user logged in from the Default tenant."); +#pragma warning restore CA1848 // Use the LoggerMessage delegates return RedirectToAction("Index", "Admin", new { area = "OrchardCore.Admin" }); } From 541569432834f9d9ad3d9b212f6089d8130d1f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sun, 14 May 2023 19:37:02 +0200 Subject: [PATCH 21/30] Disabling analyzer rule --- .../Handlers/FeaturesEventHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs b/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs index 3da2235a..d08848a8 100644 --- a/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs +++ b/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs @@ -12,7 +12,9 @@ namespace Lombiq.Hosting.Tenants.FeaturesGuard.Handlers; +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix public sealed class FeaturesEventHandler : IFeatureEventHandler +#pragma warning restore CA1711 // Identifiers should not have incorrect suffix { private readonly IShellFeaturesManager _shellFeaturesManager; private readonly IOptions _conditionallyEnabledFeaturesOptions; From 4037d11a8e55c3253003223a83b83fe4360c87c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Sun, 14 May 2023 19:41:38 +0200 Subject: [PATCH 22/30] Restoring analyzer rules and removing editorconfig --- .editorconfig | 310 ------------------ .../Controllers/TenantLoginController.cs | 2 - .../Handlers/FeaturesEventHandler.cs | 2 - 3 files changed, 314 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1791262b..00000000 --- a/.editorconfig +++ /dev/null @@ -1,310 +0,0 @@ -# WARNING: Only edit this file in the Lombiq .NET Analyzers repository's "Lombiq.Analyzers" folder. A copy of this file -# anywhere else will be overwritten. - -# Common .NET conventions, code formatting and naming convention rules. Check out possible configs here: -# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference -# If you found some issue then try to fix it, which you can also do from VS Quick Actions: -# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#automatically-configure-code-styles -# Add new file types at the bottom. For new rules for C# files follow the structure that VS generates. - -# All files -[*] - -# Basics -charset = utf-8 -guidelines = 120 1px solid a0ffc000, 150 1px solid 80ff0000 - -# Indentation and spacing -indent_size = 4 -indent_style = space -trim_trailing_whitespace = true - - -# New line preferences -end_of_line = crlf -insert_final_newline = true -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -dotnet_style_coalesce_expression = true:warning -dotnet_style_null_propagation = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning -dotnet_style_prefer_auto_properties = true:warning - -# C# files -[*.cs] - -#### .NET Coding Conventions #### - -# Organize usings -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = false - -# this. and Me. preferences -dotnet_style_qualification_for_event = false:warning -dotnet_style_qualification_for_field = false:warning -dotnet_style_qualification_for_method = false:warning -dotnet_style_qualification_for_property = false:warning - -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:warning -dotnet_style_predefined_type_for_member_access = true:warning - -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent - -# Modifier preferences -dotnet_style_require_accessibility_modifiers = always:warning - -# Expression-level preferences -dotnet_style_coalesce_expression = true:warning -dotnet_style_collection_initializer = true:warning -dotnet_style_explicit_tuple_names = true:warning -dotnet_style_null_propagation = true:warning -dotnet_style_object_initializer = true:warning -dotnet_style_prefer_auto_properties = true:warning -dotnet_style_prefer_compound_assignment = true:warning -dotnet_style_prefer_conditional_expression_over_assignment = true:warning -dotnet_style_prefer_conditional_expression_over_return = false -dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning -dotnet_style_prefer_inferred_tuple_names = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning -dotnet_style_prefer_simplified_interpolation = true:warning - -# Field preferences -dotnet_style_readonly_field = true:warning - -# Parameter preferences -dotnet_code_quality_unused_parameters = non_pubic:suggestion - -#### C# Style Rules #### - -#IDE* rules are managed here. - -# Note that currently both IDE* rules and csharp_style_* rules are necessary, because only IDE rules will be enforced -# during build, see: https://github.com/dotnet/roslyn/issues/44201. - -# Default severity for analyzer diagnostics for all categories (escalated to build warnings). See -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/categories for the list of categories. -dotnet_analyzer_diagnostic.category-Design.severity = warning -dotnet_analyzer_diagnostic.category-Documentation.severity = warning -dotnet_analyzer_diagnostic.category-Globalization.severity = warning -dotnet_analyzer_diagnostic.category-Interoperability.severity = warning -dotnet_analyzer_diagnostic.category-Maintainability.severity = warning -dotnet_analyzer_diagnostic.category-Naming.severity = warning -dotnet_analyzer_diagnostic.category-Performance.severity = warning -dotnet_analyzer_diagnostic.category-SingleFile.severity = warning -dotnet_analyzer_diagnostic.category-Reliability.severity = warning -dotnet_analyzer_diagnostic.category-Security.severity = warning -dotnet_analyzer_diagnostic.category-Style.severity = warning -dotnet_analyzer_diagnostic.category-Usage.severity = warning -dotnet_analyzer_diagnostic.category-CodeQuality.severity = warning - -# IDE0011: Add braces to 'if' statement. -# The "when-multiline:warning" config is not actually for cases when the if body is in another line so we have to turn -# this off completely, see: https://github.com/dotnet/roslyn/issues/40912. -dotnet_diagnostic.IDE0011.severity = none -# IDE0050: Convert to tuple -# Quite dangerous as we most frequently use anonymous types to interface with other APIs (like generating routes) and -# those can fail on this silently in runtime while building correctly. -dotnet_diagnostic.IDE0050.severity = none -# IDE0052: Private member can be removed as the value assigned to it is never used. -# We use S4487 for that. -dotnet_diagnostic.IDE0052.severity = none -# IDE0072: Populate switch -# Also signals on switches with discards, by design: https://github.com/dotnet/roslyn/issues/48876. -dotnet_diagnostic.IDE0072.severity = none -# "Namespace 'Foo' does not match folder structure, expected 'Foo'" -dotnet_diagnostic.IDE0130.severity = none - -# While these are in the Style category, they need to be explicitly set for some reason. -# IDE0079 Remove unnecessary suppression -dotnet_diagnostic.IDE0079.severity = warning -dotnet_diagnostic.IDE0022.severity = warning - -# 'var' preferences -dotnet_diagnostic.IDE0007.severity = warning -dotnet_diagnostic.IDE0008.severity = none - -# We don't want to decorate every public member with XML comments, only public APIs - so turn CS1591 off. -# CS1591 Missing XML Comment for publicly visible type or member -dotnet_diagnostic.CS1591.severity = none - -#### C# Coding Conventions #### - -# 'var' preferences -# These won't take effect during build due to this bug: -# https://github.com/dotnet/roslyn/issues/44250 -csharp_style_var_elsewhere = true:warning -# If this is not turned off then there will be messages for e.g. integers too, like in for loops. -csharp_style_var_for_built_in_types = false:none -csharp_style_var_when_type_is_apparent = true:warning - -# Expression-bodied members -csharp_style_expression_bodied_accessors = true:warning -csharp_style_expression_bodied_constructors = true:warning -csharp_style_expression_bodied_indexers = true:warning -csharp_style_expression_bodied_lambdas = true:warning -csharp_style_expression_bodied_local_functions = true:warning -csharp_style_expression_bodied_methods = true:warning -csharp_style_expression_bodied_operators = true:warning -csharp_style_expression_bodied_properties = true:warning - -# Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true:warning -csharp_style_pattern_matching_over_is_with_cast_check = true:warning -csharp_style_prefer_switch_expression = true:warning - -# Null-checking preferences -csharp_style_conditional_delegate_call = true:warning - -# Modifier preferences -csharp_prefer_static_local_function = true:warning -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent - -# Code-block preferences -csharp_prefer_simple_using_statement = true:warning -# IDE0160: Convert to block-scoped namespace -csharp_style_namespace_declarations = file_scoped:warning - -# Expression-level preferences -csharp_prefer_simple_default_expression = true:warning -csharp_style_deconstructed_variable_declaration = false:silent -csharp_style_inlined_variable_declaration = true:warning -csharp_style_pattern_local_over_anonymous_function = true:warning -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning -csharp_style_throw_expression = true:warning -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:none - -# 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent - -#### C# Formatting Rules #### - -# New line preferences -csharp_new_line_before_catch = true -csharp_new_line_before_else = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_open_brace = all -csharp_new_line_between_query_expression_clauses = true - -# Indentation preferences -csharp_indent_block_contents = true -csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_case_contents_when_block = true -csharp_indent_labels = no_change -csharp_indent_switch_labels = true - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false -csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_between_square_brackets = false - -# Wrapping preferences -csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = true - -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = * -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = * -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = * -dotnet_naming_symbols.non_field_members.required_modifiers = - -# Naming styles - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -# Disabling "catch a more specific exception type" suggestion which is overwhelmingly a false positive. -dotnet_diagnostic.CA1031.severity = silent -# Disabling "do not nest type" suggestion. -dotnet_diagnostic.CA1034.severity = silent -# Disabling "CA1810: Initialize reference type static fields inline" as RSPEC-3963 is used instead. -dotnet_diagnostic.CA1810.severity = none - -# Don't apply "CA1822: Mark members as static" to the public API -dotnet_code_quality.ca1822.api_surface = private, internal -csharp_prefer_braces = true:silent - -# Various config files -[*.{config,csproj,json,props,targets}] - -indent_size = 2 - -# Markdown files -[*.md] - -trim_trailing_whitespace = false - -# JavaScript files -[*.js] - -# Placeholder, no unique rules for JS files at the moment. - - -# SCSS files -[*.scss] - -# Placeholder, no unique rules for SCSS files at the moment. - -# PowerShell files -[*.ps1] - -# Placeholder, no unique rules for PS files at the moment. diff --git a/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs b/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs index f1cb0700..9fc195d1 100644 --- a/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs +++ b/Lombiq.Hosting.Tenants.Admin.Login/Controllers/TenantLoginController.cs @@ -77,9 +77,7 @@ public async Task Index(string password) } await _userSignInManager.SignInAsync(adminUser, isPersistent: false); -#pragma warning disable CA1848 // Use the LoggerMessage delegates _logger.LogInformation(1, "An admin user logged in from the Default tenant."); -#pragma warning restore CA1848 // Use the LoggerMessage delegates return RedirectToAction("Index", "Admin", new { area = "OrchardCore.Admin" }); } diff --git a/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs b/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs index d08848a8..3da2235a 100644 --- a/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs +++ b/Lombiq.Hosting.Tenants.FeaturesGuard/Handlers/FeaturesEventHandler.cs @@ -12,9 +12,7 @@ namespace Lombiq.Hosting.Tenants.FeaturesGuard.Handlers; -#pragma warning disable CA1711 // Identifiers should not have incorrect suffix public sealed class FeaturesEventHandler : IFeatureEventHandler -#pragma warning restore CA1711 // Identifiers should not have incorrect suffix { private readonly IShellFeaturesManager _shellFeaturesManager; private readonly IOptions _conditionallyEnabledFeaturesOptions; From dd01a5d8466f13be42622e64e9a773b66de43349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 15 May 2023 14:27:30 +0200 Subject: [PATCH 23/30] Minor fixes --- Lombiq.Hosting.Tenants.Maintenance/License.md | 2 +- .../Lombiq.Hosting.Tenants.Maintenance.csproj | 2 +- .../Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs | 1 + Lombiq.Hosting.Tenants.Maintenance/Readme.md | 2 ++ .../Services/IMaintenanceProvider.cs | 2 +- .../Services/MaintenanceManager.cs | 3 +-- .../Services/MaintenanceRunnerService.cs | 3 +-- Lombiq.Hosting.Tenants.Maintenance/Startup.cs | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/License.md b/Lombiq.Hosting.Tenants.Maintenance/License.md index ff974d8e..d57e1305 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/License.md +++ b/Lombiq.Hosting.Tenants.Maintenance/License.md @@ -1,4 +1,4 @@ -Copyright © 2022, [Lombiq Technologies Ltd.](https://lombiq.com) +Copyright © 2021, [Lombiq Technologies Ltd.](https://lombiq.com) All rights reserved. diff --git a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj index 9a14965b..c21d3476 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj +++ b/Lombiq.Hosting.Tenants.Maintenance/Lombiq.Hosting.Tenants.Maintenance.csproj @@ -9,7 +9,7 @@ Lombiq Hosting - Tenants Maintenance for Orchard Core Lombiq Technologies - Copyright © 2022, Lombiq Technologies Ltd. + Copyright © 2021, Lombiq Technologies Ltd. Lombiq Hosting - Tenants Maintenance for Orchard Core: With the help of this module you can execute maintenance tasks on tenants. NuGetIcon.png OrchardCore;Lombiq;AspNetCore;Multitenancy;SaaS;Maintenance diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs index 36c47c7b..101b43f3 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs @@ -6,6 +6,7 @@ internal static class TenantUrlHelper { public static string GetTenantUrl(TenantUrlMaintenanceOptions options, ShellSettings shellSettings) { + // The tenant name is intentionally lowercased here. #pragma warning disable CA1308 // Normalize strings to uppercase var evaluatedTenantUrl = options.TenantUrl.Replace( "{TenantName}", diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index cfc764ee..55745cb2 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -1,5 +1,7 @@ # Lombiq Hosting - Tenant Maintenance for Orchard Core +[![Lombiq.Hosting.Tenants.Maintenance NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Maintenance?label=Lombiq.Hosting.Tenants.Maintenance)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Maintenance/) + ## About With the help of this module you can execute maintenance tasks on tenants. These tasks can be anything that you want to run on tenants, like updating the tenants' URL based on the app configuration. diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs index 1da0352e..0abc2829 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/IMaintenanceProvider.cs @@ -22,7 +22,7 @@ public interface IMaintenanceProvider /// Determines whether the maintenance task should be executed. /// /// Provides information about the current execution. - /// True if the maintenance task should be executed, false otherwise. + /// if the maintenance task should be executed, otherwise. Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context); /// diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs index 9106bc21..afdc86a6 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceManager.cs @@ -45,7 +45,7 @@ public Task GetLatestExecutionByMaintenanceIdAsync public async Task ExecuteMaintenanceTasksAsync() { - var orderedProviders = _maintenanceProviders.OrderBy(p => p.Order); + var orderedProviders = _maintenanceProviders.OrderBy(provider => provider.Order); foreach (var provider in orderedProviders) { var currentExecution = new MaintenanceTaskExecutionData @@ -55,7 +55,6 @@ public async Task ExecuteMaintenanceTasksAsync() }; var context = new MaintenanceTaskExecutionContext { - // GetLatestExecutionTask = () => GetLatestExecutionByMaintenanceIdAsync(provider.Id), LatestExecution = await GetLatestExecutionByMaintenanceIdAsync(provider.Id), CurrentExecution = currentExecution, }; diff --git a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs index 9138a681..a349b0cf 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Services/MaintenanceRunnerService.cs @@ -26,8 +26,7 @@ public override async Task ActivatedAsync() { if (_shellSettings.State != TenantState.Running) return; - // Getting the scope here is important because the shell might not be fully initialized yet. - _logger.LogInformation( + _logger.LogDebug( "Executing maintenance tasks on shell '{ShellName}'.", _shellSettings.Name); await _maintenanceManager.ExecuteMaintenanceTasksAsync(); diff --git a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs index f43b3abc..aad3dd3c 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Startup.cs @@ -13,7 +13,7 @@ public class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) { - services.Configure(o => o.Collections.Add(DocumentCollections.Maintenance)); + services.Configure(options => options.Collections.Add(DocumentCollections.Maintenance)); services.AddScoped(); services.AddSingleton(); From 4218f3e57eb4382e10fa297af6f52723c66614e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 15 May 2023 14:41:17 +0200 Subject: [PATCH 24/30] Updating main readme --- Readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index c7fde6d0..68115e7d 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ # Lombiq Hosting - Tenants for Orchard Core -[![Lombiq.Hosting.Tenants.Admin.Login NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Admin.Login?label=Lombiq.Hosting.Tenants.Admin.Login)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Admin.Login/) [![Lombiq.Hosting.Tenants.FeaturesGuard NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard?label=Lombiq.Hosting.Tenants.FeaturesGuard)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard/) [![Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI?label=Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI/) [![Lombiq.Hosting.Tenants.IdleTenantManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement?label=Lombiq.Hosting.Tenants.IdleTenantManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement/) [![Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI?label=Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI/) [![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) [![Lombiq.Hosting.Tenants.MediaStorageManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement?label=Lombiq.Hosting.Tenants.MediaStorageManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement/) [![Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI?label=Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI/) +[![Lombiq.Hosting.Tenants.Admin.Login NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Admin.Login?label=Lombiq.Hosting.Tenants.Admin.Login)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Admin.Login/) [![Lombiq.Hosting.Tenants.FeaturesGuard NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard?label=Lombiq.Hosting.Tenants.FeaturesGuard)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard/) [![Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI?label=Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI/) [![Lombiq.Hosting.Tenants.IdleTenantManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement?label=Lombiq.Hosting.Tenants.IdleTenantManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement/) [![Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI?label=Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI/) [![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) [![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) [![Lombiq.Hosting.Tenants.MediaStorageManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement?label=Lombiq.Hosting.Tenants.MediaStorageManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement/) [![Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI?label=Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI/) ## About @@ -17,6 +17,7 @@ The project consists of the following independent modules: - [Lombiq Hosting - Tenants Features Guard](Lombiq.Hosting.Tenants.FeaturesGuard/Readme.md) - [Lombiq Hosting - Tenants Admin Login](Lombiq.Hosting.Tenants.Admin.Login/Readme.md) - [Lombiq Hosting - Tenants Management](Lombiq.Hosting.Tenants.Management/Readme.md) +- [Lombiq Hosting - Tenants Maintenance](Lombiq.Hosting.Tenants.Maintenance/Readme.md) - [Lombiq Hosting - Tenants Idle Tenant Management](Lombiq.Hosting.Tenants.IdleTenantManagement/Readme.md) - [Lombiq Hosting - Tenants Media Storage Management](Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md) From 3bc722c7314f1ddcad20368bfc04843f961ee3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Mon, 15 May 2023 20:31:41 +0200 Subject: [PATCH 25/30] Updating tenant URL updating providers --- .../Constants/FeatureNames.cs | 1 - .../Helpers/TenantUrlHelpers.cs | 10 +++++ .../TenantUrlMaintenanceCore/Startup.cs | 24 ----------- .../TenantUrlHelper.cs | 23 ----------- .../TenantUrlMaintenanceOptions.cs | 7 ---- .../UpdateShellRequestUrl/Startup.cs | 16 +++++++- ...UpdateShellRequestUrlMaintenanceOptions.cs | 8 ++++ ...dateShellRequestUrlsMaintenanceProvider.cs | 30 ++++++++++---- .../Maintenance/UpdateSiteUrl/Startup.cs | 16 +++++++- .../UpdateSiteUrlMaintenanceOptions.cs | 6 +++ .../UpdateSiteUrlMaintenanceProvider.cs | 12 ++---- .../Manifest.cs | 12 +----- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 40 ++++++++++++++++++- 13 files changed, 120 insertions(+), 85 deletions(-) create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs delete mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs delete mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs delete mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs diff --git a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs index 290ddf3f..bbe6ae41 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Constants/FeatureNames.cs @@ -4,7 +4,6 @@ public static class FeatureNames { public const string Module = "Lombiq.Hosting.Tenants.Maintenance"; public const string Maintenance = Module; - public const string TenantUrlMaintenanceCore = Maintenance + "." + nameof(TenantUrlMaintenanceCore); public const string UpdateSiteUrl = Maintenance + "." + nameof(UpdateSiteUrl); public const string UpdateShellRequestUrls = Maintenance + "." + nameof(UpdateShellRequestUrls); } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs b/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs new file mode 100644 index 00000000..7a21196a --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs @@ -0,0 +1,10 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Helpers; + +internal static class TenantUrlHelpers +{ + public static string ReplaceTenantName(string url, string tenantName) => + // Evaluate the tenant name in lowercase as it will be used in the URL or request URL prefixes. +#pragma warning disable CA1308 // Normalize strings to uppercase + url?.Replace("{TenantName}", tenantName.ToLowerInvariant()); +#pragma warning restore CA1308 // Normalize strings to uppercase +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs deleted file mode 100644 index e3ae7dfb..00000000 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/Startup.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Lombiq.Hosting.Tenants.Maintenance.Constants; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.Environment.Shell.Configuration; -using OrchardCore.Modules; - -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; - -[Feature(FeatureNames.TenantUrlMaintenanceCore)] -public class Startup : StartupBase -{ - private readonly IShellConfiguration _shellConfiguration; - - public Startup(IShellConfiguration shellConfiguration) => - _shellConfiguration = shellConfiguration; - - public override void ConfigureServices(IServiceCollection services) - { - var options = new TenantUrlMaintenanceOptions(); - var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:TenantUrlMaintenance"); - configSection.Bind(options); - services.Configure(configSection); - } -} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs deleted file mode 100644 index 101b43f3..00000000 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlHelper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using OrchardCore.Environment.Shell; - -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; - -internal static class TenantUrlHelper -{ - public static string GetTenantUrl(TenantUrlMaintenanceOptions options, ShellSettings shellSettings) - { - // The tenant name is intentionally lowercased here. -#pragma warning disable CA1308 // Normalize strings to uppercase - var evaluatedTenantUrl = options.TenantUrl.Replace( - "{TenantName}", - shellSettings.Name.ToLowerInvariant()); -#pragma warning restore CA1308 // Normalize strings to uppercase - var defaultTenantUrl = - string.IsNullOrEmpty(options.DefaultTenantUrl) - ? evaluatedTenantUrl - : options.DefaultTenantUrl; - return shellSettings.IsDefaultShell() - ? defaultTenantUrl - : evaluatedTenantUrl; - } -} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs deleted file mode 100644 index 78099982..00000000 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/TenantUrlMaintenanceCore/TenantUrlMaintenanceOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; - -public class TenantUrlMaintenanceOptions -{ - public string DefaultTenantUrl { get; set; } - public string TenantUrl { get; set; } -} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs index 51d07003..2b21fa1e 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/Startup.cs @@ -1,6 +1,8 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; @@ -8,6 +10,18 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; [Feature(FeatureNames.UpdateShellRequestUrls)] public class Startup : StartupBase { - public override void ConfigureServices(IServiceCollection services) => + private readonly IShellConfiguration _shellConfiguration; + + public Startup(IShellConfiguration shellConfiguration) => + _shellConfiguration = shellConfiguration; + + public override void ConfigureServices(IServiceCollection services) + { + var options = new UpdateShellRequestUrlMaintenanceOptions(); + var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:UpdateShellRequestUrl"); + configSection.Bind(options); + services.Configure(configSection); + services.AddScoped(); + } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs new file mode 100644 index 00000000..0ae38ebd --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs @@ -0,0 +1,8 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; + +public class UpdateShellRequestUrlMaintenanceOptions +{ + public string DefaultShellRequestUrl { get; set; } + public string RequestUrl { get; set; } + public string RequestUrlPrefix { get; set; } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs index 666b3d3d..80d7a526 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs @@ -1,11 +1,9 @@ using Lombiq.Hosting.Tenants.Maintenance.Extensions; -using Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; +using Lombiq.Hosting.Tenants.Maintenance.Helpers; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell; -using OrchardCore.Environment.Shell.Models; -using System.Linq; using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; @@ -13,12 +11,12 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; public class UpdateShellRequestUrlsMaintenanceProvider : MaintenanceProviderBase { private readonly ShellSettings _shellSettings; - private readonly IOptions _options; + private readonly IOptions _options; private readonly IShellSettingsManager _shellSettingsManager; public UpdateShellRequestUrlsMaintenanceProvider( ShellSettings shellSettings, - IOptions options, + IOptions options, IShellSettingsManager shellSettingsManager) { _shellSettings = shellSettings; @@ -32,13 +30,29 @@ public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext co public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { var allShellSettings = await _shellSettingsManager.LoadSettingsAsync(); - foreach (var shellSettings in allShellSettings.Where(settings => settings.State == TenantState.Running)) + foreach (var shellSettings in allShellSettings) { - string tenantUrl = TenantUrlHelper.GetTenantUrl(_options.Value, shellSettings); - shellSettings.RequestUrlHost = tenantUrl; + shellSettings.RequestUrlHost = GetTenantUrl(_options.Value, shellSettings); + shellSettings.RequestUrlPrefix = TenantUrlHelpers.ReplaceTenantName( + _options.Value.RequestUrlPrefix, + shellSettings.Name); + await _shellSettingsManager.SaveSettingsAsync(shellSettings); } context.ReloadShellAfterMaintenanceCompletion = true; } + + private static string GetTenantUrl(UpdateShellRequestUrlMaintenanceOptions options, ShellSettings shellSettings) + { + var evaluatedRequestUrl = !string.IsNullOrEmpty(options.RequestUrl) + ? TenantUrlHelpers.ReplaceTenantName(options.RequestUrl, shellSettings.Name) + : string.Empty; + var defaultShellRequestUrl = + string.IsNullOrEmpty(options.DefaultShellRequestUrl) + ? evaluatedRequestUrl + : options.DefaultShellRequestUrl; + + return shellSettings.IsDefaultShell() ? defaultShellRequestUrl : evaluatedRequestUrl; + } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs index c8ebd16e..ff86c21c 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/Startup.cs @@ -1,6 +1,8 @@ using Lombiq.Hosting.Tenants.Maintenance.Constants; using Lombiq.Hosting.Tenants.Maintenance.Services; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateSiteUrl; @@ -8,6 +10,18 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateSiteUrl; [Feature(FeatureNames.UpdateSiteUrl)] public class Startup : StartupBase { - public override void ConfigureServices(IServiceCollection services) => + private readonly IShellConfiguration _shellConfiguration; + + public Startup(IShellConfiguration shellConfiguration) => + _shellConfiguration = shellConfiguration; + + public override void ConfigureServices(IServiceCollection services) + { + var options = new UpdateSiteUrlMaintenanceOptions(); + var configSection = _shellConfiguration.GetSection("Lombiq_Hosting_Tenants_Maintenance:UpdateSiteUrl"); + configSection.Bind(options); + services.Configure(configSection); + services.AddScoped(); + } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs new file mode 100644 index 00000000..98b403c5 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs @@ -0,0 +1,6 @@ +namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateSiteUrl; + +public class UpdateSiteUrlMaintenanceOptions +{ + public string SiteUrl { get; set; } +} diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs index cf54b07a..d7fa1478 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -1,5 +1,4 @@ -using Lombiq.Hosting.Tenants.Maintenance.Extensions; -using Lombiq.Hosting.Tenants.Maintenance.Maintenance.TenantUrlMaintenanceCore; +using Lombiq.Hosting.Tenants.Maintenance.Helpers; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; using Microsoft.Extensions.Options; @@ -13,25 +12,22 @@ public class UpdateSiteUrlMaintenanceProvider : MaintenanceProviderBase { private readonly ISiteService _siteService; private readonly ShellSettings _shellSettings; - private readonly IOptions _options; + private readonly IOptions _options; public UpdateSiteUrlMaintenanceProvider( ISiteService siteService, ShellSettings shellSettings, - IOptions options) + IOptions options) { _siteService = siteService; _shellSettings = shellSettings; _options = options; } - public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => - Task.FromResult(!string.IsNullOrEmpty(_options.Value.TenantUrl) && !context.WasLatestExecutionSuccessful()); - public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { var siteSettings = await _siteService.LoadSiteSettingsAsync(); - siteSettings.BaseUrl = TenantUrlHelper.GetTenantUrl(_options.Value, _shellSettings); + siteSettings.BaseUrl = TenantUrlHelpers.ReplaceTenantName(_options.Value.SiteUrl, _shellSettings.Name); await _siteService.UpdateSiteSettingsAsync(siteSettings); } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs index 19b5be7e..3c8ebf55 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Manifest.cs @@ -16,20 +16,12 @@ Dependencies = new[] { "OrchardCore.Tenants" } )] -[assembly: Feature( - Id = TenantUrlMaintenanceCore, - Name = "Lombiq Hosting - Tenants Maintenance - Tenant URL Maintenance Core", - Description = "Provides the core functionality for updating the URL of a tenant (e.g., reading tenant URLs from configuration).", - Category = "Maintenance", - Dependencies = new[] { Maintenance } -)] - [assembly: Feature( Id = UpdateSiteUrl, Name = "Lombiq Hosting - Tenants Maintenance - Update Site URL", Description = "Updates the URL of the site in the site settings (e.g., when the production database is copied to staging).", Category = "Maintenance", - Dependencies = new[] { TenantUrlMaintenanceCore } + Dependencies = new[] { Maintenance } )] [assembly: Feature( @@ -38,5 +30,5 @@ Description = "Updates the shell request URLs of each tenant (e.g., when the production database is copied to staging). It's executed only on the default tenant.", Category = "Maintenance", DefaultTenantOnly = true, - Dependencies = new[] { TenantUrlMaintenanceCore } + Dependencies = new[] { Maintenance } )] diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index 55745cb2..d4f7c2c0 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -43,7 +43,9 @@ Provides the core functionality for updating the tenants' URL based on the app c ### `Lombiq.Hosting.Tenants.Maintenance.UpdateSiteUrl` -It's a maintenance task that updates the site's base URL in the site settings based on the app configuration (see `Lombiq.Hosting.Tenants.Maintenance.TenantUrlMaintenanceCore`). It is available on any tenant. To make your application execute this task, you need to add the following to your `Startup.cs`: +It's a maintenance task that updates the site's base URL in the site settings based on the app configuration. It is available on any tenant. + +To make your application execute this task, you need to add the following to your `Startup.cs`: ```csharp public void ConfigureServices(IServiceCollection services) => @@ -51,6 +53,40 @@ public void ConfigureServices(IServiceCollection services) => builder => builder.AddTenantFeatures(Lombiq.Hosting.Tenants.Maintenance.Constants.FeatureNames.UpdateTenantUrl)); ``` +The following configuration options are available to set the site URL: + +```json +{ + "OrchardCore": { + "Lombiq_Hosting_Tenants_Maintenance": { + "UpdateSiteUrl": { + "SiteUrl": "https://domain.com/{TenantName}" + } + } + } +} +``` + +**NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name automatically. + ### `Lombiq.Hosting.Tenants.Maintenance.UpdateShellRequestUrls` -It's a maintenance task that updates the shell's request URLs in each tenant's shell settings based on the app configuration (see `Lombiq.Hosting.Tenants.Maintenance.TenantUrlMaintenanceCore`). It is available only for the default tenant. +It's a maintenance task that updates the shell's request URLs in each tenant's shell settings based on the app configuration. It is available only for the default tenant. + +The following configuration options are available to set the shell request URLs: + +```json +{ + "OrchardCore": { + "Lombiq_Hosting_Tenants_Maintenance": { + "UpdateShellRequestUrl": { + "DefaultRequestUrl": "domain.com", + "RequestUrl": "{TenantName}.domain.com", + "RequestUrlPrefix": "{TenantName}" + } + } + } +} +``` + +**NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name automatically. From 529b51889ac52a79e7ea58a9e493c4857206b294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 16 May 2023 16:34:03 +0200 Subject: [PATCH 26/30] Updating SiteUrl logic, adding IsEnabled --- .../Helpers/TenantUrlHelpers.cs | 15 +++++++++++ ...UpdateShellRequestUrlMaintenanceOptions.cs | 1 + ...dateShellRequestUrlsMaintenanceProvider.cs | 22 +++++----------- .../UpdateSiteUrlMaintenanceOptions.cs | 2 ++ .../UpdateSiteUrlMaintenanceProvider.cs | 10 ++++++- Lombiq.Hosting.Tenants.Maintenance/Readme.md | 26 ++++--------------- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs b/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs index 7a21196a..7f7a214a 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Helpers/TenantUrlHelpers.cs @@ -1,3 +1,5 @@ +using OrchardCore.Environment.Shell; + namespace Lombiq.Hosting.Tenants.Maintenance.Helpers; internal static class TenantUrlHelpers @@ -7,4 +9,17 @@ public static string ReplaceTenantName(string url, string tenantName) => #pragma warning disable CA1308 // Normalize strings to uppercase url?.Replace("{TenantName}", tenantName.ToLowerInvariant()); #pragma warning restore CA1308 // Normalize strings to uppercase + + public static string GetTenantUrl(string urlForDefaultTenant, string urlForAnyTenant, ShellSettings shellSettings) + { + var evaluatedRequestUrl = !string.IsNullOrEmpty(urlForAnyTenant) + ? ReplaceTenantName(urlForAnyTenant, shellSettings.Name) + : string.Empty; + var defaultShellRequestUrl = + string.IsNullOrEmpty(urlForDefaultTenant) + ? evaluatedRequestUrl + : urlForDefaultTenant; + + return shellSettings.IsDefaultShell() ? defaultShellRequestUrl : evaluatedRequestUrl; + } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs index 0ae38ebd..4f23e550 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlMaintenanceOptions.cs @@ -2,6 +2,7 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateShellRequestUrl; public class UpdateShellRequestUrlMaintenanceOptions { + public bool IsEnabled { get; set; } public string DefaultShellRequestUrl { get; set; } public string RequestUrl { get; set; } public string RequestUrlPrefix { get; set; } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs index 80d7a526..2f5c7e53 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateShellRequestUrl/UpdateShellRequestUrlsMaintenanceProvider.cs @@ -25,14 +25,19 @@ public UpdateShellRequestUrlsMaintenanceProvider( } public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => - Task.FromResult(_shellSettings.IsDefaultShell() && !context.WasLatestExecutionSuccessful()); + Task.FromResult(_options.Value.IsEnabled && + _shellSettings.IsDefaultShell() && + !context.WasLatestExecutionSuccessful()); public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { var allShellSettings = await _shellSettingsManager.LoadSettingsAsync(); foreach (var shellSettings in allShellSettings) { - shellSettings.RequestUrlHost = GetTenantUrl(_options.Value, shellSettings); + shellSettings.RequestUrlHost = TenantUrlHelpers.GetTenantUrl( + _options.Value.DefaultShellRequestUrl, + _options.Value.RequestUrl, + shellSettings); shellSettings.RequestUrlPrefix = TenantUrlHelpers.ReplaceTenantName( _options.Value.RequestUrlPrefix, shellSettings.Name); @@ -42,17 +47,4 @@ public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) context.ReloadShellAfterMaintenanceCompletion = true; } - - private static string GetTenantUrl(UpdateShellRequestUrlMaintenanceOptions options, ShellSettings shellSettings) - { - var evaluatedRequestUrl = !string.IsNullOrEmpty(options.RequestUrl) - ? TenantUrlHelpers.ReplaceTenantName(options.RequestUrl, shellSettings.Name) - : string.Empty; - var defaultShellRequestUrl = - string.IsNullOrEmpty(options.DefaultShellRequestUrl) - ? evaluatedRequestUrl - : options.DefaultShellRequestUrl; - - return shellSettings.IsDefaultShell() ? defaultShellRequestUrl : evaluatedRequestUrl; - } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs index 98b403c5..21104e88 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceOptions.cs @@ -2,5 +2,7 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Maintenance.UpdateSiteUrl; public class UpdateSiteUrlMaintenanceOptions { + public bool IsEnabled { get; set; } + public string DefaultTenantSiteUrl { get; set; } public string SiteUrl { get; set; } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs index d7fa1478..a732507c 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs +++ b/Lombiq.Hosting.Tenants.Maintenance/Maintenance/UpdateSiteUrl/UpdateSiteUrlMaintenanceProvider.cs @@ -1,3 +1,4 @@ +using Lombiq.Hosting.Tenants.Maintenance.Extensions; using Lombiq.Hosting.Tenants.Maintenance.Helpers; using Lombiq.Hosting.Tenants.Maintenance.Models; using Lombiq.Hosting.Tenants.Maintenance.Services; @@ -24,10 +25,17 @@ public UpdateSiteUrlMaintenanceProvider( _options = options; } + public override Task ShouldExecuteAsync(MaintenanceTaskExecutionContext context) => + Task.FromResult(_options.Value.IsEnabled && !context.WasLatestExecutionSuccessful()); + public override async Task ExecuteAsync(MaintenanceTaskExecutionContext context) { var siteSettings = await _siteService.LoadSiteSettingsAsync(); - siteSettings.BaseUrl = TenantUrlHelpers.ReplaceTenantName(_options.Value.SiteUrl, _shellSettings.Name); + siteSettings.BaseUrl = TenantUrlHelpers.GetTenantUrl( + _options.Value.DefaultTenantSiteUrl, + _options.Value.SiteUrl, + _shellSettings); + await _siteService.UpdateSiteSettingsAsync(siteSettings); } } diff --git a/Lombiq.Hosting.Tenants.Maintenance/Readme.md b/Lombiq.Hosting.Tenants.Maintenance/Readme.md index d4f7c2c0..1c4decf0 100644 --- a/Lombiq.Hosting.Tenants.Maintenance/Readme.md +++ b/Lombiq.Hosting.Tenants.Maintenance/Readme.md @@ -22,25 +22,6 @@ public void ConfigureServices(IServiceCollection services) => To add new maintenance tasks, you need to implement the `IMaintenanceProvider` interface and register it as a service. -### `Lombiq.Hosting.Tenants.Maintenance.TenantUrlMaintenanceCore` - -Provides the core functionality for updating the tenants' URL based on the app configuration. It's a dependency of the `UpdateSiteUrl` and `UpdateShellRequestUrls` maintenance tasks and the configuration options affect them as well. The following configuration options are available to set the tenant URLs: - -```json -{ - "OrchardCore": { - "Lombiq_Hosting_Tenants_Maintenance": { - "TenantUrlMaintenance": { - "DefaultTenantUrl": "domain.com", - "TenantUrl": "{TenantName}.domain.com" - } - } - } -} -``` - -**NOTE**: The `{TenantName}` placeholder will be replaced with the actual tenant name automatically. - ### `Lombiq.Hosting.Tenants.Maintenance.UpdateSiteUrl` It's a maintenance task that updates the site's base URL in the site settings based on the app configuration. It is available on any tenant. @@ -60,7 +41,9 @@ The following configuration options are available to set the site URL: "OrchardCore": { "Lombiq_Hosting_Tenants_Maintenance": { "UpdateSiteUrl": { - "SiteUrl": "https://domain.com/{TenantName}" + "IsEnabled": true, + "SiteUrl": "https://domain.com/{TenantName}", + "DefaultTenantSiteUrl": "https://domain.com" } } } @@ -80,7 +63,8 @@ The following configuration options are available to set the shell request URLs: "OrchardCore": { "Lombiq_Hosting_Tenants_Maintenance": { "UpdateShellRequestUrl": { - "DefaultRequestUrl": "domain.com", + "IsEnabled": true, + "DefaultShellRequestUrl": "domain.com", "RequestUrl": "{TenantName}.domain.com", "RequestUrlPrefix": "{TenantName}" } From 920fa736944b675490d512019e2b4017f472d209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 16 May 2023 19:25:44 +0200 Subject: [PATCH 27/30] Adding empty UI test project --- .../Extensions/ConfigurationExtensions.cs | 33 ++++++++++++++++++ .../TestCaseUITestContextExtensions.cs | 9 +++++ .../License.md | 13 +++++++ ...osting.Tenants.Maintenance.Tests.UI.csproj | 33 ++++++++++++++++++ .../NuGetIcon.png | Bin 0 -> 4657 bytes .../Readme.md | 7 ++++ 6 files changed, 95 insertions(+) create mode 100644 Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs create mode 100644 Lombiq.Hosting.Tenants.Maintenance.Tests.UI/License.md create mode 100644 Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Lombiq.Hosting.Tenants.Maintenance.Tests.UI.csproj create mode 100644 Lombiq.Hosting.Tenants.Maintenance.Tests.UI/NuGetIcon.png create mode 100644 Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Readme.md diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs new file mode 100644 index 00000000..aa79d048 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs @@ -0,0 +1,33 @@ +using Lombiq.Tests.UI.Extensions; +using Lombiq.Tests.UI.Services; +using Shouldly; +using System; +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.Maintenance.Tests.UI.Extensions; + +public static class ConfigurationExtensions +{ + public static void SetUpdateSiteUrlConfiguration( + this OrchardCoreUITestExecutorConfiguration configuration) => configuration.OrchardCoreConfiguration.BeforeAppStart += + (_, argumentsBuilder) => + { + argumentsBuilder + .AddWithValue( + "OrchardCore:Lombiq_Hosting_Tenants_Maintenance:UpdateSiteUrl:IsEnabled", + value: true) + .AddWithValue( + "OrchardCore:Lombiq_Hosting_Tenants_Maintenance:UpdateSiteUrl:DefaultTenantSiteUrl", + value: "https://test.com"); + + argumentsBuilder + .AddWithValue("Logging:LogLevel:Default", "Information"); + + return Task.CompletedTask; + }; + + public static readonly Func AssertAppLogsWithIdleCheckAsync = + async webApplicationInstance => + (await webApplicationInstance.GetLogOutputAsync()) + .ShouldContain("Shutting down tenant \"Default\" because of idle timeout."); +} diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs new file mode 100644 index 00000000..febcf72b --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs @@ -0,0 +1,9 @@ +using Lombiq.Tests.UI.Services; +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.Maintenance.Tests.UI.Extensions; + +public static class TestCaseUITestContextExtensions +{ + public static Task TestMaintenanceExecution(this UITestContext context) => Task.CompletedTask; +} diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/License.md b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/License.md new file mode 100644 index 00000000..d57e1305 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/License.md @@ -0,0 +1,13 @@ +Copyright © 2021, [Lombiq Technologies Ltd.](https://lombiq.com) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Lombiq.Hosting.Tenants.Maintenance.Tests.UI.csproj b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Lombiq.Hosting.Tenants.Maintenance.Tests.UI.csproj new file mode 100644 index 00000000..ab640ee5 --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Lombiq.Hosting.Tenants.Maintenance.Tests.UI.csproj @@ -0,0 +1,33 @@ + + + + net6.0 + + + + Lombiq Hosting - Tenants Maintenance for Orchard Core - UI Test Extensions + Lombiq Technologies + Copyright © 2021, Lombiq Technologies Ltd. + Lombiq Hosting - Tenants Maintenance for Orchard Core - UI Test Extensions: Extension methods that test tenants maintenance for Orchard Core. + NuGetIcon.png + OrchardCore;Lombiq;AspNetCore;Multitenancy;SaaS + https://github.com/Lombiq/Hosting-Tenants + https://github.com/Lombiq/Hosting-Tenants/blob/dev/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Readme.md + License.md + + + + + + + + + + + + + + + + + diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/NuGetIcon.png b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/NuGetIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..162a00508d8041833604d427216238c1b23b2d47 GIT binary patch literal 4657 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 literal 0 HcmV?d00001 diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Readme.md b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Readme.md new file mode 100644 index 00000000..e17c0a1d --- /dev/null +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Readme.md @@ -0,0 +1,7 @@ +# Lombiq Hosting - Tenants Maintenance for Orchard Core - UI Test Extensions + +## About + +Extension methods that test tenants maintenance for Orchard Core, with the help of [Lombiq UI Testing Toolbox for Orchard Core](https://github.com/Lombiq/UI-Testing-Toolbox). + +Call these from a UI test project to verify the module's basic features; as seen in [Open-Source Orchard Core Extensions](https://github.com/Lombiq/Open-Source-Orchard-Core-Extensions). From fb720043399bf036e87e63f4ca78b7dfcb2fbe9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 16 May 2023 20:14:05 +0200 Subject: [PATCH 28/30] Adding test --- ...Extensions.cs => MaintenanceExtensions.cs} | 20 ++++++++++++++----- .../TestCaseUITestContextExtensions.cs | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) rename Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/{ConfigurationExtensions.cs => MaintenanceExtensions.cs} (51%) diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs similarity index 51% rename from Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs rename to Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs index aa79d048..61c6f667 100644 --- a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/ConfigurationExtensions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs @@ -6,9 +6,9 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Tests.UI.Extensions; -public static class ConfigurationExtensions +public static class MaintenanceExtensions { - public static void SetUpdateSiteUrlConfiguration( + public static void SetUpdateSiteUrlMaintenanceConfiguration( this OrchardCoreUITestExecutorConfiguration configuration) => configuration.OrchardCoreConfiguration.BeforeAppStart += (_, argumentsBuilder) => { @@ -21,13 +21,23 @@ public static void SetUpdateSiteUrlConfiguration( value: "https://test.com"); argumentsBuilder - .AddWithValue("Logging:LogLevel:Default", "Information"); + .AddWithValue("Logging:LogLevel:Default", "Debug"); return Task.CompletedTask; }; - public static readonly Func AssertAppLogsWithIdleCheckAsync = + public static readonly Func AssertAppLogsWithMaintenanceExecutionStartAsync = async webApplicationInstance => (await webApplicationInstance.GetLogOutputAsync()) - .ShouldContain("Shutting down tenant \"Default\" because of idle timeout."); + .ShouldContain("Executing maintenance tasks on shell 'Default'."); + + public static readonly Func AssertAppLogsWithSuccessfulUpdateSiteUrlExecutionAsync = + async webApplicationInstance => + (await webApplicationInstance.GetLogOutputAsync()) + .ShouldContain("Maintenance task UpdateSiteUrlMaintenanceProvider executed successfully."); + + public static readonly Func AssertAppLogsWithSkippedUpdateShellRequestUrlExecutionAsync = + async webApplicationInstance => + (await webApplicationInstance.GetLogOutputAsync()) + .ShouldContain("Maintenance task UpdateShellRequestUrlsMaintenanceProvider is not needed."); } diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs index febcf72b..a8c52c8e 100644 --- a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs @@ -5,5 +5,5 @@ namespace Lombiq.Hosting.Tenants.Maintenance.Tests.UI.Extensions; public static class TestCaseUITestContextExtensions { - public static Task TestMaintenanceExecution(this UITestContext context) => Task.CompletedTask; + public static Task TestMaintenanceExecutionAsync(this UITestContext context) => Task.CompletedTask; } From 3721107fc830af1f69298fc51dca7ab460aea1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 16 May 2023 22:11:39 +0200 Subject: [PATCH 29/30] Fixing test --- .../Extensions/MaintenanceExtensions.cs | 21 ------------------- .../TestCaseUITestContextExtensions.cs | 11 +++++++++- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs index 61c6f667..0fb468c7 100644 --- a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/MaintenanceExtensions.cs @@ -1,7 +1,4 @@ -using Lombiq.Tests.UI.Extensions; using Lombiq.Tests.UI.Services; -using Shouldly; -using System; using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Tests.UI.Extensions; @@ -20,24 +17,6 @@ public static void SetUpdateSiteUrlMaintenanceConfiguration( "OrchardCore:Lombiq_Hosting_Tenants_Maintenance:UpdateSiteUrl:DefaultTenantSiteUrl", value: "https://test.com"); - argumentsBuilder - .AddWithValue("Logging:LogLevel:Default", "Debug"); - return Task.CompletedTask; }; - - public static readonly Func AssertAppLogsWithMaintenanceExecutionStartAsync = - async webApplicationInstance => - (await webApplicationInstance.GetLogOutputAsync()) - .ShouldContain("Executing maintenance tasks on shell 'Default'."); - - public static readonly Func AssertAppLogsWithSuccessfulUpdateSiteUrlExecutionAsync = - async webApplicationInstance => - (await webApplicationInstance.GetLogOutputAsync()) - .ShouldContain("Maintenance task UpdateSiteUrlMaintenanceProvider executed successfully."); - - public static readonly Func AssertAppLogsWithSkippedUpdateShellRequestUrlExecutionAsync = - async webApplicationInstance => - (await webApplicationInstance.GetLogOutputAsync()) - .ShouldContain("Maintenance task UpdateShellRequestUrlsMaintenanceProvider is not needed."); } diff --git a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs index a8c52c8e..d7191e42 100644 --- a/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs +++ b/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs @@ -1,9 +1,18 @@ +using Atata; +using Lombiq.Tests.UI.Extensions; using Lombiq.Tests.UI.Services; +using OpenQA.Selenium; +using Shouldly; using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.Maintenance.Tests.UI.Extensions; public static class TestCaseUITestContextExtensions { - public static Task TestMaintenanceExecutionAsync(this UITestContext context) => Task.CompletedTask; + public static async Task TestSiteUrlMaintenanceExecution(this UITestContext context) + { + await context.SignInDirectlyAsync(); + await context.GoToAdminRelativeUrlAsync("/Settings/general"); + context.Get(By.Name("ISite.BaseUrl")).GetValue().ShouldBe("https://test.com"); + } } From 106f338e1bb2ab80cdeb12bf9bedd00f1084c11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartha?= Date: Tue, 16 May 2023 22:17:26 +0200 Subject: [PATCH 30/30] Adding the UI test project to the solution --- Lombiq.Hosting.Tenants.sln | 6 ++++++ Readme.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Lombiq.Hosting.Tenants.sln b/Lombiq.Hosting.Tenants.sln index 633eae70..e7fcaca9 100644 --- a/Lombiq.Hosting.Tenants.sln +++ b/Lombiq.Hosting.Tenants.sln @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lombiq.Hosting.Tenants.Medi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lombiq.Hosting.Tenants.Maintenance", "Lombiq.Hosting.Tenants.Maintenance\Lombiq.Hosting.Tenants.Maintenance.csproj", "{6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lombiq.Hosting.Tenants.Maintenance.Tests.UI", "Lombiq.Hosting.Tenants.Maintenance.Tests.UI\Lombiq.Hosting.Tenants.Maintenance.Tests.UI.csproj", "{5E1E19E5-18EB-49A1-B392-3BD74418D9FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -63,6 +65,10 @@ Global {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D3217B9-EA0F-4608-BA9B-B9B935AEDF3A}.Release|Any CPU.Build.0 = Release|Any CPU + {5E1E19E5-18EB-49A1-B392-3BD74418D9FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E1E19E5-18EB-49A1-B392-3BD74418D9FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E1E19E5-18EB-49A1-B392-3BD74418D9FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E1E19E5-18EB-49A1-B392-3BD74418D9FC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Readme.md b/Readme.md index 68115e7d..6d3b5c4d 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ # Lombiq Hosting - Tenants for Orchard Core -[![Lombiq.Hosting.Tenants.Admin.Login NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Admin.Login?label=Lombiq.Hosting.Tenants.Admin.Login)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Admin.Login/) [![Lombiq.Hosting.Tenants.FeaturesGuard NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard?label=Lombiq.Hosting.Tenants.FeaturesGuard)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard/) [![Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI?label=Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI/) [![Lombiq.Hosting.Tenants.IdleTenantManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement?label=Lombiq.Hosting.Tenants.IdleTenantManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement/) [![Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI?label=Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI/) [![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) [![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) [![Lombiq.Hosting.Tenants.MediaStorageManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement?label=Lombiq.Hosting.Tenants.MediaStorageManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement/) [![Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI?label=Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI/) +[![Lombiq.Hosting.Tenants.Admin.Login NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Admin.Login?label=Lombiq.Hosting.Tenants.Admin.Login)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Admin.Login/) [![Lombiq.Hosting.Tenants.FeaturesGuard NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard?label=Lombiq.Hosting.Tenants.FeaturesGuard)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard/) [![Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI?label=Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.FeaturesGuard.Tests.UI/) [![Lombiq.Hosting.Tenants.IdleTenantManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement?label=Lombiq.Hosting.Tenants.IdleTenantManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement/) [![Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI?label=Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.IdleTenantManagement.Tests.UI/) [![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) [![Lombiq.Hosting.Tenants.Maintenance NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Maintenance?label=Lombiq.Hosting.Tenants.Maintenance)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Maintenance/) [![Lombiq.Hosting.Tenants.Maintenance.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Maintenance.Tests.UI?label=Lombiq.Hosting.Tenants.Maintenance.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Maintenance.Tests.UI/) [![Lombiq.Hosting.Tenants.MediaStorageManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement?label=Lombiq.Hosting.Tenants.MediaStorageManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement/) [![Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI?label=Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement.Tests.UI/) ## About