Skip to content

Commit 472dc19

Browse files
committed
-addresses review comments
-executes generic hooks for all flag evaluations, regardless of the type
1 parent c828122 commit 472dc19

File tree

4 files changed

+61
-27
lines changed

4 files changed

+61
-27
lines changed

lib/src/main/java/dev/openfeature/javasdk/Hook.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,7 @@ default void error(HookContext<T> ctx, Exception error, Map<String, Object> hint
4949
default void finallyAfter(HookContext<T> ctx, Map<String, Object> hints) {
5050
}
5151

52-
FlagValueType supportsFlagValueType();
52+
default FlagValueType supportsFlagValueType() {
53+
return FlagValueType.OBJECT;
54+
}
5355
}

lib/src/main/java/dev/openfeature/javasdk/HookSupport.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66

77
import com.google.common.collect.Lists;
88
import lombok.RequiredArgsConstructor;
9-
import org.slf4j.Logger;
9+
import lombok.extern.slf4j.Slf4j;
1010

11-
@SuppressWarnings({"unchecked", "rawtypes", "PMD.LoggerIsNotStaticFinal"})
11+
@Slf4j
1212
@RequiredArgsConstructor
13+
@SuppressWarnings({"unchecked", "rawtypes"})
1314
class HookSupport {
1415

15-
private final Logger log;
16-
1716
public void errorHooks(FlagValueType flagValueType, HookContext hookCtx, Exception e, List<Hook> hooks, Map<String, Object> hints) {
1817
executeHooks(flagValueType, hooks, "error", hook -> hook.error(hookCtx, e, hints));
1918
}
@@ -33,17 +32,21 @@ private <T> void executeHooks(
3332
) {
3433
hooks
3534
.stream()
36-
.filter(hook -> hook.supportsFlagValueType() == flagValueType)
35+
.filter(hook -> isHookCompatible(flagValueType, hook))
3736
.forEach(hook -> executeChecked(hook, hookCode, hookMethod));
3837
}
3938

39+
private boolean isHookCompatible(FlagValueType flagValueType, Hook hook) {
40+
return hook.supportsFlagValueType() == flagValueType || hook.supportsFlagValueType() == FlagValueType.OBJECT;
41+
}
42+
4043
private <T> void executeHooksUnchecked(
4144
FlagValueType flagValueType, List<Hook> hooks,
4245
Consumer<Hook<T>> hookCode
4346
) {
4447
hooks
4548
.stream()
46-
.filter(hook -> hook.supportsFlagValueType() == flagValueType)
49+
.filter(hook -> isHookCompatible(flagValueType, hook))
4750
.forEach(hookCode::accept);
4851
}
4952

@@ -65,7 +68,7 @@ private Stream<EvaluationContext> callBeforeHooks(FlagValueType flagValueType, H
6568
return Lists
6669
.reverse(hooks)
6770
.stream()
68-
.filter(hook -> hook.supportsFlagValueType() == flagValueType)
71+
.filter(hook -> isHookCompatible(flagValueType, hook))
6972
.map(hook -> hook.before(hookCtx, hints))
7073
.filter(Objects::nonNull)
7174
.flatMap(Optional::stream);

lib/src/main/java/dev/openfeature/javasdk/OpenFeatureClient.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,24 @@
55
import dev.openfeature.javasdk.exceptions.GeneralError;
66
import dev.openfeature.javasdk.internal.ObjectUtils;
77
import lombok.Getter;
8-
import org.slf4j.*;
8+
import lombok.extern.slf4j.Slf4j;
99

10+
@Slf4j
1011
@SuppressWarnings({"PMD.DataflowAnomalyAnalysis", "PMD.BeanMembersShouldSerialize", "unchecked", "rawtypes"})
1112
public class OpenFeatureClient implements Client {
12-
private static final Logger log = LoggerFactory.getLogger(OpenFeatureClient.class);
1313

1414
private final OpenFeatureAPI openfeatureApi;
15-
@Getter
16-
private final String name;
17-
@Getter
18-
private final String version;
19-
@Getter
20-
private final List<Hook> clientHooks;
15+
@Getter private final String name;
16+
@Getter private final String version;
17+
@Getter private final List<Hook> clientHooks;
2118
private final HookSupport hookSupport;
2219

2320
public OpenFeatureClient(OpenFeatureAPI openFeatureAPI, String name, String version) {
2421
this.openfeatureApi = openFeatureAPI;
2522
this.name = name;
2623
this.version = version;
2724
this.clientHooks = new ArrayList<>();
28-
this.hookSupport = new HookSupport(log);
25+
this.hookSupport = new HookSupport();
2926
}
3027

3128
@Override

lib/src/test/java/dev/openfeature/javasdk/HookSupportTest.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,27 @@
22

33
import java.util.*;
44

5+
import dev.openfeature.javasdk.fixtures.HookFixtures;
56
import org.junit.jupiter.api.*;
6-
import org.slf4j.LoggerFactory;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.EnumSource;
79

810
import static org.assertj.core.api.Assertions.assertThat;
911
import static org.mockito.Mockito.*;
1012

11-
class HookSupportTest {
13+
class HookSupportTest implements HookFixtures {
1214

1315
@Test
1416
@DisplayName("should merge EvaluationContexts on before hooks correctly")
1517
void shouldMergeEvaluationContextsOnBeforeHooksCorrectly() {
1618
var baseContext = new EvaluationContext();
1719
baseContext.addStringAttribute("baseKey", "baseValue");
1820
var hookContext = new HookContext<>("flagKey", FlagValueType.STRING, "defaultValue", baseContext, () -> "client", () -> "provider");
19-
var hook1 = stringHookMock();
20-
var hook2 = stringHookMock();
21+
var hook1 = mockStringHook();
22+
var hook2 = mockStringHook();
2123
when(hook1.before(any(), any())).thenReturn(Optional.of(evaluationContextWithValue("bla", "blubber")));
2224
when(hook2.before(any(), any())).thenReturn(Optional.of(evaluationContextWithValue("foo", "bar")));
23-
24-
var hookSupport = new HookSupport(LoggerFactory.getLogger("test"));
25+
var hookSupport = new HookSupport();
2526

2627
var result = hookSupport.beforeHooks(FlagValueType.STRING, hookContext, List.of(hook1, hook2), Map.of());
2728

@@ -30,14 +31,45 @@ void shouldMergeEvaluationContextsOnBeforeHooksCorrectly() {
3031
assertThat(result.getStringAttribute("baseKey")).isEqualTo("baseValue");
3132
}
3233

34+
@ParameterizedTest
35+
@EnumSource(value = FlagValueType.class)
36+
@DisplayName("should always call generic hook")
37+
void shouldAlwaysCallGenericHook(FlagValueType flagValueType) {
38+
var genericHook = mockGenericHook();
39+
var hookSupport = new HookSupport();
40+
var baseContext = new EvaluationContext();
41+
var hookContext = new HookContext<>("flagKey", flagValueType, createDefaultValue(flagValueType), baseContext, () -> "client", () -> "provider");
42+
43+
hookSupport.beforeHooks(flagValueType, hookContext, List.of(genericHook), Map.of());
44+
hookSupport.afterHooks(flagValueType, hookContext, FlagEvaluationDetails.builder().build(), List.of(genericHook), Map.of());
45+
hookSupport.afterAllHooks(flagValueType, hookContext, List.of(genericHook), Map.of());
46+
hookSupport.errorHooks(flagValueType, hookContext, new IllegalStateException("All fine, just a test"), List.of(genericHook), Map.of());
47+
48+
verify(genericHook).before(any(), any());
49+
verify(genericHook).after(any(), any(), any());
50+
verify(genericHook).finallyAfter(any(), any());
51+
verify(genericHook).error(any(), any(), any());
52+
}
53+
54+
private Object createDefaultValue(FlagValueType flagValueType) {
55+
switch (flagValueType) {
56+
case INTEGER:
57+
return 1;
58+
case BOOLEAN:
59+
return true;
60+
case STRING:
61+
return "defaultValue";
62+
case OBJECT:
63+
return "object";
64+
default:
65+
throw new IllegalArgumentException();
66+
}
67+
}
68+
3369
private EvaluationContext evaluationContextWithValue(String key, String value) {
3470
var result = new EvaluationContext();
3571
result.addStringAttribute(key, value);
3672
return result;
3773
}
3874

39-
private StringHook stringHookMock() {
40-
return spy(StringHook.class);
41-
}
42-
4375
}

0 commit comments

Comments
 (0)