From 77017f25b310369cf867cffac9c087412ead8f82 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Jun 2021 10:58:17 -0600 Subject: [PATCH] Adds KeyValueService Find and tests --- .../Extensions/TypeLoaderExtensions.cs | 18 +++++++------- .../Packaging/PackageMigrationPlan.cs | 6 ++++- .../Repositories/IKeyValueRepository.cs | 9 ++++++- src/Umbraco.Core/Services/IKeyValueService.cs | 12 +++++++++- .../Persistence/Mappers/AccessMapper.cs | 3 ++- .../Persistence/Mappers/KeyValueMapper.cs | 22 +++++++++++++++++ .../Mappers/MapperCollectionBuilder.cs | 1 + .../Implement/KeyValueRepository.cs | 21 +++++++++------- .../Services/Implement/KeyValueService.cs | 12 +++++++++- .../Services/KeyValueServiceTests.cs | 24 ++++++++++++++++++- 10 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs diff --git a/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs b/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs index e6e67f6153..b2d9618f30 100644 --- a/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs +++ b/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.PropertyEditors; namespace Umbraco.Extensions @@ -14,17 +15,18 @@ namespace Umbraco.Extensions /// /// Gets all classes implementing . /// - public static IEnumerable GetDataEditors(this TypeLoader mgr) - { - return mgr.GetTypes(); - } + public static IEnumerable GetDataEditors(this TypeLoader mgr) => mgr.GetTypes(); /// /// Gets all classes implementing ICacheRefresher. /// - public static IEnumerable GetCacheRefreshers(this TypeLoader mgr) - { - return mgr.GetTypes(); - } + public static IEnumerable GetCacheRefreshers(this TypeLoader mgr) => mgr.GetTypes(); + + /// + /// Gest all classes implementing + /// + /// + /// + public static IEnumerable GetPackageMigrationPlans(this TypeLoader mgr) => mgr.GetTypes(); } } diff --git a/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs b/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs index 114aea8f24..3688f5467a 100644 --- a/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs +++ b/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs @@ -1,11 +1,15 @@ using System; using System.Collections.Generic; using System.Text; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Migrations; namespace Umbraco.Cms.Core.Packaging { - public abstract class PackageMigrationPlan : MigrationPlan + /// + /// Base class for package migration plans + /// + public abstract class PackageMigrationPlan : MigrationPlan, IDiscoverable { protected PackageMigrationPlan(string name) : base(name) { diff --git a/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs index e9625892d4..9d5119898c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs @@ -1,8 +1,15 @@ -using Umbraco.Cms.Core.Models; +using System.Collections.Generic; +using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { public interface IKeyValueRepository : IReadRepository, IWriteRepository { + /// + /// Returns key/value pairs for all keys with the specified prefix. + /// + /// + /// + IReadOnlyDictionary Find(string keyPrefix); } } diff --git a/src/Umbraco.Core/Services/IKeyValueService.cs b/src/Umbraco.Core/Services/IKeyValueService.cs index 4c24ca19de..456733094c 100644 --- a/src/Umbraco.Core/Services/IKeyValueService.cs +++ b/src/Umbraco.Core/Services/IKeyValueService.cs @@ -1,4 +1,7 @@ -namespace Umbraco.Cms.Core.Services +using System.Collections; +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.Services { /// /// Manages the simplified key/value store. @@ -11,6 +14,13 @@ /// Returns null if no value was found for the key. string GetValue(string key); + /// + /// Returns key/value pairs for all keys with the specified prefix. + /// + /// + /// + IReadOnlyDictionary Find(string keyPrefix); + /// /// Sets a value. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs index ddfcae09f1..8d88b2d7df 100644 --- a/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs +++ b/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs @@ -1,9 +1,10 @@ -using System; +using System; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Persistence.Mappers { + [MapperFor(typeof(PublicAccessEntry))] public sealed class AccessMapper : BaseMapper { diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs new file mode 100644 index 0000000000..a133d4066c --- /dev/null +++ b/src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs @@ -0,0 +1,22 @@ +using System; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Infrastructure.Persistence.Dtos; + +namespace Umbraco.Cms.Infrastructure.Persistence.Mappers +{ + [MapperFor(typeof(KeyValue))] + [MapperFor(typeof(IKeyValue))] + public sealed class KeyValueMapper : BaseMapper + { + public KeyValueMapper(Lazy sqlContext, MapperConfigurationStore maps) + : base(sqlContext, maps) + { } + + protected override void DefineMaps() + { + DefineMap(nameof(KeyValue.Identifier), nameof(KeyValueDto.Key)); + DefineMap(nameof(KeyValue.Value), nameof(KeyValueDto.Value)); + DefineMap(nameof(KeyValue.UpdateDate), nameof(KeyValueDto.UpdateDate)); + } + } +} diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs index e797319810..8b993365a0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs +++ b/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs @@ -32,6 +32,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Mappers Add(); Add(); Add(); + Add(); Add(); Add(); Add(); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs index 74988d2026..a4ffe7ee9a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement @@ -19,6 +20,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement : base(scopeAccessor, AppCaches.NoCache, logger) { } + /// + public IReadOnlyDictionary Find(string keyPrefix) + => Get(Query().Where(entity => entity.Identifier.StartsWith(keyPrefix))) + .ToDictionary(x => x.Identifier, x => x.Value); + #region Overrides of IReadWriteQueryRepository public override void Save(IKeyValue entity) @@ -47,15 +53,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return sql; } - protected override string GetBaseWhereClause() - { - return Cms.Core.Constants.DatabaseSchema.Tables.KeyValue + ".key = @id"; - } + protected override string GetBaseWhereClause() => Core.Constants.DatabaseSchema.Tables.KeyValue + ".key = @id"; - protected override IEnumerable GetDeleteClauses() - { - return Enumerable.Empty(); - } + protected override IEnumerable GetDeleteClauses() => Enumerable.Empty(); protected override IKeyValue PerformGet(string id) { @@ -73,7 +73,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override IEnumerable PerformGetByQuery(IQuery query) { - throw new NotSupportedException(); + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate(); + return Database.Fetch(sql).Select(Map); } protected override void PersistNewItem(IKeyValue entity) diff --git a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs index bcd35ee7f4..73bdbdc7e8 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; @@ -25,6 +26,15 @@ namespace Umbraco.Cms.Core.Services.Implement } } + /// + public IReadOnlyDictionary Find(string keyPrefix) + { + using (var scope = _scopeProvider.CreateScope(autoComplete: true)) + { + return _repository.Find(keyPrefix); + } + } + /// public void SetValue(string key, string value) { diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs index 3437e1b286..0aa1892390 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs @@ -1,6 +1,7 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. +using System.Collections.Generic; using System.Threading; using NUnit.Framework; using Umbraco.Cms.Core.Services; @@ -19,6 +20,27 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { private IKeyValueService KeyValueService => GetRequiredService(); + [Test] + public void Can_Query_For_Key_Prefix() + { + // Arrange + KeyValueService.SetValue("test1", "hello1"); + KeyValueService.SetValue("test2", "hello2"); + KeyValueService.SetValue("test3", "hello3"); + KeyValueService.SetValue("test4", "hello4"); + + // Act + IReadOnlyDictionary value = KeyValueService.Find("test"); + + // Assert + + Assert.AreEqual(4, value.Count); + Assert.AreEqual("hello1", value["test1"]); + Assert.AreEqual("hello2", value["test2"]); + Assert.AreEqual("hello3", value["test3"]); + Assert.AreEqual("hello4", value["test4"]); + } + [Test] public void GetValue_ForMissingKey_ReturnsNull() {