From ee11ccd45c2d2d757c219ba37cb7690dbafd218f Mon Sep 17 00:00:00 2001 From: pengjuyan <42963214+codding-y@users.noreply.github.com> Date: Tue, 23 Aug 2022 14:52:01 +0800 Subject: [PATCH] refactor: refactor dcc configuration (#215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: refactor dcc configuration * refactor: refactor dcc configuration * chore: adjust dcc test * refactor: refactor dcc configuration * test: adjust dcc configuration test * fix: fix configuration test error * fix: adjust InitializeAppConfiguration function * refactor(ConfigurationApi.Dcc): Refactor Dcc * refactor: Optimize ConfigurationApi.Dcc And Perfect unit tests * refactor(Configuration): Simplified code * chore: Deal with code smells * chore: deal with code smells Co-authored-by: yanpengju Co-authored-by: 阎鹏举 <阎鹏举@DESKTOP-28NV352> Co-authored-by: zhenlei520 --- .../IConfigurationApiClient.cs | 1 + .../Options/MasaAppConfigureOptions.cs | 14 +- .../Internal/Constants.cs | 4 +- .../Internal/DccConfigurationRepository.cs | 33 +- .../DistributedCacheClientExtensions.cs | 29 + .../Internal/Model/StaticConfig.cs | 2 +- .../Internal/Options/DccOptions.cs | 20 - .../MasaConfigurationExtensions.cs | 185 ++-- .../Options/DccConfigurationOptions.cs | 26 +- .../Options/DccExpandSectionOptions.cs | 12 - .../Options/DccOptions.cs | 70 ++ .../Options/DccSectionOptions.cs | 45 +- .../README.md | 20 +- .../README.zh-CN.md | 18 +- .../_Imports.cs | 3 +- .../DccClientTest.cs | 165 ++- .../DccManageTest.cs | 2 - .../DccTest.cs | 978 +++++++----------- .../_Imports.cs | 1 + .../appsettings.json | 16 +- .../expandSections.json | 36 +- .../WebApplicationBuilderExtensions.cs | 45 +- .../Internal/ConfigurationExtensions.cs | 6 - .../MasaAppConfigureOptionsRelation.cs | 31 +- .../WebApplicationBuilderTest.cs | 6 +- .../Models/RedisServerOptions.cs | 5 +- 26 files changed, 931 insertions(+), 842 deletions(-) create mode 100644 src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Extensions/DistributedCacheClientExtensions.cs delete mode 100644 src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Options/DccOptions.cs delete mode 100644 src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccExpandSectionOptions.cs create mode 100644 src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccOptions.cs diff --git a/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs b/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs index 349e61ab2..24a0f4d74 100644 --- a/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs +++ b/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.BuildingBlocks.Configuration; + public interface IConfigurationApiClient { Task<(string Raw, ConfigurationTypes ConfigurationType)> GetRawAsync(string configObject, Action? valueChanged = null); diff --git a/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/Options/MasaAppConfigureOptions.cs b/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/Options/MasaAppConfigureOptions.cs index 1946cd609..f2efa9112 100644 --- a/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/Options/MasaAppConfigureOptions.cs +++ b/src/BuildingBlocks/Configuration/Masa.BuildingBlocks.Configuration/Options/MasaAppConfigureOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.BuildingBlocks.Configuration.Options; @@ -11,12 +11,18 @@ public class MasaAppConfigureOptions public string Cluster { get => GetValue(nameof(Cluster)); set => Data[nameof(Cluster)] = value; } - public Dictionary Data { get; set; } = new(); + private Dictionary Data { get; set; } = new(StringComparer.OrdinalIgnoreCase); - private string GetValue(string key) + public int Length => Data.Count; + + public string GetValue(string key) => GetValue(key, () => string.Empty); + + public string GetValue(string key, Func defaultFunc) { if (Data.ContainsKey(key)) return Data[key]; - return string.Empty; + return defaultFunc.Invoke(); } + + public void TryAdd(string key, string value) => Data.TryAdd(key, value); } diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Constants.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Constants.cs index c5508a2d5..892028c03 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Constants.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Constants.cs @@ -9,7 +9,7 @@ internal static class Constants internal const string DEFAULT_SUBSCRIBE_KEY_PREFIX = "masa.dcc:"; - internal const string DEFAULT_ENVIRONMENT_NAME = "ASPNETCORE_ENVIRONMENT"; - internal const string DATA_DICTIONARY_SECTION_NAME = "DataDictionary"; + + internal const string DEFAULT_PUBLIC_ID = "public-$Config"; } diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/DccConfigurationRepository.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/DccConfigurationRepository.cs index a4dd590ab..31c3c5c56 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/DccConfigurationRepository.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/DccConfigurationRepository.cs @@ -20,6 +20,7 @@ public DccConfigurationRepository( : base(loggerFactory) { _client = client; + foreach (var sectionOption in sectionOptions) { Initialize(sectionOption).ConfigureAwait(false).GetAwaiter().GetResult(); @@ -30,8 +31,8 @@ private async Task Initialize(DccSectionOptions sectionOption) { foreach (var configObject in sectionOption.ConfigObjects) { - string key = $"{sectionOption.Environment!}-{sectionOption.Cluster!}-{sectionOption.AppId}-{configObject}".ToLower(); - var result = await _client.GetRawAsync(sectionOption.Environment!, sectionOption.Cluster!, sectionOption.AppId, configObject, (val) => + string key = $"{sectionOption.Environment}-{sectionOption.Cluster}-{sectionOption.AppId}-{configObject}".ToLower(); + var result = await _client.GetRawAsync(sectionOption.Environment, sectionOption.Cluster, sectionOption.AppId, configObject, (val) => { if (_configObjectConfigurationTypeRelations.TryGetValue(key, out var configurationType)) { @@ -45,28 +46,26 @@ private async Task Initialize(DccSectionOptions sectionOption) } } - private IDictionary FormatRaw(string appId, string configObject, string? raw, ConfigurationTypes configurationType) + private static IDictionary FormatRaw(string appId, string configObject, string? raw, ConfigurationTypes configurationType) { if (raw == null) return new Dictionary(); - switch (configurationType) + return configurationType switch { - case ConfigurationTypes.Json: - return SecondaryFormat(appId, configObject, JsonConfigurationParser.Parse(raw)); - case ConfigurationTypes.Properties: - return SecondaryFormat(appId, configObject, JsonSerializer.Deserialize>(raw)!); - case ConfigurationTypes.Text: - return new Dictionary() - { - { $"{appId}{ConfigurationPath.KeyDelimiter}{configObject}" , raw ?? "" } - }; - default: - throw new NotSupportedException(nameof(configurationType)); - } + ConfigurationTypes.Json => SecondaryFormat(appId, configObject, JsonConfigurationParser.Parse(raw)), + ConfigurationTypes.Properties => SecondaryFormat(appId, configObject, JsonSerializer.Deserialize>(raw)!), + ConfigurationTypes.Text => new Dictionary + { + { $"{appId}{ConfigurationPath.KeyDelimiter}{configObject}" , raw } + }, + ConfigurationTypes.Xml => SecondaryFormat(appId, configObject, JsonConfigurationParser.Parse(raw)), + ConfigurationTypes.Yaml => SecondaryFormat(appId, configObject, JsonConfigurationParser.Parse(raw)), + _ => throw new NotSupportedException(nameof(configurationType)), + }; } - private IDictionary SecondaryFormat( + private static IDictionary SecondaryFormat( string appId, string configObject, IDictionary data) diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Extensions/DistributedCacheClientExtensions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Extensions/DistributedCacheClientExtensions.cs new file mode 100644 index 000000000..15acf3e6f --- /dev/null +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Extensions/DistributedCacheClientExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Utils.Caching.Core.Interfaces; + +internal static class DistributedCacheClientExtensions +{ + public static List GetAllConfigObjects( + this IDistributedCacheClient distributedCacheClient, + string appId, + string environment, + string cluster) + { + var defaultConfigObjects = new List(); + + string partialKey = + $"{environment}-{cluster}-{appId}".ToLower(); + List keys = distributedCacheClient.GetKeys($"{partialKey}*"); + foreach (var key in keys) + { + var configObject = key.Split($"{partialKey}-", StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); + if (configObject == null) continue; + + defaultConfigObjects.Add(configObject); + } + + return defaultConfigObjects; + } +} diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Model/StaticConfig.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Model/StaticConfig.cs index ffb4cecff..a4ec62030 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Model/StaticConfig.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Model/StaticConfig.cs @@ -7,5 +7,5 @@ internal static class StaticConfig { public static string AppId { get; set; } - public static string PublicId => "public-$Config"; + public static string PublicId { get; set; } } diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Options/DccOptions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Options/DccOptions.cs deleted file mode 100644 index 1034a3621..000000000 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Internal/Options/DccOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) MASA Stack All rights reserved. -// Licensed under the MIT License. See LICENSE.txt in the project root for license information. - -namespace Masa.Contrib.Configuration.ConfigurationApi.Dcc.Internal.Options; - -internal class DccOptions -{ - public DccConfigurationOptions DccConfigurationOptions { get; } - - public DccSectionOptions DefaultSectionOptions { get; } - - public List ExpansionSectionOptions { get; } - - public DccOptions(DccConfigurationOptions dccConfigurationOptions, DccSectionOptions defaultSectionOptions, List expansionSectionOptions) - { - DccConfigurationOptions = dccConfigurationOptions; - DefaultSectionOptions = defaultSectionOptions; - ExpansionSectionOptions = expansionSectionOptions; - } -} diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/MasaConfigurationExtensions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/MasaConfigurationExtensions.cs index a059072fb..64be4dad7 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/MasaConfigurationExtensions.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/MasaConfigurationExtensions.cs @@ -8,69 +8,32 @@ public static class MasaConfigurationExtensions public static IMasaConfigurationBuilder UseDcc( this IMasaConfigurationBuilder builder, Action? jsonSerializerOptions = null, - Action? callerOptions = null) + Action? callerOptions = null, + string sectionName = "DccOptions") { - var configurationSection = builder.Configuration.GetSection("DccOptions"); - var dccOptions = configurationSection.Get(); - - List expandSections = new(); - var configurationExpandSection = builder.Configuration.GetSection("ExpandSections"); - if (configurationExpandSection.Exists()) - { - configurationExpandSection.Bind(expandSections); - } - - return builder.UseDcc(() => dccOptions, option => - { - option.Environment = builder.Configuration[nameof(DccSectionOptions.Environment)]; - option.Cluster = builder.Configuration[nameof(DccSectionOptions.Cluster)]; - option.AppId = builder.Configuration[nameof(DccSectionOptions.AppId)]; - option.ConfigObjects = builder.Configuration.GetSection(nameof(DccSectionOptions.ConfigObjects)).Get>(); - option.Secret = builder.Configuration[nameof(DccSectionOptions.Secret)]; - }, option => option.ExpandSections = expandSections, jsonSerializerOptions, callerOptions); - } - - public static IMasaConfigurationBuilder UseDcc( - this IMasaConfigurationBuilder builder, - Func configureOptions, - Action defaultSectionOptions, - Action? expansionSectionOptions, - Action? jsonSerializerOptions = null, - Action? callerOptions = null) - { - ArgumentNullException.ThrowIfNull(configureOptions, nameof(configureOptions)); - DccConfigurationOptions dccConfigurationOptions = configureOptions.Invoke(); - - ArgumentNullException.ThrowIfNull(defaultSectionOptions, nameof(defaultSectionOptions)); - DccSectionOptions defaultSectionOption = new(); - defaultSectionOptions.Invoke(defaultSectionOption); - - var expansionSectionOption = new DccExpandSectionOptions(); - expansionSectionOptions?.Invoke(expansionSectionOption); - - return builder.UseDcc(dccConfigurationOptions, defaultSectionOption, expansionSectionOption.ExpandSections, - jsonSerializerOptions, callerOptions); + var configurationSection = builder.Configuration.GetSection(sectionName); + var dccOptions = configurationSection.Get(); + return builder.UseDcc(dccOptions, jsonSerializerOptions, callerOptions); } public static IMasaConfigurationBuilder UseDcc( this IMasaConfigurationBuilder builder, - DccConfigurationOptions configureOptions, - DccSectionOptions defaultSectionOptions, - List? expansionSectionOptions, + DccOptions dccOptions, Action? jsonSerializerOptions, Action? action) { - StaticConfig.AppId = defaultSectionOptions.AppId; - var services = builder.Services; if (services.Any(service => service.ImplementationType == typeof(DccConfigurationProvider))) return builder; services.AddSingleton(); - var config = GetDccConfigurationOption(configureOptions, defaultSectionOptions, expansionSectionOptions); + services.AddMasaRedisCache(DEFAULT_CLIENT_NAME, dccOptions.RedisOptions) + .AddSharedMasaMemoryCache(dccOptions.SubscribeKeyPrefix ?? DEFAULT_SUBSCRIBE_KEY_PREFIX); + + var dccConfigurationOptions = ComplementAndCheckDccConfigurationOption(builder, dccOptions); - var jsonSerializerOption = new JsonSerializerOptions() + var jsonSerializerOption = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; @@ -83,7 +46,7 @@ public static IMasaConfigurationBuilder UseDcc( { options.UseHttpClient(callerName, () => new MasaHttpClientBuilder( - opt => opt.BaseAddress = new Uri(config.DccConfigurationOptions.ManageServiceAddress)) + opt => opt.BaseAddress = new Uri(dccConfigurationOptions.ManageServiceAddress)) ); } else @@ -94,20 +57,22 @@ public static IMasaConfigurationBuilder UseDcc( } }); - services.AddMasaRedisCache(DEFAULT_CLIENT_NAME, config.DccConfigurationOptions.RedisOptions) - .AddSharedMasaMemoryCache(config.DccConfigurationOptions.SubscribeKeyPrefix ?? DEFAULT_SUBSCRIBE_KEY_PREFIX); + TryAddConfigurationApiClient(services, + dccConfigurationOptions.DefaultSection, + dccConfigurationOptions.ExpandSections, + jsonSerializerOption); - TryAddConfigurationApiClient(services, config.DefaultSectionOptions, config.ExpansionSectionOptions, jsonSerializerOption); - TryAddConfigurationApiManage(services, callerName, config.DefaultSectionOptions, config.ExpansionSectionOptions); + TryAddConfigurationApiManage(services, + callerName, + dccConfigurationOptions.DefaultSection, + dccConfigurationOptions.ExpandSections); - var sectionOptions = new List() - { - config.DefaultSectionOptions - }.Concat(config.ExpansionSectionOptions); + var serviceProvider = services.BuildServiceProvider(); - var configurationApiClient = services.BuildServiceProvider().GetRequiredService(); - var loggerFactory = services.BuildServiceProvider().GetRequiredService(); - builder.AddRepository(new DccConfigurationRepository(sectionOptions, configurationApiClient, loggerFactory)); + var configurationApiClient = serviceProvider.GetRequiredService(); + var loggerFactory = serviceProvider.GetRequiredService(); + builder.AddRepository(new DccConfigurationRepository(dccConfigurationOptions.GetAllSections(), + configurationApiClient, loggerFactory)); return builder; } @@ -145,54 +110,79 @@ public static IServiceCollection TryAddConfigurationApiManage(IServiceCollection return services; } - private static DccOptions GetDccConfigurationOption( - DccConfigurationOptions dccConfigurationOption, - DccSectionOptions defaultSectionOptions, - List? expansionSectionOptions = null) + public static DccConfigurationOptions ComplementAndCheckDccConfigurationOption( + IMasaConfigurationBuilder builder, + DccOptions dccOptions) { - ArgumentNullException.ThrowIfNull(dccConfigurationOption, nameof(dccConfigurationOption)); + DccConfigurationOptions dccConfigurationOptions = dccOptions; + CheckDccConfigurationOptions(dccConfigurationOptions); - if (string.IsNullOrEmpty(dccConfigurationOption.ManageServiceAddress)) - throw new ArgumentNullException(nameof(dccConfigurationOption.ManageServiceAddress)); + var serviceProvider = builder.Services.BuildServiceProvider(); - if (dccConfigurationOption.RedisOptions == null) - throw new ArgumentNullException(nameof(dccConfigurationOption.RedisOptions)); + using var scope = serviceProvider.CreateScope(); - if (dccConfigurationOption.RedisOptions.Servers == null || dccConfigurationOption.RedisOptions.Servers.Count == 0 || - dccConfigurationOption.RedisOptions.Servers.Any(service => string.IsNullOrEmpty(service.Host) || service.Port <= 0)) - throw new ArgumentNullException(nameof(dccConfigurationOption.RedisOptions.Servers)); + MasaAppConfigureOptions? masaAppConfigureOptions = null; + dccConfigurationOptions.PublicId ??= GetMasaAppConfigureOptions().GetValue(nameof(DccOptions.PublicId), () => DEFAULT_PUBLIC_ID); + dccConfigurationOptions.PublicSecret ??= GetMasaAppConfigureOptions().GetValue(nameof(DccOptions.PublicSecret)); - ArgumentNullException.ThrowIfNull(defaultSectionOptions, nameof(defaultSectionOptions)); + var distributedCacheClient = scope.ServiceProvider.GetDistributedCacheClient(); + dccConfigurationOptions.DefaultSection.ComplementAndCheckAppId(GetMasaAppConfigureOptions().AppId); + dccConfigurationOptions.DefaultSection.ComplementAndCheckEnvironment(GetMasaAppConfigureOptions().Environment); + dccConfigurationOptions.DefaultSection.ComplementAndCheckCluster(GetMasaAppConfigureOptions().Cluster); + dccConfigurationOptions.DefaultSection.ComplementConfigObjects(distributedCacheClient); - if (string.IsNullOrEmpty(defaultSectionOptions.AppId)) - throw new ArgumentNullException("AppId cannot be empty"); + if (dccConfigurationOptions.ExpandSections.All(section => section.AppId != dccConfigurationOptions.PublicId)) + { + var publicSection = new DccSectionOptions + { + AppId = dccConfigurationOptions.PublicId, + Secret = dccConfigurationOptions.PublicSecret + }; + dccConfigurationOptions.ExpandSections.Add(publicSection); + } - if (defaultSectionOptions.ConfigObjects == null || !defaultSectionOptions.ConfigObjects.Any()) - throw new ArgumentNullException("ConfigObjects cannot be empty"); + StaticConfig.AppId = dccConfigurationOptions.DefaultSection.AppId; + StaticConfig.PublicId = dccConfigurationOptions.PublicId; - if (string.IsNullOrEmpty(defaultSectionOptions.Cluster)) - defaultSectionOptions.Cluster = "Default"; - if (string.IsNullOrEmpty(defaultSectionOptions.Environment)) - defaultSectionOptions.Environment = GetDefaultEnvironment(); + if (dccConfigurationOptions.ExpandSections.Any(sectionOption + => sectionOption.AppId == dccConfigurationOptions.DefaultSection.AppId)) + throw new ArgumentException("The extension AppId cannot be the same as the default AppId", nameof(dccOptions)); - List expansionOptions = new(); - foreach (var expansionOption in expansionSectionOptions ?? new()) + foreach (var sectionOption in dccConfigurationOptions.ExpandSections) { - if (string.IsNullOrEmpty(expansionOption.Environment)) - expansionOption.Environment = defaultSectionOptions.Environment; - if (string.IsNullOrEmpty(expansionOption.Cluster)) - expansionOption.Cluster = defaultSectionOptions.Cluster; + sectionOption.ComplementAndCheckEnvironment(dccConfigurationOptions.DefaultSection.Environment); + sectionOption.ComplementAndCheckCluster(dccConfigurationOptions.DefaultSection.Cluster); + sectionOption.ComplementConfigObjects(distributedCacheClient); + } + return dccConfigurationOptions; - if (expansionOption.ConfigObjects == null || !expansionOption.ConfigObjects.Any()) - throw new ArgumentNullException("ConfigObjects in the extension section cannot be empty"); + MasaAppConfigureOptions GetMasaAppConfigureOptions() + { + return masaAppConfigureOptions ??= scope.ServiceProvider.GetRequiredService>().Value; + } + } - if (expansionOption.AppId == defaultSectionOptions.AppId || - expansionOptions.Any(section => section.AppId == expansionOption.AppId)) - throw new ArgumentNullException("The current section already exists, no need to mount repeatedly"); + private static IDistributedCacheClient GetDistributedCacheClient(this IServiceProvider serviceProvider) + => serviceProvider.GetRequiredService().CreateClient(DEFAULT_CLIENT_NAME); - expansionOptions.Add(expansionOption); - } - return new(dccConfigurationOption, defaultSectionOptions, expansionOptions); + private static void CheckDccConfigurationOptions(DccConfigurationOptions dccOptions) + { + if (string.IsNullOrEmpty(dccOptions.ManageServiceAddress)) + throw new ArgumentNullException(nameof(dccOptions), "ManageServiceAddress cannot be empty"); + + if (!dccOptions.RedisOptions.Servers.Any()) + throw new ArgumentException("The Redis configuration cannot be empty", nameof(dccOptions)); + + if (dccOptions.RedisOptions.Servers.Any(service => string.IsNullOrEmpty(service.Host) || service.Port <= 0)) + throw new ArgumentException( + "The Redis server address cannot be empty, and the Redis port must be grather than 0", + nameof(dccOptions)); + + if (dccOptions.ExpandSections.Any(dccSectionOptions => string.IsNullOrWhiteSpace(dccSectionOptions.AppId))) + throw new ArgumentException("sections with an empty AppId are not allowed", nameof(dccOptions)); + + if (dccOptions.ExpandSections.DistinctBy(dccSectionOptions => dccSectionOptions.AppId).Count() != dccOptions.ExpandSections.Count) + throw new ArgumentException("AppId cannot be repeated", nameof(dccOptions)); } private static ICachingBuilder AddSharedMasaMemoryCache(this ICachingBuilder builder, string subscribeKeyPrefix) @@ -206,11 +196,6 @@ private static ICachingBuilder AddSharedMasaMemoryCache(this ICachingBuilder bui return builder; } - private static string GetDefaultEnvironment() - => System.Environment.GetEnvironmentVariable(DEFAULT_ENVIRONMENT_NAME) ?? - throw new ArgumentNullException( - "Error getting environment information, please make sure the value of ASPNETCORE_ENVIRONMENT has been configured"); - private sealed class DccConfigurationProvider { diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccConfigurationOptions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccConfigurationOptions.cs index 605bbae3a..f2a3fb573 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccConfigurationOptions.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccConfigurationOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.Contrib.Configuration.ConfigurationApi.Dcc.Options; @@ -13,4 +13,28 @@ public class DccConfigurationOptions /// The prefix of Dcc PubSub, it is not recommended to modify /// public string? SubscribeKeyPrefix { get; set; } + + /// + /// public config id + /// + internal string? PublicId { get; set; } + + internal string? PublicSecret { get; set; } + + public DccSectionOptions DefaultSection { get; set; } + + /// + /// Expansion section information + /// + public List ExpandSections { get; set; } + + public DccConfigurationOptions() + { + ExpandSections = new(); + } + + public IEnumerable GetAllSections() => new List() + { + DefaultSection + }.Concat(ExpandSections); } diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccExpandSectionOptions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccExpandSectionOptions.cs deleted file mode 100644 index 57ce1c9a0..000000000 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccExpandSectionOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) MASA Stack All rights reserved. -// Licensed under the MIT License. See LICENSE.txt in the project root for license information. - -namespace Masa.Contrib.Configuration.ConfigurationApi.Dcc.Options; - -public class DccExpandSectionOptions -{ - /// - /// Expansion section information - /// - public List? ExpandSections { get; set; } -} diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccOptions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccOptions.cs new file mode 100644 index 000000000..9b68f4d17 --- /dev/null +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccOptions.cs @@ -0,0 +1,70 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Configuration.ConfigurationApi.Dcc.Options; + +public class DccOptions : DccSectionOptions +{ + public RedisConfigurationOptions RedisOptions { get; set; } = new(); + + public string ManageServiceAddress { get; set; } = default!; + + /// + /// The prefix of Dcc PubSub, it is not recommended to modify + /// + public string? SubscribeKeyPrefix { get; set; } + + /// + /// public config id + /// + public string? PublicId { get; set; } = default!; + + public string? PublicSecret { get; set; } + + /// + /// Expansion section information + /// + public List ExpandSections { get; set; } = new(); + + public static implicit operator DccConfigurationOptions(DccOptions options) + { + var dccConfigurationOptions = new DccConfigurationOptions() + { + RedisOptions = new RedisConfigurationOptions() + { + Servers = options.RedisOptions.Servers.Select(server => new RedisServerOptions(server.Host, server.Port)).ToList(), + AbortOnConnectFail = options.RedisOptions.AbortOnConnectFail, + AllowAdmin = options.RedisOptions.AllowAdmin, + ClientName = options.RedisOptions.ClientName, + ChannelPrefix = options.RedisOptions.ChannelPrefix, + ConnectRetry = options.RedisOptions.ConnectRetry, + ConnectTimeout = options.RedisOptions.ConnectTimeout, + DefaultDatabase = options.RedisOptions.DefaultDatabase, + Password = options.RedisOptions.Password, + Proxy = options.RedisOptions.Proxy, + Ssl = options.RedisOptions.Ssl, + SyncTimeout = options.RedisOptions.SyncTimeout, + AbsoluteExpiration = options.RedisOptions.AbsoluteExpiration, + AbsoluteExpirationRelativeToNow = options.RedisOptions.AbsoluteExpirationRelativeToNow, + SlidingExpiration = options.RedisOptions.SlidingExpiration + }, + ManageServiceAddress = options.ManageServiceAddress, + SubscribeKeyPrefix = options.SubscribeKeyPrefix, + PublicId = options.PublicId, + PublicSecret = options.PublicSecret, + DefaultSection = new DccSectionOptions( + options.AppId, + options.Environment, + options.Cluster, + options.ConfigObjects, + options.Secret), + ExpandSections = options.ExpandSections.Select(section => new DccSectionOptions( + section.AppId, + section.Environment, + section.Cluster, + section.ConfigObjects, + section.Secret)).ToList() + }; + return dccConfigurationOptions; + } +} diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccSectionOptions.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccSectionOptions.cs index 1394f89c2..da216b5b0 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccSectionOptions.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/Options/DccSectionOptions.cs @@ -9,19 +9,56 @@ public class DccSectionOptions /// The environment name. /// Get from the environment variable ASPNETCORE_ENVIRONMENT when Environment is null or empty /// - public string? Environment { get; set; } = null; + public string Environment { get; set; } = string.Empty; /// /// The cluster name. /// - public string? Cluster { get; set; } + public string Cluster { get; set; } = string.Empty; /// /// The app id. /// - public string AppId { get; set; } = default!; + public string AppId { get; set; } = string.Empty; - public List ConfigObjects { get; set; } = default!; + public List ConfigObjects { get; set; } = new(); public string? Secret { get; set; } + + public DccSectionOptions() + { + + } + + public DccSectionOptions(string appId, string environment, string cluster, List configObjects, string? secret) : this() + { + AppId = appId; + Environment = environment; + Cluster = cluster; + ConfigObjects = configObjects; + Secret = secret ?? string.Empty; + } + + public void ComplementConfigObjects(IDistributedCacheClient distributedCacheClient) + { + if (!ConfigObjects.Any()) + { + ConfigObjects = distributedCacheClient.GetAllConfigObjects(AppId, Environment, Cluster); + } + } + + public void ComplementAndCheckAppId(string defaultValue) + { + if (string.IsNullOrWhiteSpace(AppId)) AppId = defaultValue; + } + + public void ComplementAndCheckEnvironment(string defaultValue) + { + if (string.IsNullOrWhiteSpace(Environment)) Environment = defaultValue; + } + + public void ComplementAndCheckCluster(string defaultValue) + { + if (string.IsNullOrWhiteSpace(Cluster)) Cluster = defaultValue; + } } diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.md b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.md index d7055f623..7f2ff398f 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.md +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.md @@ -37,13 +37,15 @@ appsettings.json ], "DefaultDatabase": 0, "Password": "" - } + }, + "PublicId": "PublicId", + "PublicSecret": "PublicSecret", + "AppId": "Replace-With-Your-AppId", + "Environment": "Development", + "ConfigObjects": [ "Redis" ], //The name of the object to be mounted, the Redis configuration will be mounted here under the ConfigurationApi: node + "Secret": "", //Dcc App key + "Cluster": "Default" }, - "AppId": "Replace-With-Your-AppId", - "Environment": "Development", - "ConfigObjects": [ "Redis" ], //The name of the object to be mounted, the Redis configuration will be mounted here under the ConfigurationApi: node - "Secret": "", //Dcc App key - "Cluster": "Default" } ``` @@ -66,9 +68,9 @@ public class RedisOptions : ConfigurationApiMasaConfigurationOptions public override string? ObjectName { get; init; } = "Redis"; public string Host { get; set; } - + public int Port { get; set; } - + public int DefaultDatabase { get; set; } } @@ -79,7 +81,7 @@ public class CustomDccSectionOptions : ConfigurationApiMasaConfigurationOptions /// [JsonIgnore] public override string AppId { get; set; } = "Replace-With-Your-AppId"; - + /// /// The environment name. /// Get from the environment variable ASPNETCORE_ENVIRONMENT when Environment is null or empty diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.zh-CN.md b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.zh-CN.md index b217be2dc..ec66a221e 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.zh-CN.md +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/README.zh-CN.md @@ -27,7 +27,7 @@ appsettings.json { //Dcc配置,扩展Configuration能力,支持远程配置 "DccOptions": { - "ManageServiceAddress ": "http://localhost:8890", + "ManageServiceAddress": "http://localhost:8890", "RedisOptions": { "Servers": [ { @@ -37,13 +37,15 @@ appsettings.json ], "DefaultDatabase": 0, "Password": "" - } - }, - "AppId": "Replace-With-Your-AppId", - "Environment": "Development", - "ConfigObjects": [ "Redis" ], //待挂载的对象名, 此处会将Redis配置挂载到ConfigurationApi:节点下 - "Secret": "", //Dcc App 秘钥 - "Cluster": "Default" + }, + "PublicId": "PublicId", + "PublicSecret": "PublicSecret", + "AppId": "Replace-With-Your-AppId", + "Environment": "Development", + "ConfigObjects": [ "Redis" ], //待挂载的对象名, 此处会将Redis配置挂载到ConfigurationApi:节点下 + "Secret": "", //Dcc App 秘钥 + "Cluster": "Default" + } } ``` diff --git a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/_Imports.cs b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/_Imports.cs index 0bf86bba8..4bbdf4926 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/_Imports.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Masa.Contrib.Configuration.ConfigurationApi.Dcc/_Imports.cs @@ -7,12 +7,12 @@ global using Masa.BuildingBlocks.Service.Caller.Options; global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Internal; global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Internal.Model; -global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Internal.Options; global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Internal.Parser; global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Options; global using Masa.Contrib.Service.Caller; global using Masa.Contrib.Service.Caller.HttpClient; global using Masa.Utils.Caching.Core.DependencyInjection; +global using Masa.Utils.Caching.Core.Interfaces; global using Masa.Utils.Caching.Core.Models; global using Masa.Utils.Caching.DistributedMemory.DependencyInjection; global using Masa.Utils.Caching.DistributedMemory.Interfaces; @@ -22,6 +22,7 @@ global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; global using System.Collections.Concurrent; global using System.Data; global using System.Diagnostics; diff --git a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccClientTest.cs b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccClientTest.cs index 670e9aad4..c1ae7712f 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccClientTest.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccClientTest.cs @@ -432,33 +432,140 @@ public async Task TestGetAsyncByJsonReturn(string environment, string cluster, s Assert.IsTrue(ret.Id == newBrand.Id && ret.Name == newBrand.Name); } - // [DataTestMethod] - // [DataRow("Test", "Default", "DccTest", "Brand")] - // public async Task GetAsyncByProperty(string environment, string cluster, string appId, string configObject) - // { - // var brand = new List() - // { - // new() - // { - // Key = "Id", - // Value = Guid.NewGuid().ToString(), - // }, - // new() - // { - // Key = "Name", - // Value = "Microsoft" - // } - // }; - // _client.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result).Returns(() => new PublishRelease() - // { - // ConfigFormat = ConfigFormats.Properties, - // Content = brand.Serialize(_jsonSerializerOptions) - // }.Serialize(_jsonSerializerOptions)).Verifiable(); - // var client = new ConfigurationApiClient(_serviceProvider, _client.Object, _jsonSerializerOptions, _dccSectionOptions, null); - // var ret = await client.GetAsync(environment, cluster, appId, configObject, It.IsAny>()); - // Assert.IsNotNull(ret); - // - // Assert.IsTrue(ret.Id.ToString() == brand.Where(b => b.Key == "Id").Select(t => t.Value).FirstOrDefault() && - // ret.Name == brand.Where(b => b.Key == "Name").Select(t => t.Value).FirstOrDefault()); - // } + [DataTestMethod] + [DataRow("Development", "Default", "WebApplication1", "Brand")] + public void TestSingleSection(string environment, string cluster, string appId, string configObject) + { + CustomTrigger trigger = new CustomTrigger(_jsonSerializerOptions); + var brand = new Brands("Microsoft"); + var response = JsonSerializer.Serialize(new PublishRelease() + { + Content = brand.Serialize(_jsonSerializerOptions), + ConfigFormat = ConfigFormats.Text + }); + Mock memoryCacheClient = new(); + memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) + .Returns(() => response); + var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), + memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); + _services.AddSingleton(configurationApiClient); + + Assert.IsTrue( + configurationApiClient + .GetRawAsync(environment, cluster, appId, configObject, It.IsAny>()) + .GetAwaiter() + .GetResult().Raw == brand.Serialize(_jsonSerializerOptions)); + trigger.Execute(); + } + + [DataTestMethod] + [DataRow("Development", "Default", "WebApplication1", "Brand")] + public void TestSingleSection2(string environment, string cluster, string appId, string configObject) + { + CustomTrigger trigger = new CustomTrigger(_jsonSerializerOptions); + Mock memoryCacheClient = new(); + Dictionary masaDic = new Dictionary() + { + { "Id", Guid.NewGuid().ToString() }, + { "Name", "Masa" } + }; + var response = JsonSerializer.Serialize(new PublishRelease() + { + Content = masaDic.Serialize(_jsonSerializerOptions), + ConfigFormat = ConfigFormats.Json + }); + memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) + .Returns(() => response); + var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), + memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); + _services.AddSingleton(configurationApiClient); + + Assert.IsTrue( + configurationApiClient.GetRawAsync( + environment, + cluster, + appId, + configObject, + It.IsAny>()).Result.Raw == masaDic.Serialize(_jsonSerializerOptions)); + } + + [DataTestMethod] + [DataRow("Development", "Default", "WebApplication1", "Brand")] + public void TestSingleSection3(string environment, string cluster, string appId, string configObject) + { + Mock memoryCacheClient = new(); + + var response = JsonSerializer.Serialize(new PublishRelease() + { + Content = "Test", + ConfigFormat = ConfigFormats.Text + }); + memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) + .Returns(() => response); + var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), + memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); + _services.AddSingleton(configurationApiClient); + + Assert.IsTrue(configurationApiClient.GetRawAsync( + environment, + cluster, + appId, + configObject, + It.IsAny>()).GetAwaiter().GetResult().Raw == "Test"); + } + + [DataTestMethod] + [DataRow("Development", "Default", "WebApplication1", "Brand")] + public void TestSingleSection4(string environment, string cluster, string appId, string configObject) + { + Mock memoryCacheClient = new(); + + var response = JsonSerializer.Serialize(new PublishRelease() + { + Content = null, + ConfigFormat = ConfigFormats.Text + }); + memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) + .Returns(() => response); + var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), + memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); + _services.AddSingleton(configurationApiClient); + + Assert.IsTrue(configurationApiClient.GetRawAsync( + environment, + cluster, + appId, + configObject, + It.IsAny>()).GetAwaiter().GetResult().Raw == null); + } + + [DataTestMethod] + [DataRow("Test", "Default", "DccTest", "Brand")] + public async Task GetAsyncByProperty(string environment, string cluster, string appId, string configObject) + { + var brand = new List() + { + new() + { + Key = "Id", + Value = Guid.NewGuid().ToString(), + }, + new() + { + Key = "Name", + Value = "Microsoft" + } + }; + _client.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result).Returns(() => new PublishRelease() + { + ConfigFormat = ConfigFormats.Properties, + Content = brand.Serialize(_jsonSerializerOptions) + }.Serialize(_jsonSerializerOptions)).Verifiable(); + var client = new ConfigurationApiClient(_serviceProvider, _client.Object, _jsonSerializerOptions, _dccSectionOptions, null); + var ret = await client.GetAsync(environment, cluster, appId, configObject, It.IsAny>()); + Assert.IsNotNull(ret); + + Assert.IsTrue(ret.Id.ToString() == brand.Where(b => b.Key == "Id").Select(t => t.Value).FirstOrDefault() && + ret.Name == brand.Where(b => b.Key == "Name").Select(t => t.Value).FirstOrDefault()); + } } diff --git a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccManageTest.cs b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccManageTest.cs index 115ccff3a..dfa80ceda 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccManageTest.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccManageTest.cs @@ -1,8 +1,6 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. - - namespace Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests; [TestClass] diff --git a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccTest.cs b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccTest.cs index cd70b55b2..36af8aabd 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccTest.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/DccTest.cs @@ -12,22 +12,28 @@ public class DccTest private IServiceCollection _services; private Mock _memoryCacheClientFactory; + private Mock _distributedCacheClientFactory; private Mock _memoryCache; private Mock _distributedCacheClient; private const string DEFAULT_ENVIRONMENT_NAME = "ASPNETCORE_ENVIRONMENT"; private const string DEFAULT_SUBSCRIBE_KEY_PREFIX = "masa.dcc:"; + private const string DEFAULT_PUBLIC_ID = "public-$Config"; [TestInitialize] public void Initialize() { - _services = new ServiceCollection(); - _masaConfigurationBuilder = new Mock(); + var builder = WebApplication.CreateBuilder(); + builder = builder.InitializeAppConfiguration(); + _services = builder.Services; + _masaConfigurationBuilder = new(); var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", true, true).Build; - _masaConfigurationBuilder.Setup(builder => builder.Configuration).Returns(configuration).Verifiable(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - _memoryCacheClientFactory = new Mock(); - _memoryCache = new Mock(); - _distributedCacheClient = new Mock(); + _masaConfigurationBuilder.Setup(masaConfigurationBuilder => masaConfigurationBuilder.Configuration).Returns(configuration) + .Verifiable(); + _masaConfigurationBuilder.Setup(masaConfigurationBuilder => masaConfigurationBuilder.Services).Returns(_services).Verifiable(); + _memoryCacheClientFactory = new(); + _distributedCacheClientFactory = new(); + _memoryCache = new(); + _distributedCacheClient = new(); _jsonSerializerOptions = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true @@ -102,17 +108,6 @@ public void TestTryAddConfigurationApiManage() Assert.IsTrue(serviceProvider.GetServices().Count() == 1); } - [TestMethod] - public void TestUseDccAndNullDccConfigurationOption() - { - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => null!, option => - { - option.AppId = "Test"; - option.Environment = "Test"; - option.ConfigObjects = new List() { "Te" }; - }, null)); - } - [TestMethod] public void TestCustomCaller() { @@ -128,7 +123,7 @@ public void TestCustomCaller() var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); _services.AddSingleton(configurationApiClient); - _masaConfigurationBuilder.Object.UseDcc(() => new DccConfigurationOptions() + _masaConfigurationBuilder.Object.UseDcc(new DccOptions() { ManageServiceAddress = "https://github.com", RedisOptions = new RedisConfigurationOptions @@ -141,16 +136,14 @@ public void TestCustomCaller() Port = 6379 } } - } - }, option => - { - option.AppId = "Test"; - option.Environment = "Test"; - option.ConfigObjects = new List() + }, + AppId = "Test", + Environment = "Test", + ConfigObjects = new List() { "Settings" - }; - }, null, jsonSerializerOption => + } + }, jsonSerializerOption => { jsonSerializerOption.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping; }, option => @@ -164,386 +157,6 @@ public void TestCustomCaller() Assert.IsNotNull(caller); } - [TestMethod] - public void TestUseDccAndEmptyDccServiceAddress() - { - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "", - }; - }, null!, null), "DccServiceAddress"); - } - - [TestMethod] - public void TestUseDccAndErrorDccService() - { - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = null! - } - }; - }, null!, null), "Servers"); - - _services = new ServiceCollection(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions - { - Servers = new List() - } - }; - }, null!, null), "Servers"); - - _services = new ServiceCollection(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions - { - Servers = new List() - { - new() - { - Host = "", - Port = 8080 - } - } - } - }; - }, null!, null), "Servers"); - - _services = new ServiceCollection(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = -1 - } - } - } - }; - }, null!, null), "Servers"); - } - - [TestMethod] - public void TestUseDccAndErrorDefaultSectionOption() - { - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, null!, null), "defaultSectionOptions"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = ""; - }, null), "AppId cannot be empty"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = null!; - }, null), "ConfigObjects cannot be empty"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List(); - }, null), "ConfigObjects cannot be empty"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List() - { - "Brand" - }; - }, null), "Error getting environment information, please make sure the value of ASPNETCORE_ENVIRONMENT has been configured"); - } - - [TestMethod] - public void TestUseDccAndErrorExpansionSectionOptions() - { - Environment.SetEnvironmentVariable(DEFAULT_ENVIRONMENT_NAME, "Test"); - - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List() - { - "Brand" - }; - }, option => - { - option.ExpandSections = new List() - { - new() - { - AppId = "Test2", - } - }; - }), "ConfigObjects in the extension section cannot be empty"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List() - { - "Brand" - }; - }, option => - { - option.ExpandSections = new List() - { - new() - { - AppId = "Test2", - ConfigObjects = new List() - } - }; - }), "ConfigObjects in the extension section cannot be empty"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List() - { - "Brand" - }; - }, option => - { - option.ExpandSections = new List() - { - new() - { - AppId = "Test", - ConfigObjects = new List() - { - "Settings" - } - } - }; - }), "The current section already exists, no need to mount repeatedly"); - - _services = new ServiceCollection(); - _masaConfigurationBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); - - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc(() => - { - return new DccConfigurationOptions() - { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() - { - Servers = new List() - { - new() - { - Host = "localhost", - Port = 6379 - } - } - } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List() - { - "Brand" - }; - }, option => - { - option.ExpandSections = new List() - { - new() - { - AppId = "Test2", - ConfigObjects = new List() - { - "Settings" - } - }, - new() - { - AppId = "Test2", - ConfigObjects = new List() - { - "Settings" - } - } - }; - }), "The current section already exists, no need to mount repeatedly"); - } - [DataTestMethod] [DataRow("Development", "Default", "WebApplication1", "Brand")] public void TestUseDccAndSuccess(string environment, string cluster, string appId, string configObject) @@ -563,186 +176,34 @@ public void TestUseDccAndSuccess(string environment, string cluster, string appI memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); _services.AddSingleton(configurationApiClient); - _masaConfigurationBuilder.Object.UseDcc(() => + var dccOptions = new DccOptions() { - return new DccConfigurationOptions() + ManageServiceAddress = "https://github.com", + RedisOptions = new RedisConfigurationOptions() { - ManageServiceAddress = "https://github.com", - RedisOptions = new RedisConfigurationOptions() + Servers = new List() { - Servers = new List() + new() { - new() - { - Host = "localhost", - Port = 6379 - } + Host = "localhost", + Port = 6379 } } - }; - }, option => - { - option.AppId = "Test"; - option.ConfigObjects = new List() + }, + + AppId = "Test", + ConfigObjects = new List() { "Brand" - }; - }, null); + } + }; + _masaConfigurationBuilder.Object.UseDcc(dccOptions, null, null); var optionFactory = _services.BuildServiceProvider().GetRequiredService>(); var option = optionFactory.Create(DEFAULT_CLIENT_NAME); - Assert.IsTrue(option.SubscribeKeyType == SubscribeKeyTypes.SpecificPrefix); - Assert.IsTrue(option.SubscribeKeyPrefix == DEFAULT_SUBSCRIBE_KEY_PREFIX); } - [TestMethod] - public void TestDccConfigurationOptions() - { - var options = new DccConfigurationOptions - { - RedisOptions = new RedisConfigurationOptions() - { - SyncTimeout = 10 - }, - ManageServiceAddress = "https://github.com", - SubscribeKeyPrefix = "masa.dcc.test:" - }; - Assert.IsTrue(options.SubscribeKeyPrefix == "masa.dcc.test:"); - Assert.IsTrue(options.ManageServiceAddress == "https://github.com"); - Assert.IsTrue(options.RedisOptions.SyncTimeout == 10); - } - - [DataTestMethod] - [DataRow("Development", "Default", "WebApplication1", "Brand")] - public void TestUseDccAndSingleSection(string environment, string cluster, string appId, string configObject) - { - CustomTrigger trigger = new CustomTrigger(_jsonSerializerOptions); - var brand = new Brands("Microsoft"); - var response = JsonSerializer.Serialize(new PublishRelease() - { - Content = brand.Serialize(_jsonSerializerOptions), - ConfigFormat = ConfigFormats.Text - }); - Mock memoryCacheClient = new(); - memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) - .Returns(() => response); - var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), - memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); - _services.AddSingleton(configurationApiClient); - - _masaConfigurationBuilder.Object.UseDcc(); - Assert.IsTrue( - configurationApiClient - .GetRawAsync(environment, cluster, appId, configObject, It.IsAny>()) - .GetAwaiter() - .GetResult().Raw == brand.Serialize(_jsonSerializerOptions)); - trigger.Execute(); - } - - [DataTestMethod] - [DataRow("Development", "Default", "WebApplication1", "Brand")] - public void TestUseDccAndSingleSection2(string environment, string cluster, string appId, string configObject) - { - CustomTrigger trigger = new CustomTrigger(_jsonSerializerOptions); - Mock memoryCacheClient = new(); - Dictionary masaDic = new Dictionary() - { - { "Id", Guid.NewGuid().ToString() }, - { "Name", "Masa" } - }; - var response = JsonSerializer.Serialize(new PublishRelease() - { - Content = masaDic.Serialize(_jsonSerializerOptions), - ConfigFormat = ConfigFormats.Json - }); - memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) - .Returns(() => response); - var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), - memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); - _services.AddSingleton(configurationApiClient); - - _masaConfigurationBuilder.Object.UseDcc(); - Assert.IsTrue( - configurationApiClient.GetRawAsync( - environment, - cluster, - appId, - configObject, - It.IsAny>()).Result.Raw == masaDic.Serialize(_jsonSerializerOptions)); - } - - [DataTestMethod] - [DataRow("Development", "Default", "WebApplication1", "Brand")] - public void TestUseDccAndSingleSection3(string environment, string cluster, string appId, string configObject) - { - Mock memoryCacheClient = new(); - - var response = JsonSerializer.Serialize(new PublishRelease() - { - Content = "Test", - ConfigFormat = ConfigFormats.Text - }); - memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) - .Returns(() => response); - var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), - memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); - _services.AddSingleton(configurationApiClient); - - _masaConfigurationBuilder.Object.UseDcc(); - Assert.IsTrue(configurationApiClient.GetRawAsync( - environment, - cluster, - appId, - configObject, - It.IsAny>()).GetAwaiter().GetResult().Raw == "Test"); - } - - [DataTestMethod] - [DataRow("Development", "Default", "WebApplication1", "Brand")] - public void TestUseDccAndSingleSection4(string environment, string cluster, string appId, string configObject) - { - Mock memoryCacheClient = new(); - - var response = JsonSerializer.Serialize(new PublishRelease() - { - Content = null, - ConfigFormat = ConfigFormats.Text - }); - memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) - .Returns(() => response); - var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), - memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); - _services.AddSingleton(configurationApiClient); - - _masaConfigurationBuilder.Object.UseDcc(); - Assert.IsTrue(configurationApiClient.GetRawAsync( - environment, - cluster, - appId, - configObject, - It.IsAny>()).GetAwaiter().GetResult().Raw == null); - } - - [TestMethod] - public void TestUseDccAndSingleSection5() - { - CustomTrigger trigger = new CustomTrigger(_jsonSerializerOptions); - Mock memoryCacheClient = new(); - var response = JsonSerializer.Serialize(new PublishRelease() - { - Content = "Test", - ConfigFormat = (ConfigFormats)4 - }); - memoryCacheClient.Setup(client => client.GetAsync(It.IsAny(), It.IsAny>()).Result) - .Returns(() => response); - var configurationApiClient = new ConfigurationApiClient(_services.BuildServiceProvider(), - memoryCacheClient.Object, _jsonSerializerOptions, new Mock().Object, new List()); - _services.AddSingleton(configurationApiClient); - - Assert.ThrowsException(() => _masaConfigurationBuilder.Object.UseDcc()); - } - [DataTestMethod] [DataRow("Development", "Default", "WebApplication1", "Brand")] public void TestUseMultiDcc(string environment, string cluster, string appId, string configObject) @@ -773,15 +234,6 @@ public void TestUseMultiDcc(string environment, string cluster, string appId, st Assert.IsTrue(httpClient.BaseAddress!.ToString() == "http://localhost:6379/"); } - [TestMethod] - public void TestLoadPropertiesShouldReturnJson() - { - var brands = new Brands("Microsoft"); - Mock configurationClient = new(); - configurationClient.Setup(client => client.GetRawAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny>())).ReturnsAsync((JsonSerializer.Serialize(brands), ConfigurationTypes.Json)); - } - [TestMethod] public void TestMasaConfigurationByConfigurationAPIReturnKeyIsExist() { @@ -838,4 +290,366 @@ public void TestGetSecretRenturnSecretEqualSecret() var option = field!.GetValue(configurationApiClient); Assert.IsTrue(((DccSectionOptions)option!).Secret == "Secret"); } + + [TestMethod] + public void TestTypeConversionByDccOptions() + { + DccOptions dccOptions = new DccOptions() + { + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new("localhost", 6379), + new("localhost", 6378) + }, + AbortOnConnectFail = true, + AllowAdmin = true, + ClientName = nameof(DccOptions.RedisOptions.ClientName), + ChannelPrefix = nameof(DccOptions.RedisOptions.ChannelPrefix), + ConnectRetry = 1, + ConnectTimeout = 300, + DefaultDatabase = 1, + Password = nameof(DccOptions.RedisOptions.Password), + Proxy = StackExchange.Redis.Proxy.Twemproxy, + Ssl = true, + SyncTimeout = 3000, + AbsoluteExpiration = DateTimeOffset.Now.AddHours(1), + AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1), + SlidingExpiration = TimeSpan.FromHours(2), + }, + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + SubscribeKeyPrefix = nameof(DccOptions.SubscribeKeyPrefix), + PublicId = nameof(DccOptions.PublicId), + PublicSecret = nameof(DccOptions.PublicSecret), + AppId = "appid", + Environment = "test", + Cluster = "default", + ConfigObjects = new List() + { + "configObjects" + }, + Secret = "secret", + ExpandSections = new List() + { + new("appid2", "dev", "default2", new List { "configObjects2" }, "secret2") + } + }; + DccConfigurationOptions dccConfigurationOptions = dccOptions; + Assert.AreEqual(dccOptions.ManageServiceAddress, dccConfigurationOptions.ManageServiceAddress); + Assert.AreEqual(dccOptions.SubscribeKeyPrefix, dccConfigurationOptions.SubscribeKeyPrefix); + Assert.AreEqual(dccOptions.AppId, dccConfigurationOptions.DefaultSection.AppId); + Assert.AreEqual(dccOptions.Environment, dccConfigurationOptions.DefaultSection.Environment); + Assert.AreEqual(dccOptions.Cluster, dccConfigurationOptions.DefaultSection.Cluster); + Assert.AreEqual(dccOptions.ConfigObjects.Count, dccConfigurationOptions.DefaultSection.ConfigObjects.Count); + Assert.AreEqual(dccOptions.ConfigObjects[0], dccConfigurationOptions.DefaultSection.ConfigObjects[0]); + Assert.AreEqual(dccOptions.Secret, dccConfigurationOptions.DefaultSection.Secret); + Assert.AreEqual(dccOptions.ExpandSections.Count, dccConfigurationOptions.ExpandSections.Count); + Assert.AreEqual(dccOptions.ExpandSections[0].AppId, dccConfigurationOptions.ExpandSections[0].AppId); + Assert.AreEqual(dccOptions.ExpandSections[0].Environment, dccConfigurationOptions.ExpandSections[0].Environment); + Assert.AreEqual(dccOptions.ExpandSections[0].Cluster, dccConfigurationOptions.ExpandSections[0].Cluster); + Assert.AreEqual(dccOptions.ExpandSections[0].Secret, dccConfigurationOptions.ExpandSections[0].Secret); + Assert.AreEqual(dccOptions.ExpandSections[0].ConfigObjects.Count, + dccConfigurationOptions.ExpandSections[0].ConfigObjects.Count); + Assert.AreEqual(dccOptions.ExpandSections[0].ConfigObjects[0], dccConfigurationOptions.ExpandSections[0].ConfigObjects[0]); + Assert.AreEqual(dccOptions.RedisOptions.AbortOnConnectFail, dccConfigurationOptions.RedisOptions.AbortOnConnectFail); + Assert.AreEqual(dccOptions.RedisOptions.AllowAdmin, dccConfigurationOptions.RedisOptions.AllowAdmin); + Assert.AreEqual(dccOptions.RedisOptions.ClientName, dccConfigurationOptions.RedisOptions.ClientName); + Assert.AreEqual(dccOptions.RedisOptions.ChannelPrefix, dccConfigurationOptions.RedisOptions.ChannelPrefix); + Assert.AreEqual(dccOptions.RedisOptions.ConnectRetry, dccConfigurationOptions.RedisOptions.ConnectRetry); + Assert.AreEqual(dccOptions.RedisOptions.ConnectTimeout, dccConfigurationOptions.RedisOptions.ConnectTimeout); + Assert.AreEqual(dccOptions.RedisOptions.DefaultDatabase, dccConfigurationOptions.RedisOptions.DefaultDatabase); + Assert.AreEqual(dccOptions.RedisOptions.Password, dccConfigurationOptions.RedisOptions.Password); + Assert.AreEqual(dccOptions.RedisOptions.Proxy, dccConfigurationOptions.RedisOptions.Proxy); + Assert.AreEqual(dccOptions.RedisOptions.Ssl, dccConfigurationOptions.RedisOptions.Ssl); + Assert.AreEqual(dccOptions.RedisOptions.SyncTimeout, dccConfigurationOptions.RedisOptions.SyncTimeout); + Assert.AreEqual(dccOptions.RedisOptions.AbsoluteExpiration, dccConfigurationOptions.RedisOptions.AbsoluteExpiration); + Assert.AreEqual(dccOptions.RedisOptions.AbsoluteExpirationRelativeToNow, + dccConfigurationOptions.RedisOptions.AbsoluteExpirationRelativeToNow); + Assert.AreEqual(dccOptions.RedisOptions.SlidingExpiration, dccConfigurationOptions.RedisOptions.SlidingExpiration); + Assert.AreEqual(dccOptions.RedisOptions.Servers.Count, dccConfigurationOptions.RedisOptions.Servers.Count); + Assert.AreEqual(dccOptions.RedisOptions.Servers[0].Host, dccConfigurationOptions.RedisOptions.Servers[0].Host); + Assert.AreEqual(dccOptions.RedisOptions.Servers[0].Port, dccConfigurationOptions.RedisOptions.Servers[0].Port); + Assert.AreEqual(dccOptions.RedisOptions.Servers[1].Host, dccConfigurationOptions.RedisOptions.Servers[1].Host); + Assert.AreEqual(dccOptions.RedisOptions.Servers[1].Port, dccConfigurationOptions.RedisOptions.Servers[1].Port); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOption() + { + string appid = "test"; + string environment = "dev"; + string cluster = "default"; + _services.Configure(options => + { + options.AppId = appid; + options.Environment = environment; + options.Cluster = cluster; + }); + var configObjects = new List() + { + "configObject", + "configObject2" + }; + var publicConfigObjects = new List() + { + "configObject3", + }; + MockDistributedCacheClient(appid, environment, cluster, configObjects); + MockDistributedCacheClient(DEFAULT_PUBLIC_ID, environment, cluster, publicConfigObjects); + MockDistributedCacheClientFactory(); + + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new() + } + } + }; + var dccConfigurationOptions = + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + + Assert.IsNotNull(dccConfigurationOptions.DefaultSection); + Assert.AreEqual(appid, dccConfigurationOptions.DefaultSection.AppId); + Assert.AreEqual(environment, dccConfigurationOptions.DefaultSection.Environment); + Assert.AreEqual(cluster, dccConfigurationOptions.DefaultSection.Cluster); + Assert.AreEqual(string.Empty, dccConfigurationOptions.DefaultSection.Secret); + Assert.AreEqual(configObjects.Count, dccConfigurationOptions.DefaultSection.ConfigObjects.Count); + Assert.AreEqual(configObjects[0], dccConfigurationOptions.DefaultSection.ConfigObjects[0]); + Assert.AreEqual(configObjects[1], dccConfigurationOptions.DefaultSection.ConfigObjects[1]); + + Assert.AreEqual(1, dccConfigurationOptions.ExpandSections.Count); + Assert.AreEqual(DEFAULT_PUBLIC_ID, dccConfigurationOptions.ExpandSections[0].AppId); + Assert.AreEqual(environment, dccConfigurationOptions.ExpandSections[0].Environment); + Assert.AreEqual(cluster, dccConfigurationOptions.ExpandSections[0].Cluster); + Assert.AreEqual(string.Empty, dccConfigurationOptions.ExpandSections[0].Secret); + Assert.AreEqual(publicConfigObjects.Count, dccConfigurationOptions.ExpandSections[0].ConfigObjects.Count); + Assert.AreEqual(publicConfigObjects[0], dccConfigurationOptions.ExpandSections[0].ConfigObjects[0]); + Assert.AreEqual(2, dccConfigurationOptions.GetAllSections().Count()); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByCustomerDccOptions() + { + string appid = "test"; + string environment = "dev"; + string cluster = "default"; + _services.Configure(options => + { + options.AppId = appid; + options.Environment = environment; + options.Cluster = cluster; + }); + var configObjects = new List() + { + "configObject", + "configObject2" + }; + var publicConfigObjects = new List() + { + "configObject3", + }; + + string customerAppid = "customer-test"; + string customerEnvironment = "customer-dev"; + string customerCluster = "customer-default"; + string customerPublic = "customer-public"; + string customerPublicSecret = "customer-public-secret"; + + MockDistributedCacheClient(customerAppid, customerEnvironment, customerCluster, configObjects); + MockDistributedCacheClient(customerPublic, customerEnvironment, customerCluster, publicConfigObjects); + MockDistributedCacheClientFactory(); + + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new() + } + }, + AppId = customerAppid, + Environment = customerEnvironment, + Cluster = customerCluster, + PublicId = customerPublic, + PublicSecret = customerPublicSecret + }; + var dccConfigurationOptions = + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + + Assert.IsNotNull(dccConfigurationOptions.DefaultSection); + Assert.AreEqual(customerAppid, dccConfigurationOptions.DefaultSection.AppId); + Assert.AreEqual(customerEnvironment, dccConfigurationOptions.DefaultSection.Environment); + Assert.AreEqual(customerCluster, dccConfigurationOptions.DefaultSection.Cluster); + Assert.AreEqual(string.Empty, dccConfigurationOptions.DefaultSection.Secret); + Assert.AreEqual(configObjects.Count, dccConfigurationOptions.DefaultSection.ConfigObjects.Count); + Assert.AreEqual(configObjects[0], dccConfigurationOptions.DefaultSection.ConfigObjects[0]); + Assert.AreEqual(configObjects[1], dccConfigurationOptions.DefaultSection.ConfigObjects[1]); + + Assert.AreEqual(1, dccConfigurationOptions.ExpandSections.Count); + Assert.AreEqual(customerPublic, dccConfigurationOptions.ExpandSections[0].AppId); + Assert.AreEqual(customerEnvironment, dccConfigurationOptions.ExpandSections[0].Environment); + Assert.AreEqual(customerCluster, dccConfigurationOptions.ExpandSections[0].Cluster); + Assert.AreEqual(customerPublicSecret, dccConfigurationOptions.ExpandSections[0].Secret); + Assert.AreEqual(publicConfigObjects.Count, dccConfigurationOptions.ExpandSections[0].ConfigObjects.Count); + Assert.AreEqual(publicConfigObjects[0], dccConfigurationOptions.ExpandSections[0].ConfigObjects[0]); + Assert.AreEqual(2, dccConfigurationOptions.GetAllSections().Count()); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByManageServiceAddressIsEmpty() + { + DccOptions dccOptions = new DccOptions(); + Assert.ThrowsException(() => + { + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + }); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByRedisServersIsEmpty() + { + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + }; + Assert.ThrowsException(() => + { + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + }); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByRedisHostIsEmpty() + { + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new() + { + Host = string.Empty + } + } + } + }; + Assert.ThrowsException(() => + { + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + }); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByRedisPortLessThanZero() + { + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new() + { + Port = -1 + } + } + } + }; + Assert.ThrowsException(() => + { + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + }); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByRepeatAppId() + { + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new() + } + }, + ExpandSections = new List() + { + new() + { + AppId = "test1" + }, + new() + { + AppId = "test1" + } + } + }; + Assert.ThrowsException(() => + { + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + }); + } + + [TestMethod] + public void TestComplementAndCheckDccConfigurationOptionByAppIdIsEmpty() + { + DccOptions dccOptions = new DccOptions() + { + ManageServiceAddress = nameof(DccOptions.ManageServiceAddress), + RedisOptions = new RedisConfigurationOptions() + { + Servers = new List() + { + new() + } + }, + ExpandSections = new List() + { + new() + { + AppId = "" + }, + new() + { + AppId = "test1" + } + } + }; + Assert.ThrowsException(() => + { + MasaConfigurationExtensions.ComplementAndCheckDccConfigurationOption(_masaConfigurationBuilder.Object, dccOptions); + }); + } + + private void MockDistributedCacheClientFactory() + { + _distributedCacheClientFactory + .Setup(factory => factory.CreateClient(DEFAULT_CLIENT_NAME)) + .Returns(() => _distributedCacheClient.Object) + .Verifiable(); + _services.AddSingleton(_ => _distributedCacheClientFactory.Object); + } + + private void MockDistributedCacheClient( + string appId, + string environment, + string cluster, + List mockKeys) + { + string partialKey = + $"{environment}-{cluster}-{appId}".ToLower(); + _distributedCacheClient.Setup(client => client.GetKeys($"{partialKey}*")).Returns(mockKeys); + } } diff --git a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/_Imports.cs b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/_Imports.cs index 4216f2904..50ed4e180 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/_Imports.cs +++ b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/_Imports.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. global using Masa.BuildingBlocks.Configuration; +global using Masa.BuildingBlocks.Configuration.Options; global using Masa.BuildingBlocks.Service.Caller; global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Internal; global using Masa.Contrib.Configuration.ConfigurationApi.Dcc.Options; diff --git a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/appsettings.json b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/appsettings.json index 3708a6f41..9df07c05c 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/appsettings.json +++ b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/appsettings.json @@ -11,13 +11,11 @@ ], "DefaultDatabase": 0, "Password": "" - } - }, - "AppId": "WebApplication1", - "Environment": "Development", - "Cluster": "Default", - "ConfigObjects": [ - "Brand" - ], - "Secret": "Secret" + }, + "AppId": "WebApplication1", + "Environment": "Development", + "Cluster": "Default", + "ConfigObjects": [ "Brand" ], + "Secret": "Secret" + } } \ No newline at end of file diff --git a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/expandSections.json b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/expandSections.json index 28e953152..a1d04ae4e 100644 --- a/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/expandSections.json +++ b/src/Contrib/Configuration/ConfigurationApi/Tests/Masa.Contrib.Configuration.ConfigurationApi.Dcc.Tests/expandSections.json @@ -10,23 +10,21 @@ ], "DefaultDatabase": 0, "Password": "" - } - }, - "AppId": "DccTest", - "Environment": "Test", - "Cluster": "Default", - "ConfigObjects": [ - "Test1" - ], - "Sectet": "", - "ExpandSections": [ - { - "AppId": "DccTest2", - "Environment": "Test2", - "Cluster": "Default", - "ConfigObjects": [ - "Test3" - ] - } - ] + }, + "AppId": "DccTest", + "Environment": "Test", + "Cluster": "Default", + "ConfigObjects": [ "Test1" ], + "Sectet": "", + "ExpandSections": [ + { + "AppId": "DccTest2", + "Environment": "Test2", + "Cluster": "Default", + "ConfigObjects": [ + "Test3" + ] + } + ] + } } \ No newline at end of file diff --git a/src/Contrib/Configuration/Masa.Contrib.Configuration/Extensions/WebApplicationBuilderExtensions.cs b/src/Contrib/Configuration/Masa.Contrib.Configuration/Extensions/WebApplicationBuilderExtensions.cs index 3f10ad884..fe99e9a70 100644 --- a/src/Contrib/Configuration/Masa.Contrib.Configuration/Extensions/WebApplicationBuilderExtensions.cs +++ b/src/Contrib/Configuration/Masa.Contrib.Configuration/Extensions/WebApplicationBuilderExtensions.cs @@ -21,32 +21,61 @@ public static WebApplicationBuilder InitializeAppConfiguration( MasaAppConfigureOptionsRelation optionsRelation = new(); action?.Invoke(optionsRelation); - IConfiguration configuration = builder.Configuration; + IConfiguration? migrateConfiguration = null; bool initialized = false; builder.Services.Configure(options => { if (!initialized) { var masaConfiguration = builder.Services.BuildServiceProvider().GetService(); - if (masaConfiguration != null) configuration = masaConfiguration.Local; + if (masaConfiguration != null) migrateConfiguration = masaConfiguration.Local; initialized = true; } if (string.IsNullOrWhiteSpace(options.AppId)) - options.AppId = configuration.GetConfigurationValue(optionsRelation.Data[nameof(options.AppId)].Variable, - () => optionsRelation.Data[nameof(options.AppId)].DefaultValue); + options.AppId = GetConfigurationValue( + optionsRelation.GetValue(nameof(options.AppId)), + builder.Configuration, + migrateConfiguration); if (string.IsNullOrWhiteSpace(options.Environment)) - options.Environment = configuration.GetConfigurationValue(optionsRelation.Data[nameof(options.Environment)].Variable, - () => optionsRelation.Data[nameof(options.Environment)].DefaultValue); + options.Environment = GetConfigurationValue( + optionsRelation.GetValue(nameof(options.Environment)), + builder.Configuration, + migrateConfiguration); if (string.IsNullOrWhiteSpace(options.Cluster)) - options.Cluster = configuration.GetConfigurationValue(optionsRelation.Data[nameof(options.Cluster)].Variable, - () => optionsRelation.Data[nameof(options.Cluster)].DefaultValue); + options.Cluster = GetConfigurationValue( + optionsRelation.GetValue(nameof(options.Cluster)), + builder.Configuration, + migrateConfiguration); + + foreach (var key in optionsRelation.GetKeys()) + { + options.TryAdd(key, GetConfigurationValue( + optionsRelation.GetValue(key), + builder.Configuration, + migrateConfiguration)); + } }); return builder; } + private static string GetConfigurationValue((string Variable, string DefaultValue) defaultConfig, + IConfiguration configuration, + IConfiguration? migrateConfiguration) + { + var value = configuration[defaultConfig.Variable]; + if (!string.IsNullOrWhiteSpace(value)) + return value; + + if (migrateConfiguration != null) + value = migrateConfiguration[defaultConfig.Variable]; + if (string.IsNullOrWhiteSpace(value)) + value = defaultConfig.DefaultValue; + return value; + } + public static WebApplicationBuilder AddMasaConfiguration( this WebApplicationBuilder builder, Action? configureDelegate = null) diff --git a/src/Contrib/Configuration/Masa.Contrib.Configuration/Internal/ConfigurationExtensions.cs b/src/Contrib/Configuration/Masa.Contrib.Configuration/Internal/ConfigurationExtensions.cs index 297bb64a9..958eb9ed7 100644 --- a/src/Contrib/Configuration/Masa.Contrib.Configuration/Internal/ConfigurationExtensions.cs +++ b/src/Contrib/Configuration/Masa.Contrib.Configuration/Internal/ConfigurationExtensions.cs @@ -28,10 +28,4 @@ public static IConfigurationBuilder AddRange(this IConfigurationBuilder configur configurationBuilder.Add(configurationSource); return configurationBuilder; } - - public static string GetConfigurationValue(this IConfiguration configuration, string key, Func func) - { - var configurationValue = configuration[key]; - return string.IsNullOrWhiteSpace(configurationValue) ? func() : configurationValue; - } } diff --git a/src/Contrib/Configuration/Masa.Contrib.Configuration/Options/MasaAppConfigureOptionsRelation.cs b/src/Contrib/Configuration/Masa.Contrib.Configuration/Options/MasaAppConfigureOptionsRelation.cs index a8436ff65..43fc8def5 100644 --- a/src/Contrib/Configuration/Masa.Contrib.Configuration/Options/MasaAppConfigureOptionsRelation.cs +++ b/src/Contrib/Configuration/Masa.Contrib.Configuration/Options/MasaAppConfigureOptionsRelation.cs @@ -1,15 +1,15 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.BuildingBlocks.Configuration.Options; public class MasaAppConfigureOptionsRelation { - public Dictionary Data { get; set; } + private Dictionary Data { get; set; } public MasaAppConfigureOptionsRelation() { - Data = new Dictionary() + Data = new Dictionary(StringComparer.OrdinalIgnoreCase) { { nameof(MasaAppConfigureOptions.AppId), @@ -26,4 +26,29 @@ public MasaAppConfigureOptionsRelation() } }; } + + public MasaAppConfigureOptionsRelation SetOptionsRelation(string key, string variable, string defaultValue) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentException($"{nameof(key)} cannot be empty", nameof(key)); + + if (string.IsNullOrEmpty(variable)) + throw new ArgumentException($"{nameof(variable)} cannot be empty", nameof(variable)); + + if (string.IsNullOrEmpty(defaultValue)) + throw new ArgumentException($"{nameof(defaultValue)} cannot be empty", nameof(defaultValue)); + + Data[key] = (variable, defaultValue); + return this; + } + + internal string[] GetKeys() => Data.Select(kvp => kvp.Key).ToArray(); + + internal (string Variable, string DefaultValue) GetValue(string key) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentException($"{nameof(key)} cannot be empty", nameof(key)); + + return Data[key]; + } } diff --git a/src/Contrib/Configuration/Tests/Masa.Contrib.Configuration.Tests/WebApplicationBuilderTest.cs b/src/Contrib/Configuration/Tests/Masa.Contrib.Configuration.Tests/WebApplicationBuilderTest.cs index 2698526c8..6ee7071c0 100644 --- a/src/Contrib/Configuration/Tests/Masa.Contrib.Configuration.Tests/WebApplicationBuilderTest.cs +++ b/src/Contrib/Configuration/Tests/Masa.Contrib.Configuration.Tests/WebApplicationBuilderTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.Contrib.Configuration.Tests; @@ -19,8 +19,8 @@ public void TestInitializeAppConfiguration() var serviceProvider = builder.Services.BuildServiceProvider(); var masaAppConfigureOptions = serviceProvider.GetService>()!; - Assert.IsTrue(masaAppConfigureOptions.Value.Data.Count == 3); + Assert.IsTrue(masaAppConfigureOptions.Value.Length == 3); Assert.IsTrue(masaAppConfigureOptions.Value.Environment == env); - Assert.IsTrue(masaAppConfigureOptions.Value.Data[nameof(MasaAppConfigureOptions.Environment)] == env); + Assert.IsTrue(masaAppConfigureOptions.Value.GetValue(nameof(MasaAppConfigureOptions.Environment)) == env); } } diff --git a/src/Utils/Caching/Distributed/Masa.Utils.Caching.Redis/Models/RedisServerOptions.cs b/src/Utils/Caching/Distributed/Masa.Utils.Caching.Redis/Models/RedisServerOptions.cs index 3f2204471..104e971e6 100644 --- a/src/Utils/Caching/Distributed/Masa.Utils.Caching.Redis/Models/RedisServerOptions.cs +++ b/src/Utils/Caching/Distributed/Masa.Utils.Caching.Redis/Models/RedisServerOptions.cs @@ -8,6 +8,7 @@ namespace Masa.Utils.Caching.Redis.Models; /// public class RedisServerOptions { + private const string DEFAULT_REDIS_HOST = "localhost"; private const int DEFAULT_REDIS_PORT = 6379; /// @@ -25,6 +26,8 @@ public class RedisServerOptions /// public RedisServerOptions() { + Host = DEFAULT_REDIS_HOST; + Port = DEFAULT_REDIS_PORT; } /// @@ -63,9 +66,7 @@ public RedisServerOptions(string host) public RedisServerOptions(string host, int port) { if (string.IsNullOrWhiteSpace(host)) - { throw new ArgumentNullException(nameof(host)); - } Host = host; Port = port;