diff --git a/src/OpenFeature/Model/EvaluationContext.cs b/src/OpenFeature/Model/EvaluationContext.cs
index e8a94bc9..6db585a1 100644
--- a/src/OpenFeature/Model/EvaluationContext.cs
+++ b/src/OpenFeature/Model/EvaluationContext.cs
@@ -16,9 +16,11 @@ public sealed class EvaluationContext
///
/// Internal constructor used by the builder.
///
+ /// The targeting key
/// The content of the context.
- internal EvaluationContext(Structure content)
+ internal EvaluationContext(string targetingKey, Structure content)
{
+ this.TargetingKey = targetingKey;
this._structure = content;
}
@@ -28,6 +30,7 @@ internal EvaluationContext(Structure content)
private EvaluationContext()
{
this._structure = Structure.Empty;
+ this.TargetingKey = string.Empty;
}
///
@@ -83,6 +86,11 @@ public IImmutableDictionary AsDictionary()
///
public int Count => this._structure.Count;
+ ///
+ /// Returns the targeting key for the context.
+ ///
+ public string TargetingKey { get; }
+
///
/// Return an enumerator for all values
///
diff --git a/src/OpenFeature/Model/EvaluationContextBuilder.cs b/src/OpenFeature/Model/EvaluationContextBuilder.cs
index 57afa5cf..89174cf6 100644
--- a/src/OpenFeature/Model/EvaluationContextBuilder.cs
+++ b/src/OpenFeature/Model/EvaluationContextBuilder.cs
@@ -14,11 +14,24 @@ public sealed class EvaluationContextBuilder
{
private readonly StructureBuilder _attributes = Structure.Builder();
+ internal string TargetingKey { get; private set; }
+
///
/// Internal to only allow direct creation by .
///
internal EvaluationContextBuilder() { }
+ ///
+ /// Set the targeting key for the context.
+ ///
+ /// The targeting key
+ /// This builder
+ public EvaluationContextBuilder SetTargetingKey(string targetingKey)
+ {
+ this.TargetingKey = targetingKey;
+ return this;
+ }
+
///
/// Set the key to the given .
///
@@ -125,6 +138,23 @@ public EvaluationContextBuilder Set(string key, DateTime value)
/// This builder
public EvaluationContextBuilder Merge(EvaluationContext context)
{
+ string newTargetingKey = "";
+
+ if (!string.IsNullOrWhiteSpace(TargetingKey))
+ {
+ newTargetingKey = TargetingKey;
+ }
+
+ if (!string.IsNullOrWhiteSpace(context.TargetingKey))
+ {
+ newTargetingKey = context.TargetingKey;
+ }
+
+ if (!string.IsNullOrWhiteSpace(newTargetingKey))
+ {
+ this.TargetingKey = newTargetingKey;
+ }
+
foreach (var kvp in context)
{
this.Set(kvp.Key, kvp.Value);
@@ -139,7 +169,7 @@ public EvaluationContextBuilder Merge(EvaluationContext context)
/// An immutable
public EvaluationContext Build()
{
- return new EvaluationContext(this._attributes.Build());
+ return new EvaluationContext(this.TargetingKey, this._attributes.Build());
}
}
}
diff --git a/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs b/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs
index 4f091ab1..9aaf5fce 100644
--- a/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs
+++ b/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs
@@ -200,12 +200,11 @@ public void Giventhevariantshouldbeandthereasonshouldbe(string expectedVariant,
[When(@"context contains keys ""(.*)"", ""(.*)"", ""(.*)"", ""(.*)"" with values ""(.*)"", ""(.*)"", (.*), ""(.*)""")]
public void Whencontextcontainskeyswithvalues(string field1, string field2, string field3, string field4, string value1, string value2, int value3, string value4)
{
- var attributes = ImmutableDictionary.CreateBuilder();
- attributes.Add(field1, new Value(value1));
- attributes.Add(field2, new Value(value2));
- attributes.Add(field3, new Value(value3));
- attributes.Add(field4, new Value(bool.Parse(value4)));
- this.context = new EvaluationContext(new Structure(attributes));
+ this.context = new EvaluationContextBuilder()
+ .Set(field1, value1)
+ .Set(field2, value2)
+ .Set(field3, value3)
+ .Set(field4, bool.Parse(value4)).Build();
}
[When(@"a flag with key ""(.*)"" is evaluated with default value ""(.*)""")]
@@ -225,7 +224,7 @@ public void Thentheresolvedstringresponseshouldbe(string expected)
[Then(@"the resolved flag value is ""(.*)"" when the context is empty")]
public void Giventheresolvedflagvalueiswhenthecontextisempty(string expected)
{
- string emptyContextValue = client.GetStringValue(contextAwareFlagKey, contextAwareDefaultValue, new EvaluationContext(new Structure(ImmutableDictionary.Empty))).Result;
+ string emptyContextValue = client.GetStringValue(contextAwareFlagKey, contextAwareDefaultValue, new EvaluationContextBuilder().Build()).Result;
Assert.Equal(expected, emptyContextValue);
}
diff --git a/test/OpenFeature.Tests/OpenFeatureEvaluationContextTests.cs b/test/OpenFeature.Tests/OpenFeatureEvaluationContextTests.cs
index a9906cf4..0b8ee097 100644
--- a/test/OpenFeature.Tests/OpenFeatureEvaluationContextTests.cs
+++ b/test/OpenFeature.Tests/OpenFeatureEvaluationContextTests.cs
@@ -16,7 +16,6 @@ public void Should_Merge_Two_Contexts()
.Set("key1", "value1");
var contextBuilder2 = new EvaluationContextBuilder()
.Set("key2", "value2");
-
var context1 = contextBuilder1.Merge(contextBuilder2.Build()).Build();
Assert.Equal(2, context1.Count);
@@ -24,6 +23,35 @@ public void Should_Merge_Two_Contexts()
Assert.Equal("value2", context1.GetValue("key2").AsString);
}
+ [Fact]
+ public void Should_Change_TargetingKey_From_OverridingContext()
+ {
+ var contextBuilder1 = new EvaluationContextBuilder()
+ .Set("key1", "value1")
+ .SetTargetingKey("targeting_key");
+ var contextBuilder2 = new EvaluationContextBuilder()
+ .Set("key2", "value2")
+ .SetTargetingKey("overriding_key");
+
+ var mergeContext = contextBuilder1.Merge(contextBuilder2.Build()).Build();
+
+ Assert.Equal("overriding_key", mergeContext.TargetingKey);
+ }
+
+ [Fact]
+ public void Should_Retain_TargetingKey_When_OverridingContext_TargetingKey_Value_IsEmpty()
+ {
+ var contextBuilder1 = new EvaluationContextBuilder()
+ .Set("key1", "value1")
+ .SetTargetingKey("targeting_key");
+ var contextBuilder2 = new EvaluationContextBuilder()
+ .Set("key2", "value2");
+
+ var mergeContext = contextBuilder1.Merge(contextBuilder2.Build()).Build();
+
+ Assert.Equal("targeting_key", mergeContext.TargetingKey);
+ }
+
[Fact]
[Specification("3.2.2", "Evaluation context MUST be merged in the order: API (global; lowest precedence) - client - invocation - before hooks (highest precedence), with duplicate values being overwritten.")]
public void Should_Merge_TwoContexts_And_Override_Duplicates_With_RightHand_Context()
@@ -51,6 +79,8 @@ public void EvaluationContext_Should_All_Types()
var now = fixture.Create();
var structure = fixture.Create();
var contextBuilder = new EvaluationContextBuilder()
+ .SetTargetingKey("targeting_key")
+ .Set("targeting_key", "userId")
.Set("key1", "value")
.Set("key2", 1)
.Set("key3", true)
@@ -60,6 +90,11 @@ public void EvaluationContext_Should_All_Types()
var context = contextBuilder.Build();
+ context.TargetingKey.Should().Be("targeting_key");
+ var targetingKeyValue = context.GetValue(context.TargetingKey);
+ targetingKeyValue.IsString.Should().BeTrue();
+ targetingKeyValue.AsString.Should().Be("userId");
+
var value1 = context.GetValue("key1");
value1.IsString.Should().BeTrue();
value1.AsString.Should().Be("value");