Skip to content

Commit 24ef9dd

Browse files
authored
fix: hooks not run in NOT_READY/FATAL (#1392)
* fix: hooks not run in NOT_READY/FATAL Signed-off-by: Todd Baert <[email protected]> --------- Signed-off-by: Todd Baert <[email protected]>
1 parent 7536679 commit 24ef9dd

File tree

3 files changed

+72
-6
lines changed

3 files changed

+72
-6
lines changed

src/main/java/dev/openfeature/sdk/OpenFeatureClient.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,6 @@ private <T> FlagEvaluationDetails<T> evaluateFlag(
178178
// provider must be accessed once to maintain a consistent reference
179179
provider = stateManager.getProvider();
180180
ProviderState state = stateManager.getState();
181-
if (ProviderState.NOT_READY.equals(state)) {
182-
throw new ProviderNotReadyError("provider not yet initialized");
183-
}
184-
if (ProviderState.FATAL.equals(state)) {
185-
throw new FatalError("provider is in an irrecoverable error state");
186-
}
187181

188182
mergedHooks = ObjectUtils.merge(
189183
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getHooks());
@@ -203,6 +197,14 @@ private <T> FlagEvaluationDetails<T> evaluateFlag(
203197
afterHookContext =
204198
HookContext.from(key, type, this.getMetadata(), provider.getMetadata(), mergedCtx, defaultValue);
205199

200+
// "short circuit" if the provider is in NOT_READY or FATAL state
201+
if (ProviderState.NOT_READY.equals(state)) {
202+
throw new ProviderNotReadyError("Provider not yet initialized");
203+
}
204+
if (ProviderState.FATAL.equals(state)) {
205+
throw new FatalError("Provider is in an irrecoverable error state");
206+
}
207+
206208
ProviderEvaluation<T> providerEval =
207209
(ProviderEvaluation<T>) createProviderEvaluation(type, key, defaultValue, provider, mergedCtx);
208210

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package dev.openfeature.sdk;
2+
3+
import dev.openfeature.sdk.exceptions.FatalError;
4+
import dev.openfeature.sdk.exceptions.GeneralError;
5+
6+
public class FatalErrorProvider implements FeatureProvider {
7+
8+
private final String name = "fatal";
9+
10+
@Override
11+
public Metadata getMetadata() {
12+
return () -> name;
13+
}
14+
15+
@Override
16+
public void initialize(EvaluationContext evaluationContext) throws Exception {
17+
throw new FatalError(); // throw a fatal error on startup (this will cause the SDK to short circuit evaluations)
18+
}
19+
20+
@Override
21+
public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
22+
throw new GeneralError(TestConstants.BROKEN_MESSAGE);
23+
}
24+
25+
@Override
26+
public ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
27+
throw new GeneralError(TestConstants.BROKEN_MESSAGE);
28+
}
29+
30+
@Override
31+
public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
32+
throw new GeneralError(TestConstants.BROKEN_MESSAGE);
33+
}
34+
35+
@Override
36+
public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
37+
throw new GeneralError(TestConstants.BROKEN_MESSAGE);
38+
}
39+
40+
@Override
41+
public ProviderEvaluation<Value> getObjectEvaluation(
42+
String key, Value defaultValue, EvaluationContext invocationContext) {
43+
throw new GeneralError(TestConstants.BROKEN_MESSAGE);
44+
}
45+
}

src/test/java/dev/openfeature/sdk/HookSpecTest.java

+19
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,25 @@ void erroneous_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails() {
594594
assertThat(evaluationDetails.getValue()).isTrue();
595595
}
596596

597+
@Test
598+
void shortCircuit_flagResolution_runsHooksWithAllFields() {
599+
String domain = "shortCircuit_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails";
600+
api.setProvider(domain, new FatalErrorProvider());
601+
602+
Hook hook = mockBooleanHook();
603+
String flagKey = "test-flag-key";
604+
Client client = api.getClient(domain);
605+
client.getBooleanValue(
606+
flagKey,
607+
true,
608+
new ImmutableContext(),
609+
FlagEvaluationOptions.builder().hook(hook).build());
610+
611+
verify(hook).before(any(), any());
612+
verify(hook).error(any(HookContext.class), any(Exception.class), any(Map.class));
613+
verify(hook).finallyAfter(any(HookContext.class), any(FlagEvaluationDetails.class), any(Map.class));
614+
}
615+
597616
@Test
598617
void successful_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails() {
599618
Hook hook = mockBooleanHook();

0 commit comments

Comments
 (0)