Skip to content

Commit 7d3665b

Browse files
feat!: DISABLED is a successful evaluation (still defaults) (#671)
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com> Signed-off-by: Todd Baert <todd.baert@dynatrace.com> Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
1 parent c2afa7e commit 7d3665b

6 files changed

Lines changed: 39 additions & 22 deletions

File tree

src/OpenFeature.Providers.Flagd/Resolver/InProcess/JsonEvaluator.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,11 @@ private ResolutionDetails<T> ResolveValue<T>(string flagKey, T defaultValue,
230230
{
231231
if ("DISABLED" == flagConfiguration.State)
232232
{
233-
throw new FeatureProviderException(ErrorType.FlagNotFound,
234-
"FLAG_NOT_FOUND: flag '" + flagKey + "' is disabled");
233+
return new ResolutionDetails<T>(
234+
flagKey: flagKey,
235+
defaultValue,
236+
reason: Reason.Disabled
237+
);
235238
}
236239

237240
Dictionary<string, object> combinedMetadata = _flagSetMetadata.ToDictionary(

src/OpenFeature.Providers.Flagd/Resolver/Rpc/RpcResolver.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ public async Task<ResolutionDetails<bool>> ResolveBooleanValueAsync(string flagK
8888
FlagKey = flagKey
8989
}).ConfigureAwait(false);
9090

91-
// Use code default if variant is empty and reason is DEFAULT (no default variant in flag definition)
92-
var value = string.IsNullOrEmpty(resolveBooleanResponse.Variant) && resolveBooleanResponse.Reason == Reason.Default ? defaultValue : resolveBooleanResponse.Value;
91+
// Use code default if variant is empty and reason is DEFAULT (no default variant) or DISABLED
92+
var value = string.IsNullOrEmpty(resolveBooleanResponse.Variant) && (resolveBooleanResponse.Reason == Reason.Default || resolveBooleanResponse.Reason == Reason.Disabled) ? defaultValue : resolveBooleanResponse.Value;
9393

9494
return new ResolutionDetails<bool>(
9595
flagKey: flagKey,
@@ -111,8 +111,8 @@ public async Task<ResolutionDetails<string>> ResolveStringValueAsync(string flag
111111
FlagKey = flagKey
112112
}).ConfigureAwait(false);
113113

114-
// Use code default if variant is empty and reason is DEFAULT (no default variant in flag definition)
115-
var value = string.IsNullOrEmpty(resolveStringResponse.Variant) && resolveStringResponse.Reason == Reason.Default ? defaultValue : resolveStringResponse.Value;
114+
// Use code default if variant is empty and reason is DEFAULT (no default variant) or DISABLED
115+
var value = string.IsNullOrEmpty(resolveStringResponse.Variant) && (resolveStringResponse.Reason == Reason.Default || resolveStringResponse.Reason == Reason.Disabled) ? defaultValue : resolveStringResponse.Value;
116116

117117
return new ResolutionDetails<string>(
118118
flagKey: flagKey,
@@ -134,8 +134,8 @@ public async Task<ResolutionDetails<int>> ResolveIntegerValueAsync(string flagKe
134134
FlagKey = flagKey
135135
}).ConfigureAwait(false);
136136

137-
// Use code default if variant is empty and reason is DEFAULT (no default variant in flag definition)
138-
var value = string.IsNullOrEmpty(resolveIntResponse.Variant) && resolveIntResponse.Reason == Reason.Default ? defaultValue : (int)resolveIntResponse.Value;
137+
// Use code default if variant is empty and reason is DEFAULT (no default variant) or DISABLED
138+
var value = string.IsNullOrEmpty(resolveIntResponse.Variant) && (resolveIntResponse.Reason == Reason.Default || resolveIntResponse.Reason == Reason.Disabled) ? defaultValue : (int)resolveIntResponse.Value;
139139

140140
return new ResolutionDetails<int>(
141141
flagKey: flagKey,
@@ -157,8 +157,8 @@ public async Task<ResolutionDetails<double>> ResolveDoubleValueAsync(string flag
157157
FlagKey = flagKey
158158
}).ConfigureAwait(false);
159159

160-
// Use code default if variant is empty and reason is DEFAULT (no default variant in flag definition)
161-
var value = string.IsNullOrEmpty(resolveDoubleResponse.Variant) && resolveDoubleResponse.Reason == Reason.Default ? defaultValue : resolveDoubleResponse.Value;
160+
// Use code default if variant is empty and reason is DEFAULT (no default variant) or DISABLED
161+
var value = string.IsNullOrEmpty(resolveDoubleResponse.Variant) && (resolveDoubleResponse.Reason == Reason.Default || resolveDoubleResponse.Reason == Reason.Disabled) ? defaultValue : resolveDoubleResponse.Value;
162162

163163
return new ResolutionDetails<double>(
164164
flagKey: flagKey,
@@ -180,8 +180,8 @@ public async Task<ResolutionDetails<Value>> ResolveStructureValueAsync(string fl
180180
FlagKey = flagKey
181181
}).ConfigureAwait(false);
182182

183-
// Use code default if variant is empty and reason is DEFAULT (no default variant in flag definition)
184-
var value = string.IsNullOrEmpty(resolveObjectResponse.Variant) && resolveObjectResponse.Reason == Reason.Default ? defaultValue : ConvertObjectToValue(resolveObjectResponse.Value);
183+
// Use code default if variant is empty and reason is DEFAULT (no default variant) or DISABLED
184+
var value = string.IsNullOrEmpty(resolveObjectResponse.Variant) && (resolveObjectResponse.Reason == Reason.Default || resolveObjectResponse.Reason == Reason.Disabled) ? defaultValue : ConvertObjectToValue(resolveObjectResponse.Value);
185185

186186
return new ResolutionDetails<Value>(
187187
flagKey: flagKey,

test/OpenFeature.Providers.Flagd.E2e.Common/Steps/FlagSteps.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,31 @@ public FlagSteps(State state)
1818
this._state = state;
1919
}
2020

21-
[Given(@"a (Boolean|Float|Integer|String)(?:-flag)? with key ""(.*)"" and a default value ""(.*)""")]
21+
[Given(@"a (Boolean|Float|Integer|String|Object)(?:-flag)? with key ""(.*)"" and a default value ""(.*)""")]
2222
public void GivenAFlagType_FlagWithKeyAndADefaultValue(FlagType flagType, string key, string defaultType)
2323
{
2424
var flagState = new FlagState(key, defaultType, flagType);
2525
this._state.Flag = flagState;
2626
}
2727

28-
[StepArgumentTransformation(@"^(Boolean|Float|Integer|String)(?:-flag)?$")]
28+
[StepArgumentTransformation(@"^(Boolean|Float|Integer|String|Object)(?:-flag)?$")]
2929
public static FlagType TransformFlagType(string raw)
3030
=> raw.Replace("-flag", "").ToLowerInvariant() switch
3131
{
3232
"boolean" => FlagType.Boolean,
3333
"float" => FlagType.Float,
3434
"integer" => FlagType.Integer,
3535
"string" => FlagType.String,
36+
"object" => FlagType.Object,
3637
_ => throw new Exception($"Unsupported flag type '{raw}'")
3738
};
3839

3940
[When("the flag was evaluated with details")]
4041
public async Task WhenTheFlagWasEvaluatedWithDetails()
4142
{
4243
var flag = this._state.Flag!;
44+
Skip.If(flag.Type == FlagType.Object, "Object flag evaluation not yet supported in step bindings.");
45+
4346
var contextBuilder = this._state.EvaluationContextBuilder
4447
?? EvaluationContext.Builder();
4548
var context = contextBuilder.Build();

test/OpenFeature.Providers.Flagd.E2e.ProcessTest/OpenFeature.Providers.Flagd.E2e.ProcessTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/metadata.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
99
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/contextEnrichment.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
1010
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/config.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
11+
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/disabled.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
1112
</ItemGroup>
1213

1314
<ItemGroup>

test/OpenFeature.Providers.Flagd.E2e.RpcTest/OpenFeature.Providers.Flagd.E2e.RpcTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/metadata.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
88
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/contextEnrichment.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
99
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/config.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
10+
<None Include="../../src/OpenFeature.Providers.Flagd/flagd-testbed/gherkin/disabled.feature" Link="../../../Features/%(Filename)%(Extension)" DestinationFolder="../../../Features/" CopyToOutputDirectory="PreserveNewest" />
1011
</ItemGroup>
1112

1213
<ItemGroup>

test/OpenFeature.Providers.Flagd.Test/JsonEvaluatorTest.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,19 +245,28 @@ public void TestJsonEvaluatorDynamicObjectEvaluation()
245245
}
246246

247247
[Fact]
248-
public void TestJsonEvaluatorDisabledBoolEvaluation()
248+
public void TestJsonEvaluatorDisabledFlagReturnsDisabledReason()
249249
{
250250
_jsonEvaluator.Sync(FlagConfigurationUpdateType.ALL, Utils.flags);
251251

252-
var attributes = ImmutableDictionary.CreateBuilder<string, Value>();
253-
attributes.Add("color", new Value("yellow"));
252+
var result = _jsonEvaluator.ResolveBooleanValueAsync("disabledFlag", false);
254253

255-
var builder = EvaluationContext.Builder();
256-
builder
257-
.Set("color", "yellow");
254+
Assert.False(result.Value);
255+
Assert.Equal(Reason.Disabled, result.Reason);
256+
Assert.Null(result.Variant);
257+
}
258258

259-
Assert.Throws<FeatureProviderException>(() =>
260-
_jsonEvaluator.ResolveBooleanValueAsync("disabledFlag", false, builder.Build()));
259+
[Fact]
260+
public void TestJsonEvaluatorDisabledFlagIgnoresTargeting()
261+
{
262+
_jsonEvaluator.Sync(FlagConfigurationUpdateType.ALL, Utils.flags);
263+
264+
var context = EvaluationContext.Builder().Set("color", "yellow").Build();
265+
var result = _jsonEvaluator.ResolveBooleanValueAsync("disabledFlag", false, context);
266+
267+
Assert.False(result.Value);
268+
Assert.Equal(Reason.Disabled, result.Reason);
269+
Assert.Null(result.Variant);
261270
}
262271

263272
[Fact]

0 commit comments

Comments
 (0)