Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Implement builders and immutable contexts. #77

Merged
merged 10 commits into from
Oct 6, 2022
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ OpenFeature.Instance.SetProvider(new NoOpProvider());
var client = OpenFeature.Instance.GetClient();
// Evaluation the `my-feature` feature flag
var isEnabled = await client.GetBooleanValue("my-feature", false);

// Evaluating with a context.
var evaluationContext = EvaluationContext.Builder()
.Set("my-key", "my-value")
.Build();

// Evaluation the `my-conditional` feature flag
var isEnabled = await client.GetBooleanValue("my-conditional", false, evaluationContext);
```

### Provider
Expand Down
4 changes: 4 additions & 0 deletions build/Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@
<MicrosoftExtensionsLoggerVer>[2.0,6.0)</MicrosoftExtensionsLoggerVer>
<MicrosoftSourceLinkGitHubPkgVer>[1.0.0,2.0)</MicrosoftSourceLinkGitHubPkgVer>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="[1.7.1, 7.0.0)" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion src/OpenFeatureSDK/Hook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public abstract class Hook
public virtual Task<EvaluationContext> Before<T>(HookContext<T> context,
IReadOnlyDictionary<string, object> hints = null)
{
return Task.FromResult(new EvaluationContext());
return Task.FromResult(EvaluationContext.Empty);
}

/// <summary>
Expand Down
211 changes: 35 additions & 176 deletions src/OpenFeatureSDK/Model/EvaluationContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace OpenFeatureSDK.Model
{
Expand All @@ -8,9 +9,31 @@ namespace OpenFeatureSDK.Model
/// to the feature flag evaluation context.
/// </summary>
/// <seealso href="https://github.com/open-feature/spec/blob/main/specification/evaluation-context.md">Evaluation context</seealso>
public class EvaluationContext
public sealed class EvaluationContext
benjiro marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly Structure _structure = new Structure();
private readonly Structure _structure;

/// <summary>
/// Internal constructor used by the builder.
/// </summary>
/// <param name="content">The content of the context.</param>
internal EvaluationContext(Structure content)
{
this._structure = content;
}

/// <summary>
/// Private constructor for making an empty <see cref="EvaluationContext"/>.
/// </summary>
private EvaluationContext()
{
this._structure = Structure.Empty;
}

/// <summary>
/// An empty evaluation context.
/// </summary>
public static EvaluationContext Empty { get; } = new EvaluationContext();

/// <summary>
/// Gets the Value at the specified key
Expand All @@ -35,15 +58,6 @@ public class EvaluationContext
/// </exception>
public bool ContainsKey(string key) => this._structure.ContainsKey(key);

/// <summary>
/// Removes the Value at the specified key
/// </summary>
/// <param name="key">The key of the value to be removed</param>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
public void Remove(string key) => this._structure.Remove(key);

/// <summary>
/// Gets the value associated with the specified key
/// </summary>
Expand All @@ -59,153 +73,9 @@ public class EvaluationContext
/// Gets all values as a Dictionary
/// </summary>
/// <returns>New <see cref="IDictionary{TKey,TValue}"/> representation of this Structure</returns>
public IDictionary<string, Value> AsDictionary()
{
return new Dictionary<string, Value>(this._structure.AsDictionary());
}

/// <summary>
/// Add a new bool Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, bool value)
public IImmutableDictionary<string, Value> AsDictionary()
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new string Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, string value)
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new int Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, int value)
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new double Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, double value)
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new DateTime Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, DateTime value)
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new Structure Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, Structure value)
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new List Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, List<Value> value)
{
this._structure.Add(key, value);
return this;
}

/// <summary>
/// Add a new Value to the evaluation context
/// </summary>
/// <param name="key">The key of the value to be added</param>
/// <param name="value">The value to be added</param>
/// <returns>This <see cref="EvaluationContext"/></returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the key is <see langword="null" />
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when an element with the same key is already contained in the context
/// </exception>
public EvaluationContext Add(string key, Value value)
{
this._structure.Add(key, value);
return this;
return this._structure.AsDictionary();
}

/// <summary>
Expand All @@ -214,32 +84,21 @@ public EvaluationContext Add(string key, Value value)
public int Count => this._structure.Count;

/// <summary>
/// Merges provided evaluation context into this one.
/// Any duplicate keys will be overwritten.
/// Return an enumerator for all values
/// </summary>
/// <param name="other"><see cref="EvaluationContext"/></param>
public void Merge(EvaluationContext other)
/// <returns>An enumerator for all values</returns>
public IEnumerator<KeyValuePair<string, Value>> GetEnumerator()
{
foreach (var key in other._structure.Keys)
{
if (this._structure.ContainsKey(key))
{
this._structure[key] = other._structure[key];
}
else
{
this._structure.Add(key, other._structure[key]);
}
}
return this._structure.GetEnumerator();
}

/// <summary>
/// Return an enumerator for all values
/// Get a builder which can build an <see cref="EvaluationContext"/>.
/// </summary>
/// <returns>An enumerator for all values</returns>
public IEnumerator<KeyValuePair<string, Value>> GetEnumerator()
/// <returns>The builder</returns>
public static EvaluationContextBuilder Builder()
{
return this._structure.GetEnumerator();
return new EvaluationContextBuilder();
}
}
}
Loading