Skip to content

Commit

Permalink
Track the changes of the source override dictionary (#60)
Browse files Browse the repository at this point in the history
* Track the source override dictionary changes

* PR review suggestions

* Bump version & update changelog
  • Loading branch information
z4kn4fein authored Jan 11, 2023
1 parent 1601361 commit a9a2210
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 10 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
### 7.2.0

- Introduced a new local dictionary override factory method:
```csharp
FlagOverrides LocalDictionary(IDictionary<string, object> 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`.
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
48 changes: 48 additions & 0 deletions src/ConfigCat.Client.Tests/OverrideTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,54 @@ public void LocalOnly()
Assert.IsNull(refreshResult.ErrorException);
}

[TestMethod]
public void LocalOnly_Watch()
{
var dict = new Dictionary<string, object>
{
{"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<string, object>
{
{"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()
{
Expand Down
19 changes: 15 additions & 4 deletions src/ConfigCatClient/Override/FlagOverrides.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ private FlagOverrides(string filePath, bool autoReload, OverrideBehaviour overri
this.OverrideBehaviour = overrideBehaviour;
}

private FlagOverrides(IDictionary<string, object> dictionary, OverrideBehaviour overrideBehaviour)
private FlagOverrides(IDictionary<string, object> dictionary, bool watchChanges, OverrideBehaviour overrideBehaviour)
{
this.dictionary = dictionary;
this.autoReload = watchChanges;
this.OverrideBehaviour = overrideBehaviour;
}

Expand All @@ -37,7 +38,7 @@ private FlagOverrides(IDictionary<string, object> 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);
Expand All @@ -53,7 +54,7 @@ internal IOverrideDataSource BuildDataSource(LoggerWrapper logger)
/// <param name="overrideBehaviour">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.</param>
/// <returns>The override descriptor.</returns>
public static FlagOverrides LocalFile(string filePath, bool autoReload, OverrideBehaviour overrideBehaviour) =>
new FlagOverrides(filePath, autoReload, overrideBehaviour);
new(filePath, autoReload, overrideBehaviour);

/// <summary>
/// Creates an override descriptor that uses a dictionary data source.
Expand All @@ -62,6 +63,16 @@ public static FlagOverrides LocalFile(string filePath, bool autoReload, Override
/// <param name="overrideBehaviour">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.</param>
/// <returns>The override descriptor.</returns>
public static FlagOverrides LocalDictionary(IDictionary<string, object> dictionary, OverrideBehaviour overrideBehaviour) =>
new FlagOverrides(dictionary, overrideBehaviour);
new(dictionary, false, overrideBehaviour);

/// <summary>
/// Creates an override descriptor that uses a dictionary data source.
/// </summary>
/// <param name="dictionary">Dictionary that contains the overrides.</param>
/// <param name="watchChanges">Indicates whether the SDK should track the input dictionary for changes.</param>
/// <param name="overrideBehaviour">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.</param>
/// <returns>The override descriptor.</returns>
public static FlagOverrides LocalDictionary(IDictionary<string, object> dictionary, bool watchChanges, OverrideBehaviour overrideBehaviour) =>
new(dictionary, watchChanges, overrideBehaviour);
}
}
19 changes: 14 additions & 5 deletions src/ConfigCatClient/Override/LocalDictionaryDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,26 @@ namespace ConfigCat.Client.Override
{
internal sealed class LocalDictionaryDataSource : IOverrideDataSource
{
private readonly IDictionary<string, Setting> settings;
private readonly IDictionary<string, Setting> initialSettings;
private readonly IDictionary<string, object> overrideValues;

public LocalDictionaryDataSource(IDictionary<string, object> overrideValues)
public LocalDictionaryDataSource(IDictionary<string, object> 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<string, Setting> GetOverrides() => this.settings;
public IDictionary<string, Setting> GetOverrides() => GetSettingsFromSource();

public Task<IDictionary<string, Setting>> GetOverridesAsync() => Task.FromResult(this.settings);
public Task<IDictionary<string, Setting>> GetOverridesAsync() => Task.FromResult(GetSettingsFromSource());

public void Dispose() { /* no need to dispose anything */ }

private IDictionary<string, Setting> GetSettingsFromSource() => overrideValues != null
? overrideValues.ToDictionary(kv => kv.Key, kv => kv.Value.ToSetting())
: this.initialSettings;
}
}

0 comments on commit a9a2210

Please sign in to comment.