55import java .util .List ;
66import java .util .Map ;
77import java .util .Optional ;
8- import java .util .function .Consumer ;
8+ import java .util .function .BiConsumer ;
99import lombok .RequiredArgsConstructor ;
1010import lombok .extern .slf4j .Slf4j ;
1111
1515class HookSupport {
1616
1717 public EvaluationContext beforeHooks (
18- FlagValueType flagValueType , HookContext hookCtx , List <Hook > hooks , Map <String , Object > hints ) {
19- return callBeforeHooks (flagValueType , hookCtx , hooks , hints );
18+ FlagValueType flagValueType ,
19+ HookContext hookCtx ,
20+ List <Pair <Hook , HookData >> hookDataPairs ,
21+ Map <String , Object > hints ) {
22+ return callBeforeHooks (flagValueType , hookCtx , hookDataPairs , hints );
2023 }
2124
2225 public void afterHooks (
2326 FlagValueType flagValueType ,
2427 HookContext hookContext ,
2528 FlagEvaluationDetails details ,
26- List <Hook > hooks ,
29+ List <Pair < Hook , HookData >> hookDataPairs ,
2730 Map <String , Object > hints ) {
28- executeHooksUnchecked (flagValueType , hooks , hook -> hook .after (hookContext , details , hints ));
31+ executeHooksUnchecked (
32+ flagValueType , hookDataPairs , hookContext , (hook , ctx ) -> hook .after (ctx , details , hints ));
2933 }
3034
3135 public void afterAllHooks (
3236 FlagValueType flagValueType ,
3337 HookContext hookCtx ,
3438 FlagEvaluationDetails details ,
35- List <Hook > hooks ,
39+ List <Pair < Hook , HookData >> hookDataPairs ,
3640 Map <String , Object > hints ) {
37- executeHooks (flagValueType , hooks , "finally" , hook -> hook .finallyAfter (hookCtx , details , hints ));
41+ executeHooks (
42+ flagValueType ,
43+ hookDataPairs ,
44+ hookCtx ,
45+ "finally" ,
46+ (hook , ctx ) -> hook .finallyAfter (ctx , details , hints ));
3847 }
3948
4049 public void errorHooks (
4150 FlagValueType flagValueType ,
4251 HookContext hookCtx ,
4352 Exception e ,
44- List <Hook > hooks ,
53+ List <Pair < Hook , HookData >> hookDataPairs ,
4554 Map <String , Object > hints ) {
46- executeHooks (flagValueType , hooks , "error" , hook -> hook .error (hookCtx , e , hints ));
55+ executeHooks (flagValueType , hookDataPairs , hookCtx , "error" , (hook , ctx ) -> hook .error (ctx , e , hints ));
56+ }
57+
58+ public List <Pair <Hook , HookData >> getHookDataPairs (List <Hook > hooks , FlagValueType flagValueType ) {
59+ var pairs = new ArrayList <Pair <Hook , HookData >>();
60+ for (Hook hook : hooks ) {
61+ if (hook .supportsFlagValueType (flagValueType )) {
62+ pairs .add (Pair .of (hook , HookData .create ()));
63+ }
64+ }
65+ return pairs ;
4766 }
4867
4968 private <T > void executeHooks (
50- FlagValueType flagValueType , List <Hook > hooks , String hookMethod , Consumer <Hook <T >> hookCode ) {
51- if (hooks != null ) {
52- for (Hook hook : hooks ) {
53- if (hook .supportsFlagValueType (flagValueType )) {
54- executeChecked (hook , hookCode , hookMethod );
55- }
69+ FlagValueType flagValueType ,
70+ List <Pair <Hook , HookData >> hookDataPairs ,
71+ HookContext hookContext ,
72+ String hookMethod ,
73+ BiConsumer <Hook <T >, HookContext > hookCode ) {
74+ if (hookDataPairs != null ) {
75+ for (Pair <Hook , HookData > hookDataPair : hookDataPairs ) {
76+ Hook hook = hookDataPair .getLeft ();
77+ HookData hookData = hookDataPair .getRight ();
78+ executeChecked (hook , hookData , hookContext , hookCode , hookMethod );
5679 }
5780 }
5881 }
5982
6083 // before, error, and finally hooks shouldn't throw
61- private <T > void executeChecked (Hook <T > hook , Consumer <Hook <T >> hookCode , String hookMethod ) {
84+ private <T > void executeChecked (
85+ Hook <T > hook ,
86+ HookData hookData ,
87+ HookContext hookContext ,
88+ BiConsumer <Hook <T >, HookContext > hookCode ,
89+ String hookMethod ) {
6290 try {
63- hookCode .accept (hook );
91+ var hookCtxWithData = new HookContextWithData (hookContext , hookData );
92+ hookCode .accept (hook , hookCtxWithData );
6493 } catch (Exception exception ) {
6594 log .error (
6695 "Unhandled exception when running {} hook {} (only 'after' hooks should throw)" ,
@@ -71,29 +100,41 @@ private <T> void executeChecked(Hook<T> hook, Consumer<Hook<T>> hookCode, String
71100 }
72101
73102 // after hooks can throw in order to do validation
74- private <T > void executeHooksUnchecked (FlagValueType flagValueType , List <Hook > hooks , Consumer <Hook <T >> hookCode ) {
75- if (hooks != null ) {
76- for (Hook hook : hooks ) {
77- if (hook .supportsFlagValueType (flagValueType )) {
78- hookCode .accept (hook );
79- }
103+ private <T > void executeHooksUnchecked (
104+ FlagValueType flagValueType ,
105+ List <Pair <Hook , HookData >> hookDataPairs ,
106+ HookContext hookContext ,
107+ BiConsumer <Hook <T >, HookContext > hookCode ) {
108+ if (hookDataPairs != null ) {
109+ for (Pair <Hook , HookData > hookDataPair : hookDataPairs ) {
110+ Hook hook = hookDataPair .getLeft ();
111+ HookData hookData = hookDataPair .getRight ();
112+ var hookCtxWithData = new HookContextWithData (hookContext , hookData );
113+ hookCode .accept (hook , hookCtxWithData );
80114 }
81115 }
82116 }
83117
84118 private EvaluationContext callBeforeHooks (
85- FlagValueType flagValueType , HookContext hookCtx , List <Hook > hooks , Map <String , Object > hints ) {
119+ FlagValueType flagValueType ,
120+ HookContext hookCtx ,
121+ List <Pair <Hook , HookData >> hookDataPairs ,
122+ Map <String , Object > hints ) {
86123 // These traverse backwards from normal.
87- List <Hook > reversedHooks = new ArrayList <>(hooks );
124+ List <Pair < Hook , HookData >> reversedHooks = new ArrayList <>(hookDataPairs );
88125 Collections .reverse (reversedHooks );
89126 EvaluationContext context = hookCtx .getCtx ();
90- for (Hook hook : reversedHooks ) {
91- if (hook .supportsFlagValueType (flagValueType )) {
92- Optional <EvaluationContext > optional =
93- Optional .ofNullable (hook .before (hookCtx , hints )).orElse (Optional .empty ());
94- if (optional .isPresent ()) {
95- context = context .merge (optional .get ());
96- }
127+
128+ for (Pair <Hook , HookData > hookDataPair : reversedHooks ) {
129+ Hook hook = hookDataPair .getLeft ();
130+ HookData hookData = hookDataPair .getRight ();
131+
132+ // Create a new context with this hook's data
133+ HookContext contextWithHookData = new HookContextWithData (hookCtx , hookData );
134+ Optional <EvaluationContext > optional =
135+ Optional .ofNullable (hook .before (contextWithHookData , hints )).orElse (Optional .empty ());
136+ if (optional .isPresent ()) {
137+ context = context .merge (optional .get ());
97138 }
98139 }
99140 return context ;
0 commit comments