diff --git a/README.md b/README.md
index a6487cf8..61a87c52 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/build/Common.props b/build/Common.props
index aae4ba90..49ac8adf 100644
--- a/build/Common.props
+++ b/build/Common.props
@@ -21,4 +21,8 @@
[2.0,6.0)[1.0.0,2.0)
+
+
+
+
diff --git a/src/OpenFeatureSDK/Hook.cs b/src/OpenFeatureSDK/Hook.cs
index 5a478e20..12949f0a 100644
--- a/src/OpenFeatureSDK/Hook.cs
+++ b/src/OpenFeatureSDK/Hook.cs
@@ -31,7 +31,7 @@ public abstract class Hook
public virtual Task Before(HookContext context,
IReadOnlyDictionary hints = null)
{
- return Task.FromResult(new EvaluationContext());
+ return Task.FromResult(EvaluationContext.Empty);
}
///
diff --git a/src/OpenFeatureSDK/Model/EvaluationContext.cs b/src/OpenFeatureSDK/Model/EvaluationContext.cs
index c50561e6..3d10e81b 100644
--- a/src/OpenFeatureSDK/Model/EvaluationContext.cs
+++ b/src/OpenFeatureSDK/Model/EvaluationContext.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
namespace OpenFeatureSDK.Model
{
@@ -8,9 +9,31 @@ namespace OpenFeatureSDK.Model
/// to the feature flag evaluation context.
///
/// Evaluation context
- public class EvaluationContext
+ public sealed class EvaluationContext
{
- private readonly Structure _structure = new Structure();
+ private readonly Structure _structure;
+
+ ///
+ /// Internal constructor used by the builder.
+ ///
+ /// The content of the context.
+ internal EvaluationContext(Structure content)
+ {
+ this._structure = content;
+ }
+
+ ///
+ /// Private constructor for making an empty .
+ ///
+ private EvaluationContext()
+ {
+ this._structure = Structure.Empty;
+ }
+
+ ///
+ /// An empty evaluation context.
+ ///
+ public static EvaluationContext Empty { get; } = new EvaluationContext();
///
/// Gets the Value at the specified key
@@ -35,15 +58,6 @@ public class EvaluationContext
///
public bool ContainsKey(string key) => this._structure.ContainsKey(key);
- ///
- /// Removes the Value at the specified key
- ///
- /// The key of the value to be removed
- ///
- /// Thrown when the key is
- ///
- public void Remove(string key) => this._structure.Remove(key);
-
///
/// Gets the value associated with the specified key
///
@@ -59,153 +73,9 @@ public class EvaluationContext
/// Gets all values as a Dictionary
///
/// New representation of this Structure
- public IDictionary AsDictionary()
- {
- return new Dictionary(this._structure.AsDictionary());
- }
-
- ///
- /// Add a new bool Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, bool value)
+ public IImmutableDictionary AsDictionary()
{
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new string Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, string value)
- {
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new int Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, int value)
- {
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new double Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, double value)
- {
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new DateTime Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, DateTime value)
- {
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new Structure Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, Structure value)
- {
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new List Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, List value)
- {
- this._structure.Add(key, value);
- return this;
- }
-
- ///
- /// Add a new Value to the evaluation context
- ///
- /// The key of the value to be added
- /// The value to be added
- /// This
- ///
- /// Thrown when the key is
- ///
- ///
- /// Thrown when an element with the same key is already contained in the context
- ///
- public EvaluationContext Add(string key, Value value)
- {
- this._structure.Add(key, value);
- return this;
+ return this._structure.AsDictionary();
}
///
@@ -214,32 +84,21 @@ public EvaluationContext Add(string key, Value value)
public int Count => this._structure.Count;
///
- /// Merges provided evaluation context into this one.
- /// Any duplicate keys will be overwritten.
+ /// Return an enumerator for all values
///
- ///
- public void Merge(EvaluationContext other)
+ /// An enumerator for all values
+ public IEnumerator> 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();
}
///
- /// Return an enumerator for all values
+ /// Get a builder which can build an .
///
- /// An enumerator for all values
- public IEnumerator> GetEnumerator()
+ /// The builder
+ public static EvaluationContextBuilder Builder()
{
- return this._structure.GetEnumerator();
+ return new EvaluationContextBuilder();
}
}
}
diff --git a/src/OpenFeatureSDK/Model/EvaluationContextBuilder.cs b/src/OpenFeatureSDK/Model/EvaluationContextBuilder.cs
new file mode 100644
index 00000000..f5c88025
--- /dev/null
+++ b/src/OpenFeatureSDK/Model/EvaluationContextBuilder.cs
@@ -0,0 +1,145 @@
+using System;
+
+namespace OpenFeatureSDK.Model
+{
+ ///
+ /// A builder which allows the specification of attributes for an .
+ ///
+ /// A object is intended for use by a single thread and should not be used
+ /// from multiple threads. Once an has been created it is immutable and safe for use
+ /// from multiple threads.
+ ///
+ ///
+ public sealed class EvaluationContextBuilder
+ {
+ private readonly StructureBuilder _attributes = Structure.Builder();
+
+ ///
+ /// Internal to only allow direct creation by .
+ ///
+ internal EvaluationContextBuilder() { }
+
+ ///
+ /// Set the key to the given .
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, Value value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given string.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, string value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given int.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, int value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given double.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, double value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given long.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, long value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given bool.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, bool value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given .
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, Structure value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given DateTime.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public EvaluationContextBuilder Set(string key, DateTime value)
+ {
+ this._attributes.Set(key, value);
+ return this;
+ }
+
+ ///
+ /// Incorporate an existing context into the builder.
+ ///
+ /// Any existing keys in the builder will be replaced by keys in the context.
+ ///
+ ///
+ /// The context to add merge
+ /// This builder
+ public EvaluationContextBuilder Merge(EvaluationContext context)
+ {
+ foreach (var kvp in context)
+ {
+ this.Set(kvp.Key, kvp.Value);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Build an immutable .
+ ///
+ /// An immutable
+ public EvaluationContext Build()
+ {
+ return new EvaluationContext(this._attributes.Build());
+ }
+ }
+}
diff --git a/src/OpenFeatureSDK/Model/HookContext.cs b/src/OpenFeatureSDK/Model/HookContext.cs
index d97b8333..329ca180 100644
--- a/src/OpenFeatureSDK/Model/HookContext.cs
+++ b/src/OpenFeatureSDK/Model/HookContext.cs
@@ -65,5 +65,17 @@ public HookContext(string flagKey,
this.ProviderMetadata = providerMetadata ?? throw new ArgumentNullException(nameof(providerMetadata));
this.EvaluationContext = evaluationContext ?? throw new ArgumentNullException(nameof(evaluationContext));
}
+
+ internal HookContext WithNewEvaluationContext(EvaluationContext context)
+ {
+ return new HookContext(
+ this.FlagKey,
+ this.DefaultValue,
+ this.FlagValueType,
+ this.ClientMetadata,
+ this.ProviderMetadata,
+ context
+ );
+ }
}
}
diff --git a/src/OpenFeatureSDK/Model/Structure.cs b/src/OpenFeatureSDK/Model/Structure.cs
index eec68240..c2d0ba8d 100644
--- a/src/OpenFeatureSDK/Model/Structure.cs
+++ b/src/OpenFeatureSDK/Model/Structure.cs
@@ -1,6 +1,6 @@
-using System;
using System.Collections;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
namespace OpenFeatureSDK.Model
@@ -8,25 +8,38 @@ namespace OpenFeatureSDK.Model
///
/// Structure represents a map of Values
///
- public class Structure : IEnumerable>
+ public sealed class Structure : IEnumerable>
{
- private readonly Dictionary _attributes;
+ private readonly ImmutableDictionary _attributes;
///
- /// Creates a new structure with an empty set of attributes
+ /// Internal constructor for use by the builder.
///
- public Structure()
+ internal Structure(ImmutableDictionary attributes)
{
- this._attributes = new Dictionary();
+ this._attributes = attributes;
}
+ ///
+ /// Private constructor for creating an empty .
+ ///
+ private Structure()
+ {
+ this._attributes = ImmutableDictionary.Empty;
+ }
+
+ ///
+ /// An empty structure.
+ ///
+ public static Structure Empty { get; } = new Structure();
+
///
/// Creates a new structure with the supplied attributes
///
///
public Structure(IDictionary attributes)
{
- this._attributes = new Dictionary(attributes);
+ this._attributes = ImmutableDictionary.CreateRange(attributes);
}
///
@@ -43,13 +56,6 @@ public Structure(IDictionary attributes)
/// indicating the presence of the key.
public bool ContainsKey(string key) => this._attributes.ContainsKey(key);
- ///
- /// Removes the Value at the specified key
- ///
- /// The key of the value to be retrieved
- /// indicating the presence of the key.
- public bool Remove(string key) => this._attributes.Remove(key);
-
///
/// Gets the value associated with the specified key by mutating the supplied value.
///
@@ -62,9 +68,9 @@ public Structure(IDictionary attributes)
/// Gets all values as a Dictionary
///
/// New representation of this Structure
- public IDictionary AsDictionary()
+ public IImmutableDictionary AsDictionary()
{
- return new Dictionary(this._attributes);
+ return this._attributes;
}
///
@@ -74,114 +80,17 @@ public IDictionary AsDictionary()
public Value this[string key]
{
get => this._attributes[key];
- set => this._attributes[key] = value;
- }
-
- ///
- /// Return a collection containing all the keys in this structure
- ///
- public ICollection Keys => this._attributes.Keys;
-
- ///
- /// Return a collection containing all the values in this structure
- ///
- public ICollection Values => this._attributes.Values;
-
- ///
- /// Add a new bool Value to the structure
- ///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, bool value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
-
- ///
- /// Add a new string Value to the structure
- ///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, string value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
-
- ///
- /// Add a new int Value to the structure
- ///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, int value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
}
///
- /// Add a new double Value to the structure
+ /// Return a list containing all the keys in this structure
///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, double value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
+ public IImmutableList Keys => this._attributes.Keys.ToImmutableList();
///
- /// Add a new DateTime Value to the structure
+ /// Return an enumerable containing all the values in this structure
///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, DateTime value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
-
- ///
- /// Add a new Structure Value to the structure
- ///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, Structure value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
-
- ///
- /// Add a new List Value to the structure
- ///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, IList value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
-
- ///
- /// Add a new Value to the structure
- ///
- /// The key of the value to be retrieved
- /// The value to be added
- /// This
- public Structure Add(string key, Value value)
- {
- this._attributes.Add(key, new Value(value));
- return this;
- }
+ public IImmutableList Values => this._attributes.Values.ToImmutableList();
///
/// Return a count of all values
@@ -197,6 +106,15 @@ public IEnumerator> GetEnumerator()
return this._attributes.GetEnumerator();
}
+ ///
+ /// Get a builder which can build a .
+ ///
+ /// The builder
+ public static StructureBuilder Builder()
+ {
+ return new StructureBuilder();
+ }
+
[ExcludeFromCodeCoverage]
IEnumerator IEnumerable.GetEnumerator()
{
diff --git a/src/OpenFeatureSDK/Model/StructureBuilder.cs b/src/OpenFeatureSDK/Model/StructureBuilder.cs
new file mode 100644
index 00000000..5fba1422
--- /dev/null
+++ b/src/OpenFeatureSDK/Model/StructureBuilder.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace OpenFeatureSDK.Model
+{
+ ///
+ /// A builder which allows the specification of attributes for a .
+ ///
+ /// A object is intended for use by a single thread and should not be used from
+ /// multiple threads. Once a has been created it is immutable and safe for use from
+ /// multiple threads.
+ ///
+ ///
+ public sealed class StructureBuilder
+ {
+ private readonly ImmutableDictionary.Builder _attributes =
+ ImmutableDictionary.CreateBuilder();
+
+ ///
+ /// Internal to only allow direct creation by .
+ ///
+ internal StructureBuilder() { }
+
+ ///
+ /// Set the key to the given .
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, Value value)
+ {
+ // Remove the attribute. Will not throw an exception if not present.
+ this._attributes.Remove(key);
+ this._attributes.Add(key, value);
+ return this;
+ }
+
+ ///
+ /// Set the key to the given string.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, string value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given int.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, int value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given double.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, double value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given long.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, long value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given bool.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, bool value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given .
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, Structure value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given DateTime.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, DateTime value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Set the key to the given list.
+ ///
+ /// The key for the value
+ /// The value to set
+ /// This builder
+ public StructureBuilder Set(string key, IList value)
+ {
+ this.Set(key, new Value(value));
+ return this;
+ }
+
+ ///
+ /// Build an immutable /
+ ///
+ /// The built
+ public Structure Build()
+ {
+ return new Structure(this._attributes.ToImmutable());
+ }
+ }
+}
diff --git a/src/OpenFeatureSDK/Model/Value.cs b/src/OpenFeatureSDK/Model/Value.cs
index 308c698a..10ce3c7f 100644
--- a/src/OpenFeatureSDK/Model/Value.cs
+++ b/src/OpenFeatureSDK/Model/Value.cs
@@ -1,6 +1,6 @@
using System;
-using System.Collections;
using System.Collections.Generic;
+using System.Collections.Immutable;
namespace OpenFeatureSDK.Model
{
@@ -8,7 +8,7 @@ namespace OpenFeatureSDK.Model
/// Values serve as a return type for provider objects. Providers may deal in JSON, protobuf, XML or some other data-interchange format.
/// This intermediate representation provides a good medium of exchange.
///
- public class Value
+ public sealed class Value
{
private readonly object _innerValue;
@@ -23,6 +23,10 @@ public class Value
/// The object to set as the inner value
public Value(Object value)
{
+ if (value is IList list)
+ {
+ value = list.ToImmutableList();
+ }
// integer is a special case, convert those.
this._innerValue = value is int ? Convert.ToDouble(value) : value;
if (!(this.IsNull
@@ -77,8 +81,8 @@ public Value(Object value)
///
/// Creates a Value with the inner set to list type
///
- /// List type
- public Value(IList value) => this._innerValue = value;
+ /// List type
+ public Value(IList value) => this._innerValue = value.ToImmutableList();
///
/// Creates a Value with the inner set to DateTime type
@@ -120,7 +124,7 @@ public Value(Object value)
/// Determines if inner value is list
///
/// True if value is list
- public bool IsList => this._innerValue is IList;
+ public bool IsList => this._innerValue is IImmutableList;
///
/// Determines if inner value is DateTime
@@ -174,7 +178,7 @@ public Value(Object value)
/// Value will be null if it isn't a List
///
/// Value as List
- public IList AsList => this.IsList ? (IList)this._innerValue : null;
+ public IImmutableList AsList => this.IsList ? (IImmutableList)this._innerValue : null;
///
/// Returns the underlying DateTime value
diff --git a/src/OpenFeatureSDK/OpenFeature.cs b/src/OpenFeatureSDK/OpenFeature.cs
index 8ea95048..42b23dcf 100644
--- a/src/OpenFeatureSDK/OpenFeature.cs
+++ b/src/OpenFeatureSDK/OpenFeature.cs
@@ -11,7 +11,7 @@ namespace OpenFeatureSDK
///
public sealed class OpenFeature
{
- private EvaluationContext _evaluationContext = new EvaluationContext();
+ private EvaluationContext _evaluationContext = EvaluationContext.Empty;
private FeatureProvider _featureProvider = new NoOpFeatureProvider();
private readonly List _hooks = new List();
@@ -82,7 +82,7 @@ public FeatureClient GetClient(string name = null, string version = null, ILogge
/// Sets the global
///
///
- public void SetContext(EvaluationContext context) => this._evaluationContext = context ?? new EvaluationContext();
+ public void SetContext(EvaluationContext context) => this._evaluationContext = context ?? EvaluationContext.Empty;
///
/// Gets the global
diff --git a/src/OpenFeatureSDK/OpenFeatureClient.cs b/src/OpenFeatureSDK/OpenFeatureClient.cs
index a105644a..3d4120b7 100644
--- a/src/OpenFeatureSDK/OpenFeatureClient.cs
+++ b/src/OpenFeatureSDK/OpenFeatureClient.cs
@@ -47,7 +47,7 @@ public FeatureClient(FeatureProvider featureProvider, string name, string versio
this._featureProvider = featureProvider ?? throw new ArgumentNullException(nameof(featureProvider));
this._metadata = new ClientMetadata(name, version);
this._logger = logger ?? new Logger(new NullLoggerFactory());
- this._evaluationContext = context ?? new EvaluationContext();
+ this._evaluationContext = context ?? EvaluationContext.Empty;
}
///
@@ -213,13 +213,15 @@ private async Task> EvaluateFlag(
// New up a evaluation context if one was not provided.
if (context == null)
{
- context = new EvaluationContext();
+ context = EvaluationContext.Empty;
}
// merge api, client, and invocation context.
var evaluationContext = OpenFeature.Instance.GetContext();
- evaluationContext.Merge(this.GetContext());
- evaluationContext.Merge(context);
+ var evaluationContextBuilder = EvaluationContext.Builder();
+ evaluationContextBuilder.Merge(evaluationContext);
+ evaluationContextBuilder.Merge(this.GetContext());
+ evaluationContextBuilder.Merge(context);
var allHooks = new List()
.Concat(OpenFeature.Instance.GetHooks())
@@ -240,16 +242,16 @@ private async Task> EvaluateFlag(
defaultValue,
flagValueType, this._metadata,
OpenFeature.Instance.GetProviderMetadata(),
- evaluationContext
+ evaluationContextBuilder.Build()
);
FlagEvaluationDetails evaluation;
try
{
- await this.TriggerBeforeHooks(allHooks, hookContext, options);
+ var contextFromHooks = await this.TriggerBeforeHooks(allHooks, hookContext, options);
evaluation =
- (await resolveValueDelegate.Invoke(flagKey, defaultValue, hookContext.EvaluationContext))
+ (await resolveValueDelegate.Invoke(flagKey, defaultValue, contextFromHooks.EvaluationContext))
.ToFlagEvaluationDetails();
await this.TriggerAfterHooks(allHooksReversed, hookContext, evaluation, options);
@@ -277,15 +279,19 @@ private async Task> EvaluateFlag(
return evaluation;
}
- private async Task TriggerBeforeHooks(IReadOnlyList hooks, HookContext context,
+ private async Task> TriggerBeforeHooks(IReadOnlyList hooks, HookContext context,
FlagEvaluationOptions options)
{
+ var evalContextBuilder = EvaluationContext.Builder();
+ evalContextBuilder.Merge(context.EvaluationContext);
+
foreach (var hook in hooks)
{
var resp = await hook.Before(context, options?.HookHints);
if (resp != null)
{
- context.EvaluationContext.Merge(resp);
+ evalContextBuilder.Merge(resp);
+ context = context.WithNewEvaluationContext(evalContextBuilder.Build());
}
else
{
@@ -293,6 +299,8 @@ private async Task TriggerBeforeHooks(IReadOnlyList hooks, HookContext<
hook.GetType().Name);
}
}
+
+ return context.WithNewEvaluationContext(evalContextBuilder.Build());
}
private async Task TriggerAfterHooks(IReadOnlyList hooks, HookContext context,
diff --git a/test/OpenFeatureSDK.Tests/OpenFeatureClientTests.cs b/test/OpenFeatureSDK.Tests/OpenFeatureClientTests.cs
index 45a19200..6babbdf5 100644
--- a/test/OpenFeatureSDK.Tests/OpenFeatureClientTests.cs
+++ b/test/OpenFeatureSDK.Tests/OpenFeatureClientTests.cs
@@ -7,7 +7,6 @@
using Moq;
using OpenFeatureSDK.Constant;
using OpenFeatureSDK.Error;
-using OpenFeatureSDK.Extension;
using OpenFeatureSDK.Model;
using OpenFeatureSDK.Tests.Internal;
using Xunit;
@@ -75,24 +74,24 @@ public async Task OpenFeatureClient_Should_Allow_Flag_Evaluation()
var client = OpenFeature.Instance.GetClient(clientName, clientVersion);
(await client.GetBooleanValue(flagName, defaultBoolValue)).Should().Be(defaultBoolValue);
- (await client.GetBooleanValue(flagName, defaultBoolValue, new EvaluationContext())).Should().Be(defaultBoolValue);
- (await client.GetBooleanValue(flagName, defaultBoolValue, new EvaluationContext(), emptyFlagOptions)).Should().Be(defaultBoolValue);
+ (await client.GetBooleanValue(flagName, defaultBoolValue, EvaluationContext.Empty)).Should().Be(defaultBoolValue);
+ (await client.GetBooleanValue(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultBoolValue);
(await client.GetIntegerValue(flagName, defaultIntegerValue)).Should().Be(defaultIntegerValue);
- (await client.GetIntegerValue(flagName, defaultIntegerValue, new EvaluationContext())).Should().Be(defaultIntegerValue);
- (await client.GetIntegerValue(flagName, defaultIntegerValue, new EvaluationContext(), emptyFlagOptions)).Should().Be(defaultIntegerValue);
+ (await client.GetIntegerValue(flagName, defaultIntegerValue, EvaluationContext.Empty)).Should().Be(defaultIntegerValue);
+ (await client.GetIntegerValue(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultIntegerValue);
(await client.GetDoubleValue(flagName, defaultDoubleValue)).Should().Be(defaultDoubleValue);
- (await client.GetDoubleValue(flagName, defaultDoubleValue, new EvaluationContext())).Should().Be(defaultDoubleValue);
- (await client.GetDoubleValue(flagName, defaultDoubleValue, new EvaluationContext(), emptyFlagOptions)).Should().Be(defaultDoubleValue);
+ (await client.GetDoubleValue(flagName, defaultDoubleValue, EvaluationContext.Empty)).Should().Be(defaultDoubleValue);
+ (await client.GetDoubleValue(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultDoubleValue);
(await client.GetStringValue(flagName, defaultStringValue)).Should().Be(defaultStringValue);
- (await client.GetStringValue(flagName, defaultStringValue, new EvaluationContext())).Should().Be(defaultStringValue);
- (await client.GetStringValue(flagName, defaultStringValue, new EvaluationContext(), emptyFlagOptions)).Should().Be(defaultStringValue);
+ (await client.GetStringValue(flagName, defaultStringValue, EvaluationContext.Empty)).Should().Be(defaultStringValue);
+ (await client.GetStringValue(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultStringValue);
(await client.GetObjectValue(flagName, defaultStructureValue)).Should().BeEquivalentTo(defaultStructureValue);
- (await client.GetObjectValue(flagName, defaultStructureValue, new EvaluationContext())).Should().BeEquivalentTo(defaultStructureValue);
- (await client.GetObjectValue(flagName, defaultStructureValue, new EvaluationContext(), emptyFlagOptions)).Should().BeEquivalentTo(defaultStructureValue);
+ (await client.GetObjectValue(flagName, defaultStructureValue, EvaluationContext.Empty)).Should().BeEquivalentTo(defaultStructureValue);
+ (await client.GetObjectValue(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(defaultStructureValue);
}
[Fact]
@@ -122,28 +121,28 @@ public async Task OpenFeatureClient_Should_Allow_Details_Flag_Evaluation()
var boolFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultBoolValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant);
(await client.GetBooleanDetails(flagName, defaultBoolValue)).Should().BeEquivalentTo(boolFlagEvaluationDetails);
- (await client.GetBooleanDetails(flagName, defaultBoolValue, new EvaluationContext())).Should().BeEquivalentTo(boolFlagEvaluationDetails);
- (await client.GetBooleanDetails(flagName, defaultBoolValue, new EvaluationContext(), emptyFlagOptions)).Should().BeEquivalentTo(boolFlagEvaluationDetails);
+ (await client.GetBooleanDetails(flagName, defaultBoolValue, EvaluationContext.Empty)).Should().BeEquivalentTo(boolFlagEvaluationDetails);
+ (await client.GetBooleanDetails(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(boolFlagEvaluationDetails);
var integerFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultIntegerValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant);
(await client.GetIntegerDetails(flagName, defaultIntegerValue)).Should().BeEquivalentTo(integerFlagEvaluationDetails);
- (await client.GetIntegerDetails(flagName, defaultIntegerValue, new EvaluationContext())).Should().BeEquivalentTo(integerFlagEvaluationDetails);
- (await client.GetIntegerDetails(flagName, defaultIntegerValue, new EvaluationContext(), emptyFlagOptions)).Should().BeEquivalentTo(integerFlagEvaluationDetails);
+ (await client.GetIntegerDetails(flagName, defaultIntegerValue, EvaluationContext.Empty)).Should().BeEquivalentTo(integerFlagEvaluationDetails);
+ (await client.GetIntegerDetails(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(integerFlagEvaluationDetails);
var doubleFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultDoubleValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant);
(await client.GetDoubleDetails(flagName, defaultDoubleValue)).Should().BeEquivalentTo(doubleFlagEvaluationDetails);
- (await client.GetDoubleDetails(flagName, defaultDoubleValue, new EvaluationContext())).Should().BeEquivalentTo(doubleFlagEvaluationDetails);
- (await client.GetDoubleDetails(flagName, defaultDoubleValue, new EvaluationContext(), emptyFlagOptions)).Should().BeEquivalentTo(doubleFlagEvaluationDetails);
+ (await client.GetDoubleDetails(flagName, defaultDoubleValue, EvaluationContext.Empty)).Should().BeEquivalentTo(doubleFlagEvaluationDetails);
+ (await client.GetDoubleDetails(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(doubleFlagEvaluationDetails);
var stringFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultStringValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant);
(await client.GetStringDetails(flagName, defaultStringValue)).Should().BeEquivalentTo(stringFlagEvaluationDetails);
- (await client.GetStringDetails(flagName, defaultStringValue, new EvaluationContext())).Should().BeEquivalentTo(stringFlagEvaluationDetails);
- (await client.GetStringDetails(flagName, defaultStringValue, new EvaluationContext(), emptyFlagOptions)).Should().BeEquivalentTo(stringFlagEvaluationDetails);
+ (await client.GetStringDetails(flagName, defaultStringValue, EvaluationContext.Empty)).Should().BeEquivalentTo(stringFlagEvaluationDetails);
+ (await client.GetStringDetails(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(stringFlagEvaluationDetails);
var structureFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultStructureValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant);
(await client.GetObjectDetails(flagName, defaultStructureValue)).Should().BeEquivalentTo(structureFlagEvaluationDetails);
- (await client.GetObjectDetails(flagName, defaultStructureValue, new EvaluationContext())).Should().BeEquivalentTo(structureFlagEvaluationDetails);
- (await client.GetObjectDetails(flagName, defaultStructureValue, new EvaluationContext(), emptyFlagOptions)).Should().BeEquivalentTo(structureFlagEvaluationDetails);
+ (await client.GetObjectDetails(flagName, defaultStructureValue, EvaluationContext.Empty)).Should().BeEquivalentTo(structureFlagEvaluationDetails);
+ (await client.GetObjectDetails(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(structureFlagEvaluationDetails);
}
[Fact]
@@ -393,7 +392,7 @@ public void Should_Get_And_Set_Context()
var KEY = "key";
var VAL = 1;
FeatureClient client = OpenFeature.Instance.GetClient();
- client.SetContext(new EvaluationContext().Add(KEY, VAL));
+ client.SetContext(new EvaluationContextBuilder().Set(KEY, VAL).Build());
Assert.Equal(VAL, client.GetContext().GetValue(KEY).AsInteger);
}
}
diff --git a/test/OpenFeatureSDK.Tests/OpenFeatureEvaluationContextTests.cs b/test/OpenFeatureSDK.Tests/OpenFeatureEvaluationContextTests.cs
index 8c743568..2dc90710 100644
--- a/test/OpenFeatureSDK.Tests/OpenFeatureEvaluationContextTests.cs
+++ b/test/OpenFeatureSDK.Tests/OpenFeatureEvaluationContextTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using AutoFixture;
using FluentAssertions;
using OpenFeatureSDK.Model;
@@ -13,12 +12,12 @@ public class OpenFeatureEvaluationContextTests
[Fact]
public void Should_Merge_Two_Contexts()
{
- var context1 = new EvaluationContext()
- .Add("key1", "value1");
- var context2 = new EvaluationContext()
- .Add("key2", "value2");
+ var contextBuilder1 = new EvaluationContextBuilder()
+ .Set("key1", "value1");
+ var contextBuilder2 = new EvaluationContextBuilder()
+ .Set("key2", "value2");
- context1.Merge(context2);
+ var context1 = contextBuilder1.Merge(contextBuilder2.Build()).Build();
Assert.Equal(2, context1.Count);
Assert.Equal("value1", context1.GetValue("key1").AsString);
@@ -29,21 +28,18 @@ public void Should_Merge_Two_Contexts()
[Specification("3.2.2", "Duplicate values being overwritten.")]
public void Should_Merge_TwoContexts_And_Override_Duplicates_With_RightHand_Context()
{
- var context1 = new EvaluationContext();
- var context2 = new EvaluationContext();
+ var contextBuilder1 = new EvaluationContextBuilder();
+ var contextBuilder2 = new EvaluationContextBuilder();
- context1.Add("key1", "value1");
- context2.Add("key1", "overriden_value");
- context2.Add("key2", "value2");
+ contextBuilder1.Set("key1", "value1");
+ contextBuilder2.Set("key1", "overriden_value");
+ contextBuilder2.Set("key2", "value2");
- context1.Merge(context2);
+ var context1 = contextBuilder1.Merge(contextBuilder2.Build()).Build();
Assert.Equal(2, context1.Count);
Assert.Equal("overriden_value", context1.GetValue("key1").AsString);
Assert.Equal("value2", context1.GetValue("key2").AsString);
-
- context1.Remove("key1");
- Assert.Throws(() => context1.GetValue("key1"));
}
[Fact]
@@ -54,13 +50,15 @@ public void EvaluationContext_Should_All_Types()
var fixture = new Fixture();
var now = fixture.Create();
var structure = fixture.Create();
- var context = new EvaluationContext()
- .Add("key1", "value")
- .Add("key2", 1)
- .Add("key3", true)
- .Add("key4", now)
- .Add("key5", structure)
- .Add("key6", 1.0);
+ var contextBuilder = new EvaluationContextBuilder()
+ .Set("key1", "value")
+ .Set("key2", 1)
+ .Set("key3", true)
+ .Set("key4", now)
+ .Set("key5", structure)
+ .Set("key6", 1.0);
+
+ var context = contextBuilder.Build();
var value1 = context.GetValue("key1");
value1.IsString.Should().BeTrue();
@@ -89,24 +87,24 @@ public void EvaluationContext_Should_All_Types()
[Fact]
[Specification("3.1.4", "The evaluation context fields MUST have an unique key.")]
- public void When_Duplicate_Key_Throw_Unique_Constraint()
+ public void When_Duplicate_Key_Set_It_Replaces_Value()
{
- var context = new EvaluationContext().Add("key", "value");
- var exception = Assert.Throws(() =>
- context.Add("key", "overriden_value"));
- exception.Message.Should().StartWith("An item with the same key has already been added.");
+ var contextBuilder = new EvaluationContextBuilder().Set("key", "value");
+ contextBuilder.Set("key", "overriden_value");
+ Assert.Equal("overriden_value", contextBuilder.Build().GetValue("key").AsString);
}
[Fact]
[Specification("3.1.3", "The evaluation context MUST support fetching the custom fields by key and also fetching all key value pairs.")]
public void Should_Be_Able_To_Get_All_Values()
{
- var context = new EvaluationContext()
- .Add("key1", "value1")
- .Add("key2", "value2")
- .Add("key3", "value3")
- .Add("key4", "value4")
- .Add("key5", "value5");
+ var context = new EvaluationContextBuilder()
+ .Set("key1", "value1")
+ .Set("key2", "value2")
+ .Set("key3", "value3")
+ .Set("key4", "value4")
+ .Set("key5", "value5")
+ .Build();
// Iterate over key value pairs and check consistency
var count = 0;
diff --git a/test/OpenFeatureSDK.Tests/OpenFeatureHookTests.cs b/test/OpenFeatureSDK.Tests/OpenFeatureHookTests.cs
index 8266e2d7..0c97e18b 100644
--- a/test/OpenFeatureSDK.Tests/OpenFeatureHookTests.cs
+++ b/test/OpenFeatureSDK.Tests/OpenFeatureHookTests.cs
@@ -33,19 +33,19 @@ public async Task Hooks_Should_Be_Called_In_Order()
apiHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(),
It.IsAny>()))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
clientHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(),
It.IsAny>()))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
invocationHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(),
It.IsAny>()))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
providerHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(),
It.IsAny>()))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
providerHook.InSequence(sequence).Setup(x => x.After(It.IsAny>(),
It.IsAny>(),
@@ -82,7 +82,7 @@ public async Task Hooks_Should_Be_Called_In_Order()
var client = OpenFeature.Instance.GetClient(clientName, clientVersion);
client.AddHooks(clientHook.Object);
- await client.GetBooleanValue(flagName, defaultValue, new EvaluationContext(),
+ await client.GetBooleanValue(flagName, defaultValue, EvaluationContext.Empty,
new FlagEvaluationOptions(invocationHook.Object, new Dictionary()));
apiHook.Verify(x => x.Before(
@@ -127,19 +127,19 @@ public async Task Hooks_Should_Be_Called_In_Order()
public void Hook_Context_Should_Not_Allow_Nulls()
{
Assert.Throws(() =>
- new HookContext(null, new Structure(), FlagValueType.Object, new ClientMetadata(null, null),
- new Metadata(null), new EvaluationContext()));
+ new HookContext(null, Structure.Empty, FlagValueType.Object, new ClientMetadata(null, null),
+ new Metadata(null), EvaluationContext.Empty));
Assert.Throws(() =>
- new HookContext("test", new Structure(), FlagValueType.Object, null,
- new Metadata(null), new EvaluationContext()));
+ new HookContext("test", Structure.Empty, FlagValueType.Object, null,
+ new Metadata(null), EvaluationContext.Empty));
Assert.Throws(() =>
- new HookContext("test", new Structure(), FlagValueType.Object, new ClientMetadata(null, null),
- null, new EvaluationContext()));
+ new HookContext("test", Structure.Empty, FlagValueType.Object, new ClientMetadata(null, null),
+ null, EvaluationContext.Empty));
Assert.Throws(() =>
- new HookContext("test", new Structure(), FlagValueType.Object, new ClientMetadata(null, null),
+ new HookContext("test", Structure.Empty, FlagValueType.Object, new ClientMetadata(null, null),
new Metadata(null), null));
}
@@ -150,9 +150,9 @@ public void Hook_Context_Should_Have_Properties_And_Be_Immutable()
{
var clientMetadata = new ClientMetadata("client", "1.0.0");
var providerMetadata = new Metadata("provider");
- var testStructure = new Structure();
+ var testStructure = Structure.Empty;
var context = new HookContext("test", testStructure, FlagValueType.Object, clientMetadata,
- providerMetadata, new EvaluationContext());
+ providerMetadata, EvaluationContext.Empty);
context.ClientMetadata.Should().BeSameAs(clientMetadata);
context.ProviderMetadata.Should().BeSameAs(providerMetadata);
@@ -166,7 +166,7 @@ public void Hook_Context_Should_Have_Properties_And_Be_Immutable()
[Specification("4.3.3", "Any `evaluation context` returned from a `before` hook MUST be passed to subsequent `before` hooks (via `HookContext`).")]
public async Task Evaluation_Context_Must_Be_Mutable_Before_Hook()
{
- var evaluationContext = new EvaluationContext().Add("test", "test");
+ var evaluationContext = new EvaluationContextBuilder().Set("test", "test").Build();
var hook1 = new Mock(MockBehavior.Strict);
var hook2 = new Mock(MockBehavior.Strict);
var hookContext = new HookContext("test", false,
@@ -181,7 +181,7 @@ public async Task Evaluation_Context_Must_Be_Mutable_Before_Hook()
.ReturnsAsync(evaluationContext);
var client = OpenFeature.Instance.GetClient("test", "1.0.0");
- await client.GetBooleanValue("test", false, new EvaluationContext(),
+ await client.GetBooleanValue("test", false, EvaluationContext.Empty,
new FlagEvaluationOptions(new[] { hook1.Object, hook2.Object }, new Dictionary()));
hook1.Verify(x => x.Before(It.IsAny>(), It.IsAny>()), Times.Once);
@@ -204,23 +204,27 @@ public async Task Evaluation_Context_Must_Be_Merged_In_Correct_Order()
var propHook = "4.3.4hook";
// setup a cascade of overwriting properties
- OpenFeature.Instance.SetContext(new EvaluationContext()
- .Add(propGlobal, true)
- .Add(propGlobalToOverwrite, false));
-
- var clientContext = new EvaluationContext()
- .Add(propClient, true)
- .Add(propGlobalToOverwrite, true)
- .Add(propClientToOverwrite, false);
-
- var invocationContext = new EvaluationContext()
- .Add(propInvocation, true)
- .Add(propClientToOverwrite, true)
- .Add(propInvocationToOverwrite, false);
-
- var hookContext = new EvaluationContext()
- .Add(propHook, true)
- .Add(propInvocationToOverwrite, true);
+ OpenFeature.Instance.SetContext(new EvaluationContextBuilder()
+ .Set(propGlobal, true)
+ .Set(propGlobalToOverwrite, false)
+ .Build());
+
+ var clientContext = new EvaluationContextBuilder()
+ .Set(propClient, true)
+ .Set(propGlobalToOverwrite, true)
+ .Set(propClientToOverwrite, false)
+ .Build();
+
+ var invocationContext = new EvaluationContextBuilder()
+ .Set(propInvocation, true)
+ .Set(propClientToOverwrite, true)
+ .Set(propInvocationToOverwrite, false)
+ .Build();
+
+ var hookContext = new EvaluationContextBuilder()
+ .Set(propHook, true)
+ .Set(propInvocationToOverwrite, true)
+ .Build();
var provider = new Mock(MockBehavior.Strict);
@@ -270,10 +274,10 @@ public async Task Hook_Should_Return_No_Errors()
["number"] = 1,
["boolean"] = true,
["datetime"] = DateTime.Now,
- ["structure"] = new Structure()
+ ["structure"] = Structure.Empty
};
var hookContext = new HookContext("test", false, FlagValueType.Boolean,
- new ClientMetadata(null, null), new Metadata(null), new EvaluationContext());
+ new ClientMetadata(null, null), new Metadata(null), EvaluationContext.Empty);
await hook.Before(hookContext, hookHints);
await hook.After(hookContext, new FlagEvaluationDetails("test", false, ErrorType.None, "testing", "testing"), hookHints);
@@ -307,7 +311,7 @@ public async Task Hook_Should_Execute_In_Correct_Order()
hook.InSequence(sequence).Setup(x =>
x.Before(It.IsAny>(), It.IsAny>()))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
featureProvider.InSequence(sequence)
.Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny()))
@@ -372,11 +376,11 @@ public async Task Finally_Hook_Should_Be_Executed_Even_If_Abnormal_Termination()
hook1.InSequence(sequence).Setup(x =>
x.Before(It.IsAny>(), null))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
hook2.InSequence(sequence).Setup(x =>
x.Before(It.IsAny>(), null))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
featureProvider.InSequence(sequence)
.Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny()))
@@ -431,11 +435,11 @@ public async Task Error_Hook_Should_Be_Executed_Even_If_Abnormal_Termination()
hook1.InSequence(sequence).Setup(x =>
x.Before(It.IsAny>(), null))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
hook2.InSequence(sequence).Setup(x =>
x.Before(It.IsAny>(), null))
- .ReturnsAsync(new EvaluationContext());
+ .ReturnsAsync(EvaluationContext.Empty);
featureProvider1.InSequence(sequence)
.Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny()))
diff --git a/test/OpenFeatureSDK.Tests/OpenFeatureTests.cs b/test/OpenFeatureSDK.Tests/OpenFeatureTests.cs
index 5150f032..5f9a778b 100644
--- a/test/OpenFeatureSDK.Tests/OpenFeatureTests.cs
+++ b/test/OpenFeatureSDK.Tests/OpenFeatureTests.cs
@@ -1,4 +1,3 @@
-using AutoFixture;
using FluentAssertions;
using Moq;
using OpenFeatureSDK.Constant;
@@ -79,8 +78,13 @@ public void OpenFeature_Should_Create_Client(string name = null, string version
[Fact]
public void Should_Set_Given_Context()
{
- var fixture = new Fixture();
- var context = fixture.Create();
+ var context = EvaluationContext.Empty;
+
+ OpenFeature.Instance.SetContext(context);
+
+ OpenFeature.Instance.GetContext().Should().BeSameAs(context);
+
+ context = EvaluationContext.Builder().Build();
OpenFeature.Instance.SetContext(context);
diff --git a/test/OpenFeatureSDK.Tests/StructureTests.cs b/test/OpenFeatureSDK.Tests/StructureTests.cs
index 2e78a62f..12bde7fb 100644
--- a/test/OpenFeatureSDK.Tests/StructureTests.cs
+++ b/test/OpenFeatureSDK.Tests/StructureTests.cs
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using FluentAssertions;
using OpenFeatureSDK.Model;
using Xunit;
@@ -10,18 +13,16 @@ public class StructureTests
[Fact]
public void No_Arg_Should_Contain_Empty_Attributes()
{
- Structure structure = new Structure();
+ Structure structure = Structure.Empty;
Assert.Equal(0, structure.Count);
- Assert.Equal(0, structure.AsDictionary().Keys.Count);
+ Assert.Empty(structure.AsDictionary());
}
[Fact]
public void Dictionary_Arg_Should_Contain_New_Dictionary()
{
string KEY = "key";
- IDictionary dictionary = new Dictionary(){
- { KEY, new Value(KEY) }
- };
+ IDictionary dictionary = new Dictionary() { { KEY, new Value(KEY) } };
Structure structure = new Structure(dictionary);
Assert.Equal(KEY, structure.AsDictionary()[KEY].AsString);
Assert.NotSame(structure.AsDictionary(), dictionary); // should be a copy
@@ -44,19 +45,20 @@ public void Add_And_Get_Add_And_Return_Values()
int INT_VAL = 13;
double DOUBLE_VAL = .5;
DateTime DATE_VAL = DateTime.Now;
- Structure STRUCT_VAL = new Structure();
+ Structure STRUCT_VAL = Structure.Empty;
IList LIST_VAL = new List();
Value VALUE_VAL = new Value();
- Structure structure = new Structure();
- structure.Add(BOOL_KEY, BOOL_VAL);
- structure.Add(STRING_KEY, STRING_VAL);
- structure.Add(INT_KEY, INT_VAL);
- structure.Add(DOUBLE_KEY, DOUBLE_VAL);
- structure.Add(DATE_KEY, DATE_VAL);
- structure.Add(STRUCT_KEY, STRUCT_VAL);
- structure.Add(LIST_KEY, LIST_VAL);
- structure.Add(VALUE_KEY, VALUE_VAL);
+ var structureBuilder = Structure.Builder();
+ structureBuilder.Set(BOOL_KEY, BOOL_VAL);
+ structureBuilder.Set(STRING_KEY, STRING_VAL);
+ structureBuilder.Set(INT_KEY, INT_VAL);
+ structureBuilder.Set(DOUBLE_KEY, DOUBLE_VAL);
+ structureBuilder.Set(DATE_KEY, DATE_VAL);
+ structureBuilder.Set(STRUCT_KEY, STRUCT_VAL);
+ structureBuilder.Set(LIST_KEY, ImmutableList.CreateRange(LIST_VAL));
+ structureBuilder.Set(VALUE_KEY, VALUE_VAL);
+ var structure = structureBuilder.Build();
Assert.Equal(BOOL_VAL, structure.GetValue(BOOL_KEY).AsBoolean);
Assert.Equal(STRING_VAL, structure.GetValue(STRING_KEY).AsString);
@@ -68,27 +70,14 @@ public void Add_And_Get_Add_And_Return_Values()
Assert.True(structure.GetValue(VALUE_KEY).IsNull);
}
- [Fact]
- public void Remove_Should_Remove_Value()
- {
- String KEY = "key";
- bool VAL = true;
-
- Structure structure = new Structure();
- structure.Add(KEY, VAL);
- Assert.Equal(1, structure.Count);
- structure.Remove(KEY);
- Assert.Equal(0, structure.Count);
- }
-
[Fact]
public void TryGetValue_Should_Return_Value()
{
String KEY = "key";
String VAL = "val";
- Structure structure = new Structure();
- structure.Add(KEY, VAL);
+ var structure = Structure.Builder()
+ .Set(KEY, VAL).Build();
Value value;
Assert.True(structure.TryGetValue(KEY, out value));
Assert.Equal(VAL, value.AsString);
@@ -100,8 +89,8 @@ public void Values_Should_Return_Values()
String KEY = "key";
Value VAL = new Value("val");
- Structure structure = new Structure();
- structure.Add(KEY, VAL);
+ var structure = Structure.Builder()
+ .Set(KEY, VAL).Build();
Assert.Equal(1, structure.Values.Count);
}
@@ -111,10 +100,10 @@ public void Keys_Should_Return_Keys()
String KEY = "key";
Value VAL = new Value("val");
- Structure structure = new Structure();
- structure.Add(KEY, VAL);
+ var structure = Structure.Builder()
+ .Set(KEY, VAL).Build();
Assert.Equal(1, structure.Keys.Count);
- Assert.True(structure.Keys.Contains(KEY));
+ Assert.Equal(0, structure.Keys.IndexOf(KEY));
}
[Fact]
@@ -123,8 +112,8 @@ public void GetEnumerator_Should_Return_Enumerator()
string KEY = "key";
string VAL = "val";
- Structure structure = new Structure();
- structure.Add(KEY, VAL);
+ var structure = Structure.Builder()
+ .Set(KEY, VAL).Build();
IEnumerator> enumerator = structure.GetEnumerator();
enumerator.MoveNext();
Assert.Equal(VAL, enumerator.Current.Value.AsString);
diff --git a/test/OpenFeatureSDK.Tests/TestImplementations.cs b/test/OpenFeatureSDK.Tests/TestImplementations.cs
index 980aea01..4236077c 100644
--- a/test/OpenFeatureSDK.Tests/TestImplementations.cs
+++ b/test/OpenFeatureSDK.Tests/TestImplementations.cs
@@ -11,7 +11,7 @@ public class TestHook : Hook
{
public override Task Before(HookContext context, IReadOnlyDictionary hints = null)
{
- return Task.FromResult(new EvaluationContext());
+ return Task.FromResult(EvaluationContext.Empty);
}
public override Task After(HookContext context, FlagEvaluationDetails details,
diff --git a/test/OpenFeatureSDK.Tests/ValueTests.cs b/test/OpenFeatureSDK.Tests/ValueTests.cs
index cf3f214c..8c3ae37d 100644
--- a/test/OpenFeatureSDK.Tests/ValueTests.cs
+++ b/test/OpenFeatureSDK.Tests/ValueTests.cs
@@ -7,7 +7,9 @@ namespace OpenFeatureSDK.Tests
{
public class ValueTests
{
- class Foo { }
+ class Foo
+ {
+ }
[Fact]
public void No_Arg_Should_Contain_Null()
@@ -19,24 +21,23 @@ public void No_Arg_Should_Contain_Null()
[Fact]
public void Object_Arg_Should_Contain_Object()
{
- try
+ // int is a special case, see Int_Object_Arg_Should_Contain_Object()
+ IList