diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b9d789..c3ef4951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +### 7.2.0 + +- Introduced a new local dictionary override factory method: + ```csharp + FlagOverrides LocalDictionary(IDictionary dictionary, bool watchChanges, OverrideBehaviour overrideBehaviour) + ``` + Where the `watchChanges` parameter indicates whether the SDK should rebuild the overrides upon each read to keep track of the source dictionary's changes. + ### 7.1.0 - Add new evaluation methods `GetAllValueDetails`/`GetAllValueDetailsAsync`. diff --git a/appveyor.yml b/appveyor.yml index a76bc8c5..ab9b13d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ environment: - build_version: 7.1.0 + build_version: 7.2.0 version: $(build_version)-{build} image: Visual Studio 2022 configuration: Release diff --git a/src/ConfigCat.Client.Tests/OverrideTests.cs b/src/ConfigCat.Client.Tests/OverrideTests.cs index 7e7f6aba..a7b46e43 100644 --- a/src/ConfigCat.Client.Tests/OverrideTests.cs +++ b/src/ConfigCat.Client.Tests/OverrideTests.cs @@ -303,6 +303,54 @@ public void LocalOnly() Assert.IsNull(refreshResult.ErrorException); } + [TestMethod] + public void LocalOnly_Watch() + { + var dict = new Dictionary + { + {"fakeKey", "test1"}, + }; + +#pragma warning disable CS0618 // Type or member is obsolete + using var client = new ConfigCatClient(options => + { + options.SdkKey = "localhost"; + options.FlagOverrides = FlagOverrides.LocalDictionary(dict, true, OverrideBehaviour.LocalOnly); + options.PollingMode = PollingModes.ManualPoll; + }); +#pragma warning restore CS0618 // Type or member is obsolete + + Assert.AreEqual("test1", client.GetValue("fakeKey", string.Empty)); + + dict["fakeKey"] = "test2"; + + Assert.AreEqual("test2", client.GetValue("fakeKey", string.Empty)); + } + + [TestMethod] + public async Task LocalOnly_Async_Watch() + { + var dict = new Dictionary + { + {"fakeKey", "test1"}, + }; + +#pragma warning disable CS0618 // Type or member is obsolete + using var client = new ConfigCatClient(options => + { + options.SdkKey = "localhost"; + options.FlagOverrides = FlagOverrides.LocalDictionary(dict, true, OverrideBehaviour.LocalOnly); + options.PollingMode = PollingModes.ManualPoll; + }); +#pragma warning restore CS0618 // Type or member is obsolete + + Assert.AreEqual("test1", await client.GetValueAsync("fakeKey", string.Empty)); + + dict["fakeKey"] = "test2"; + + Assert.AreEqual("test2", await client.GetValueAsync("fakeKey", string.Empty)); + } + [TestMethod] public async Task LocalOnly_Async() { diff --git a/src/ConfigCatClient/Override/FlagOverrides.cs b/src/ConfigCatClient/Override/FlagOverrides.cs index 4d336c87..643334a4 100644 --- a/src/ConfigCatClient/Override/FlagOverrides.cs +++ b/src/ConfigCatClient/Override/FlagOverrides.cs @@ -21,9 +21,10 @@ private FlagOverrides(string filePath, bool autoReload, OverrideBehaviour overri this.OverrideBehaviour = overrideBehaviour; } - private FlagOverrides(IDictionary dictionary, OverrideBehaviour overrideBehaviour) + private FlagOverrides(IDictionary dictionary, bool watchChanges, OverrideBehaviour overrideBehaviour) { this.dictionary = dictionary; + this.autoReload = watchChanges; this.OverrideBehaviour = overrideBehaviour; } @@ -37,7 +38,7 @@ private FlagOverrides(IDictionary dictionary, OverrideBehaviour internal IOverrideDataSource BuildDataSource(LoggerWrapper logger) { if (this.dictionary != null) - return new LocalDictionaryDataSource(this.dictionary); + return new LocalDictionaryDataSource(this.dictionary, this.autoReload); if (this.filePath != null) return new LocalFileDataSource(this.filePath, this.autoReload, logger); @@ -53,7 +54,7 @@ internal IOverrideDataSource BuildDataSource(LoggerWrapper logger) /// the override behaviour. It can be used to set preference on whether the local values should override the remote values, or use local values only when a remote value doesn't exist, or use it for local only mode. /// The override descriptor. public static FlagOverrides LocalFile(string filePath, bool autoReload, OverrideBehaviour overrideBehaviour) => - new FlagOverrides(filePath, autoReload, overrideBehaviour); + new(filePath, autoReload, overrideBehaviour); /// /// Creates an override descriptor that uses a dictionary data source. @@ -62,6 +63,16 @@ public static FlagOverrides LocalFile(string filePath, bool autoReload, Override /// the override behaviour. It can be used to set preference on whether the local values should override the remote values, or use local values only when a remote value doesn't exist, or use it for local only mode. /// The override descriptor. public static FlagOverrides LocalDictionary(IDictionary dictionary, OverrideBehaviour overrideBehaviour) => - new FlagOverrides(dictionary, overrideBehaviour); + new(dictionary, false, overrideBehaviour); + + /// + /// Creates an override descriptor that uses a dictionary data source. + /// + /// Dictionary that contains the overrides. + /// Indicates whether the SDK should track the input dictionary for changes. + /// the override behaviour. It can be used to set preference on whether the local values should override the remote values, or use local values only when a remote value doesn't exist, or use it for local only mode. + /// The override descriptor. + public static FlagOverrides LocalDictionary(IDictionary dictionary, bool watchChanges, OverrideBehaviour overrideBehaviour) => + new(dictionary, watchChanges, overrideBehaviour); } } diff --git a/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs b/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs index ae328b32..deb63f7f 100644 --- a/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs +++ b/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs @@ -8,17 +8,26 @@ namespace ConfigCat.Client.Override { internal sealed class LocalDictionaryDataSource : IOverrideDataSource { - private readonly IDictionary settings; + private readonly IDictionary initialSettings; + private readonly IDictionary overrideValues; - public LocalDictionaryDataSource(IDictionary overrideValues) + public LocalDictionaryDataSource(IDictionary overrideValues, bool watchChanges) { - this.settings = overrideValues.ToDictionary(kv => kv.Key, kv => kv.Value.ToSetting()); + this.initialSettings = overrideValues.ToDictionary(kv => kv.Key, kv => kv.Value.ToSetting()); + if (watchChanges) + { + this.overrideValues = overrideValues; + } } - public IDictionary GetOverrides() => this.settings; + public IDictionary GetOverrides() => GetSettingsFromSource(); - public Task> GetOverridesAsync() => Task.FromResult(this.settings); + public Task> GetOverridesAsync() => Task.FromResult(GetSettingsFromSource()); public void Dispose() { /* no need to dispose anything */ } + + private IDictionary GetSettingsFromSource() => overrideValues != null + ? overrideValues.ToDictionary(kv => kv.Key, kv => kv.Value.ToSetting()) + : this.initialSettings; } }