Skip to content

Commit 22d183b

Browse files
PR suggestion: call hooks as early as possible
Signed-off-by: Alexandra Oberaigner <alexandra.oberaigner@dynatrace.com>
1 parent 92b6dc4 commit 22d183b

File tree

3 files changed

+54
-42
lines changed

3 files changed

+54
-42
lines changed

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

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,63 @@
66
import java.util.Optional;
77
import lombok.extern.slf4j.Slf4j;
88

9+
/**
10+
* Helper class to run hooks. Initialize {@link HookSupportData} by calling setHooks, setHookContexts
11+
* & updateEvaluationContext in this exact order.
12+
*/
913
@Slf4j
1014
class HookSupport {
1115

1216
/**
13-
* Updates the evaluation context in the given data object's eval context and each hooks eval context.
17+
* Sets the {@link Hook}-{@link HookContext}-{@link Pair} list in the given data object with {@link HookContext}
18+
* set to null. Filters hooks by supported {@link FlagValueType}.
1419
*
1520
* @param hookSupportData the data object to modify
16-
* @param evaluationContext the new context to set
21+
* @param hooks the hooks to set
22+
* @param type the flag value type to filter unsupported hooks
1723
*/
18-
public void updateEvaluationContext(HookSupportData hookSupportData, EvaluationContext evaluationContext) {
19-
hookSupportData.evaluationContext = evaluationContext;
20-
if (hookSupportData.hooks != null) {
21-
for (Pair<Hook, HookContext> hookContextPair : hookSupportData.hooks) {
22-
hookContextPair.getValue().setCtx(evaluationContext);
24+
public void setHooks(HookSupportData hookSupportData, List<Hook> hooks, FlagValueType type) {
25+
List<Pair<Hook, HookContext>> hookContextPairs = new ArrayList<>();
26+
for (Hook hook : hooks) {
27+
if (hook.supportsFlagValueType(type)) {
28+
hookContextPairs.add(Pair.of(hook, null));
2329
}
2430
}
31+
hookSupportData.hooks = hookContextPairs;
2532
}
2633

2734
/**
28-
* Sets the {@link Hook}-{@link HookContext}-{@link Pair} list in the given data object.
35+
* Creates & sets a {@link HookContext} for every {@link Hook}-{@link HookContext}-{@link Pair}
36+
* in the given data object with a new {@link HookData} instance.
2937
*
3038
* @param hookSupportData the data object to modify
31-
* @param hooks the new hooks to set
32-
* @param sharedContext the shared context across all hooks from which each hook's {@link HookContext} is
33-
* created
34-
* @param evaluationContext the evaluation context which is
39+
* @param sharedContext the shared context from which the new {@link HookContext} is created
3540
*/
36-
public void setHookSupportDataHooks(
37-
HookSupportData hookSupportData,
38-
List<Hook> hooks,
39-
SharedHookContext sharedContext,
40-
EvaluationContext evaluationContext) {
41-
List<Pair<Hook, HookContext>> hookContextPairs = new ArrayList<>();
42-
for (Hook hook : hooks) {
43-
if (hook.supportsFlagValueType(sharedContext.getType())) {
44-
HookContext curContext = sharedContext.hookContextFor(evaluationContext, new DefaultHookData());
45-
hookContextPairs.add(Pair.of(hook, curContext));
41+
public void setHookContexts(HookSupportData hookSupportData, SharedHookContext sharedContext) {
42+
for (int i = 0; i < hookSupportData.hooks.size(); i++) {
43+
Pair<Hook, HookContext> hookContextPair = hookSupportData.hooks.get(i);
44+
HookContext curHookContext = sharedContext.hookContextFor(null, new DefaultHookData());
45+
Pair<Hook, HookContext> updatedPair = Pair.of(hookContextPair.getKey(), curHookContext);
46+
hookSupportData.hooks.set(i, updatedPair);
47+
}
48+
}
49+
50+
/**
51+
* Updates the evaluation context in the given data object's eval context and each hooks eval context.
52+
*
53+
* @param hookSupportData the data object to modify
54+
* @param evaluationContext the new context to set
55+
*/
56+
public void updateEvaluationContext(HookSupportData hookSupportData, EvaluationContext evaluationContext) {
57+
hookSupportData.evaluationContext = evaluationContext;
58+
if (hookSupportData.hooks != null) {
59+
for (Pair<Hook, HookContext> hookContextPair : hookSupportData.hooks) {
60+
var curHookContext = hookContextPair.getValue();
61+
if (curHookContext != null) {
62+
curHookContext.setCtx(evaluationContext);
63+
}
4664
}
4765
}
48-
hookSupportData.hooks = hookContextPairs;
4966
}
5067

5168
public void executeBeforeHooks(HookSupportData data) {
@@ -57,9 +74,10 @@ public void executeBeforeHooks(HookSupportData data) {
5774
var hook = hookContextPair.getKey();
5875
var hookContext = hookContextPair.getValue();
5976

60-
Optional<EvaluationContext> returnedEvalContext = Optional.ofNullable(
61-
hook.before(hookContext, data.getHints()))
62-
.orElse(Optional.empty());
77+
Optional<EvaluationContext> returnedEvalContext = hook.before(hookContext, data.getHints());
78+
if (returnedEvalContext == null) {
79+
returnedEvalContext = Optional.empty();
80+
}
6381
if (returnedEvalContext.isPresent()) {
6482
// update shared evaluation context for all hooks
6583
updateEvaluationContext(data, data.getEvaluationContext().merge(returnedEvalContext.get()));

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,14 @@ private <T> FlagEvaluationDetails<T> evaluateFlag(
173173
final var provider = stateManager.getProvider();
174174
final var state = stateManager.getState();
175175

176+
// Hooks are initialized as early as possible to enable the execution of error stages
176177
var mergedHooks = ObjectUtils.merge(
177178
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getMutableHooks());
179+
hookSupport.setHooks(hookSupportData, mergedHooks, type);
178180

179181
var sharedHookContext =
180182
new SharedHookContext(key, type, this.getMetadata(), provider.getMetadata(), defaultValue);
181-
182-
hookSupport.setHookSupportDataHooks(hookSupportData, mergedHooks, sharedHookContext, ctx);
183+
hookSupport.setHookContexts(hookSupportData, sharedHookContext);
183184

184185
var evalContext = mergeEvaluationContext(ctx);
185186
hookSupport.updateEvaluationContext(hookSupportData, evalContext);

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ void shouldMergeEvaluationContextsOnBeforeHooksCorrectly() {
3535

3636
var sharedContext = getBaseHookContextForType(FlagValueType.STRING);
3737
var hookSupportData = new HookSupportData();
38-
hookSupport.setHookSupportDataHooks(
39-
hookSupportData, Arrays.asList(hook1, hook2), sharedContext, baseEvalContext);
38+
hookSupport.setHooks(hookSupportData, Arrays.asList(hook1, hook2), FlagValueType.STRING);
39+
hookSupport.setHookContexts(hookSupportData, sharedContext);
4040
hookSupport.updateEvaluationContext(hookSupportData, baseEvalContext);
4141

4242
hookSupport.executeBeforeHooks(hookSupportData);
@@ -55,11 +55,7 @@ void shouldAlwaysCallGenericHook(FlagValueType flagValueType) {
5555
Hook<?> genericHook = mockGenericHook();
5656

5757
var hookSupportData = new HookSupportData();
58-
hookSupport.setHookSupportDataHooks(
59-
hookSupportData,
60-
List.of(genericHook),
61-
getBaseHookContextForType(flagValueType),
62-
ImmutableContext.EMPTY);
58+
hookSupport.setHooks(hookSupportData, List.of(genericHook), flagValueType);
6359

6460
callAllHooks(hookSupportData);
6561

@@ -75,8 +71,8 @@ void shouldAlwaysCallGenericHook(FlagValueType flagValueType) {
7571
void shouldPassDataAcrossStages(FlagValueType flagValueType) {
7672
var testHook = new TestHookWithData();
7773
var hookSupportData = new HookSupportData();
78-
hookSupport.setHookSupportDataHooks(
79-
hookSupportData, List.of(testHook), getBaseHookContextForType(flagValueType), ImmutableContext.EMPTY);
74+
hookSupport.setHooks(hookSupportData, List.of(testHook), flagValueType);
75+
hookSupport.setHookContexts(hookSupportData, getBaseHookContextForType(flagValueType));
8076

8177
hookSupport.executeBeforeHooks(hookSupportData);
8278
assertHookData(testHook, "before");
@@ -101,11 +97,8 @@ void shouldIsolateDataBetweenHooks(FlagValueType flagValueType) {
10197
var testHook2 = new TestHookWithData(2);
10298

10399
var hookSupportData = new HookSupportData();
104-
hookSupport.setHookSupportDataHooks(
105-
hookSupportData,
106-
List.of(testHook1, testHook2),
107-
getBaseHookContextForType(flagValueType),
108-
ImmutableContext.EMPTY);
100+
hookSupport.setHooks(hookSupportData, List.of(testHook1, testHook2), flagValueType);
101+
hookSupport.setHookContexts(hookSupportData, getBaseHookContextForType(flagValueType));
109102

110103
callAllHooks(hookSupportData);
111104

0 commit comments

Comments
 (0)