Skip to content

Commit cd7f5cc

Browse files
committed
fix: hooks not run in NOT_READY/FATAL
Signed-off-by: Todd Baert <[email protected]>
1 parent 9c92ebb commit cd7f5cc

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

Diff for: 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

+45
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+
}

Diff for: src/test/java/dev/openfeature/sdk/HookSpecTest.java

+21
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,27 @@ void erroneous_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails() {
596596
assertThat(evaluationDetails.getValue()).isTrue();
597597
}
598598

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

0 commit comments

Comments
 (0)