diff --git a/src/Umbraco.Cms.Search.Core/Configuration/IndexOptions.cs b/src/Umbraco.Cms.Search.Core/Configuration/IndexOptions.cs index ec4f743..ca59ed0 100644 --- a/src/Umbraco.Cms.Search.Core/Configuration/IndexOptions.cs +++ b/src/Umbraco.Cms.Search.Core/Configuration/IndexOptions.cs @@ -9,6 +9,8 @@ public sealed class IndexOptions { private readonly Dictionary _register = []; + public required string[] IncludeRebuildWhenLanguageDeleted { get; set; } = [Constants.IndexAliases.DraftContent, Constants.IndexAliases.PublishedContent]; + public void RegisterIndex(string indexAlias, params UmbracoObjectTypes[] containedObjectTypes) where TIndexer : class, IIndexer where TSearcher : class, ISearcher diff --git a/src/Umbraco.Cms.Search.Core/NotificationHandlers/RebuildIndexesNotificationHandler.cs b/src/Umbraco.Cms.Search.Core/NotificationHandlers/RebuildIndexesNotificationHandler.cs index 7066b6c..90ea095 100644 --- a/src/Umbraco.Cms.Search.Core/NotificationHandlers/RebuildIndexesNotificationHandler.cs +++ b/src/Umbraco.Cms.Search.Core/NotificationHandlers/RebuildIndexesNotificationHandler.cs @@ -1,21 +1,26 @@ using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Search.Core.Configuration; using Umbraco.Cms.Search.Core.Services.ContentIndexing; namespace Umbraco.Cms.Search.Core.NotificationHandlers; -internal sealed class RebuildIndexesNotificationHandler : INotificationHandler +internal sealed class RebuildIndexesNotificationHandler : INotificationHandler, INotificationHandler { private readonly IContentIndexingService _contentIndexingService; private readonly ILogger _logger; + private readonly IndexOptions _options; public RebuildIndexesNotificationHandler( IContentIndexingService contentIndexingService, - ILogger logger) + ILogger logger, + IOptions options) { _contentIndexingService = contentIndexingService; _logger = logger; + _options = options.Value; } public void Handle(UmbracoApplicationStartedNotification notification) @@ -26,4 +31,14 @@ public void Handle(UmbracoApplicationStartedNotification notification) _contentIndexingService.Rebuild(Constants.IndexAliases.DraftMedia); _contentIndexingService.Rebuild(Constants.IndexAliases.DraftMembers); } + + public void Handle(LanguageDeletedNotification notification) + { + _logger.LogInformation("Rebuilding search indexes after language deletion..."); + + foreach (var indexAlias in _options.IncludeRebuildWhenLanguageDeleted) + { + _contentIndexingService.Rebuild(indexAlias); + } + } } diff --git a/src/Umbraco.Cms.Search.Core/Umbraco.Cms.Search.Core.csproj b/src/Umbraco.Cms.Search.Core/Umbraco.Cms.Search.Core.csproj index c5de816..98df4c3 100644 --- a/src/Umbraco.Cms.Search.Core/Umbraco.Cms.Search.Core.csproj +++ b/src/Umbraco.Cms.Search.Core/Umbraco.Cms.Search.Core.csproj @@ -23,6 +23,9 @@ <_Parameter1>Umbraco.Test.Search.Unit + + <_Parameter1>Umbraco.Test.Search.Examine.Integration + <_Parameter1>DynamicProxyGenAssembly2 diff --git a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantContentTreeTests.cs b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantContentTreeTests.cs index 0d23395..a9e8c68 100644 --- a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantContentTreeTests.cs +++ b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantContentTreeTests.cs @@ -228,8 +228,8 @@ public async Task CreateVariantDocumentTree() .WithCultureInfo("ja-JP") .Build(); - LocalizationService.Save(langDk); - LocalizationService.Save(langJp); + await LanguageService.CreateAsync(langDk, Constants.Security.SuperUserKey); + await LanguageService.CreateAsync(langJp, Constants.Security.SuperUserKey); IContentType contentType = new ContentTypeBuilder() .WithAlias("variant") diff --git a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantDocumentTests.cs b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantDocumentTests.cs index a4b977a..ff5f43a 100644 --- a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantDocumentTests.cs +++ b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/IndexService/VariantDocumentTests.cs @@ -177,8 +177,8 @@ private async Task CreateVariantDocument() .WithCultureInfo("ja-JP") .Build(); - LocalizationService.Save(langDk); - LocalizationService.Save(langJp); + await LanguageService.CreateAsync(langDk, Cms.Core.Constants.Security.SuperUserKey); + await LanguageService.CreateAsync(langJp, Cms.Core.Constants.Security.SuperUserKey); IContentType contentType = new ContentTypeBuilder() .WithAlias("variant") diff --git a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantDocumentTests.cs b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantDocumentTests.cs index 91297a1..0402687 100644 --- a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantDocumentTests.cs +++ b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantDocumentTests.cs @@ -9,7 +9,6 @@ namespace Umbraco.Test.Search.Examine.Integration.Tests.ContentTests.SearchServi public class VariantDocumentTests : SearcherTestBase { - [TestCase(true, "en-US", "Name")] [TestCase(false, "en-US", "Name")] [TestCase(true, "da-DK", "Navn")] @@ -83,20 +82,34 @@ public async Task CanSearchVariantTextBySegment(bool publish, string culture, st Assert.That(results.Documents.First().Id, Is.EqualTo(RootKey)); } + [TestCase(true, "da-DK", "Roden")] + [TestCase(true, "ja-JP", "ル-ト")] + public async Task CannotSearchNonExistingNameAfterLanguageDelete(bool publish, string culture, string name) + { + var indexAlias = GetIndexAlias(publish); + + await LanguageService.DeleteAsync(culture, Constants.Security.SuperUserKey); + + // We can't wait for indexing here, as it's an entire rebuild, not just a single action. + await Task.Delay(4000); + + SearchResult results = await Searcher.SearchAsync(indexAlias, name, null, null, null, culture, null, null, 0, 100); + Assert.That(results.Total, Is.EqualTo(0)); + } + [SetUp] public async Task CreateVariantDocument() { ILanguage langDk = new LanguageBuilder() .WithCultureInfo("da-DK") - .WithIsDefault(true) .Build(); ILanguage langJp = new LanguageBuilder() .WithCultureInfo("ja-JP") .Build(); - LocalizationService.Save(langDk); - LocalizationService.Save(langJp); + await LanguageService.CreateAsync(langDk, Constants.Security.SuperUserKey); + await LanguageService.CreateAsync(langJp, Constants.Security.SuperUserKey); IContentType contentType = new ContentTypeBuilder() .WithAlias("variant") diff --git a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantFilterTest.cs b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantFilterTest.cs index 502fdc7..c5f9d48 100644 --- a/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantFilterTest.cs +++ b/src/Umbraco.Test.Search.Examine.Integration/Tests/ContentTests/SearchService/VariantFilterTest.cs @@ -100,8 +100,8 @@ public async Task CreateVariantDocument() .WithCultureInfo("ja-JP") .Build(); - LocalizationService.Save(langDk); - LocalizationService.Save(langJp); + await LanguageService.CreateAsync(langDk, Constants.Security.SuperUserKey); + await LanguageService.CreateAsync(langJp, Constants.Security.SuperUserKey); IContentType contentType = new ContentTypeBuilder() .WithAlias("variant") diff --git a/src/Umbraco.Test.Search.Examine.Integration/Tests/TestBase.cs b/src/Umbraco.Test.Search.Examine.Integration/Tests/TestBase.cs index d6f2d8d..57d5890 100644 --- a/src/Umbraco.Test.Search.Examine.Integration/Tests/TestBase.cs +++ b/src/Umbraco.Test.Search.Examine.Integration/Tests/TestBase.cs @@ -5,9 +5,11 @@ using NUnit.Framework; using Umbraco.Cms.Core.HostedServices; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Search.Core.DependencyInjection; +using Umbraco.Cms.Search.Core.NotificationHandlers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; using Umbraco.Test.Search.Examine.Integration.Attributes; @@ -37,7 +39,8 @@ public abstract class TestBase : UmbracoIntegrationTest protected IDataTypeService DataTypeService => GetRequiredService(); - protected ILocalizationService LocalizationService => GetRequiredService(); + protected ILanguageService LanguageService => GetRequiredService(); + protected void SaveAndPublish(IContent content) { @@ -55,6 +58,7 @@ protected override void CustomTestSetup(IUmbracoBuilder builder) builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.AddNotificationHandler(); // the core ConfigureBuilderAttribute won't execute from other assemblies at the moment, so this is a workaround var testType = Type.GetType(TestContext.CurrentContext.Test.ClassName!);