From 4b4b003fc6927caf4fafe4bdf4e21960c62e9de7 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 28 Feb 2024 12:41:32 +0100 Subject: [PATCH] Add filter option to the "dictionar overview" endpoint (#15777) --- .../Dictionary/AllDictionaryController.cs | 4 +-- src/Umbraco.Cms.Api.Management/OpenApi.json | 7 +++++ .../Repositories/IDictionaryRepository.cs | 2 +- .../Services/DictionaryItemService.cs | 4 +-- .../Services/IDictionaryItemService.cs | 3 +- .../Implement/DictionaryRepository.cs | 30 ++++++++++++++----- 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs index a5122b7506..f3384b6f0a 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs @@ -25,10 +25,10 @@ public class AllDictionaryController : DictionaryControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> All(int skip = 0, int take = 100) + public async Task>> All(string? filter = null, int skip = 0, int take = 100) { // unfortunately we can't paginate here...we'll have to get all and paginate in memory - IDictionaryItem[] items = (await _dictionaryItemService.GetDescendantsAsync(Constants.System.RootKey)).ToArray(); + IDictionaryItem[] items = (await _dictionaryItemService.GetDescendantsAsync(Constants.System.RootKey, filter)).ToArray(); var model = new PagedViewModel { Total = items.Length, diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 35d05f79fc..087081a093 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -1687,6 +1687,13 @@ ], "operationId": "GetDictionary", "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "skip", "in": "query", diff --git a/src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs index 9d75bc1030..dd9383b3e5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs @@ -12,7 +12,7 @@ public interface IDictionaryRepository : IReadWriteQueryRepository GetDictionaryItemDescendants(Guid? parentId); + IEnumerable GetDictionaryItemDescendants(Guid? parentId, string? filter = null); Dictionary GetDictionaryItemKeyMap(); } diff --git a/src/Umbraco.Core/Services/DictionaryItemService.cs b/src/Umbraco.Core/Services/DictionaryItemService.cs index a64fd26f89..38e37afedb 100644 --- a/src/Umbraco.Core/Services/DictionaryItemService.cs +++ b/src/Umbraco.Core/Services/DictionaryItemService.cs @@ -106,11 +106,11 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem => await CountByQueryAsync(Query().Where(x => x.ParentId == parentId)); /// - public async Task> GetDescendantsAsync(Guid? parentId) + public async Task> GetDescendantsAsync(Guid? parentId, string? filter = null) { using (ScopeProvider.CreateCoreScope(autoComplete: true)) { - IDictionaryItem[] items = _dictionaryRepository.GetDictionaryItemDescendants(parentId).ToArray(); + IDictionaryItem[] items = _dictionaryRepository.GetDictionaryItemDescendants(parentId, filter).ToArray(); return await Task.FromResult(items); } } diff --git a/src/Umbraco.Core/Services/IDictionaryItemService.cs b/src/Umbraco.Core/Services/IDictionaryItemService.cs index 10855b0f25..89d06ade1d 100644 --- a/src/Umbraco.Core/Services/IDictionaryItemService.cs +++ b/src/Umbraco.Core/Services/IDictionaryItemService.cs @@ -53,8 +53,9 @@ public interface IDictionaryItemService /// Gets a list of descendants for a /// /// Id of the parent, null will return all dictionary items + /// An optional filter, which will limit the results to only those dictionary items whose key starts with the filter value. /// An enumerable list of objects - Task> GetDescendantsAsync(Guid? parentId); + Task> GetDescendantsAsync(Guid? parentId, string? filter = null); /// /// Gets the root/top objects diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs index 13f621b14b..a93ae543a0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs @@ -65,7 +65,7 @@ internal class DictionaryRepository : EntityRepositoryBase return Database.Fetch(sql).ToDictionary(x => x.Key, x => x.Id); } - public IEnumerable GetDictionaryItemDescendants(Guid? parentId) + public IEnumerable GetDictionaryItemDescendants(Guid? parentId, string? filter = null) { IDictionary languageIsoCodeById = GetLanguagesById(); @@ -76,12 +76,15 @@ internal class DictionaryRepository : EntityRepositoryBase return guids.InGroupsOf(Constants.Sql.MaxParameterCount) .Select(group => { - Sql sqlClause = GetBaseQuery(false) + Sql sql = GetBaseQuery(false) .Where(x => x.Parent != null) .WhereIn(x => x.Parent, group); - var translator = new SqlTranslator(sqlClause, Query()); - Sql sql = translator.Translate(); + if (filter.IsNullOrWhiteSpace() is false) + { + sql.Where(x => x.Key.StartsWith(filter)); + } + sql.OrderBy(x => x.UniqueId); return Database @@ -93,14 +96,25 @@ internal class DictionaryRepository : EntityRepositoryBase if (!parentId.HasValue) { Sql sql = GetBaseQuery(false) - .Where(x => x.PrimaryKey > 0) - .OrderBy(x => x.UniqueId); + .Where(x => x.PrimaryKey > 0); + + if (filter.IsNullOrWhiteSpace() is false) + { + sql.Where(x => x.Key.StartsWith(filter)); + } + return Database .FetchOneToMany(x => x.LanguageTextDtos, sql) - .Select(dto => ConvertFromDto(dto, languageIsoCodeById)); + .Select(dto => ConvertFromDto(dto, languageIsoCodeById)) + .OrderBy(DictionaryItemOrdering); } - return getItemsFromParents(new[] { parentId.Value }).SelectRecursive(items => getItemsFromParents(items.Select(x => x.Key).ToArray())).SelectMany(items => items); + return getItemsFromParents(new[] { parentId.Value }) + .SelectRecursive(items => getItemsFromParents(items.Select(x => x.Key).ToArray())).SelectMany(items => items) + .OrderBy(DictionaryItemOrdering); + + // we're loading all descendants into memory, sometimes recursively... so we have to order them in memory too + string DictionaryItemOrdering(IDictionaryItem item) => item.ItemKey; } protected override IRepositoryCachePolicy CreateCachePolicy()