Skip to content

Commit 0c0bd32

Browse files
authored
feat: introduce UnimplementedHook to avoid authors having to define empty functions (#55)
* feat: introduce UnimplementedHook to avoid authors having to define empty functions Signed-off-by: Skye Gill <[email protected]> * implemented TestBeforeHookNilContext Signed-off-by: Skye Gill <[email protected]> Signed-off-by: Skye Gill <[email protected]>
1 parent 04649c5 commit 0c0bd32

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ func main() {
3333
}
3434
```
3535

36+
### Hooks
37+
38+
Implement your own hook by conforming to the [Hook interface](./pkg/openfeature/hooks.go).
39+
40+
To satisfy the interface all methods (`Before`/`After`/`Finally`/`Error`) need to be defined. To avoid defining empty functions
41+
make use of the `UnimplementedHook` struct (which already implements all the empty functions).
42+
43+
```go
44+
type MyHook struct {
45+
openfeature.UnimplementedHook
46+
}
47+
48+
// overrides UnimplementedHook's Error function
49+
func (h MyHook) Error(hookContext openfeature.HookContext, err error, hookHints openfeature.HookHints) {
50+
log.Println(err)
51+
}
52+
```
53+
54+
Register the hook at global, client or invocation level.
55+
3656
## Configuration
3757

3858
### Logging

pkg/openfeature/client_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,29 @@ func TestFlattenContext(t *testing.T) {
557557
})
558558
}
559559
}
560+
561+
// TestBeforeHookNilContext asserts that when a Before hook returns a nil EvaluationContext it doesn't overwrite the
562+
// existing EvaluationContext
563+
func TestBeforeHookNilContext(t *testing.T) {
564+
defer t.Cleanup(initSingleton)
565+
ctrl := gomock.NewController(t)
566+
567+
mockProvider := NewMockFeatureProvider(ctrl)
568+
mockProvider.EXPECT().Metadata().AnyTimes()
569+
mockProvider.EXPECT().Hooks().AnyTimes()
570+
SetProvider(mockProvider)
571+
572+
hookNilContext := UnimplementedHook{}
573+
574+
client := NewClient("test")
575+
attributes := map[string]interface{}{"should": "persist"}
576+
evalCtx := EvaluationContext{Attributes: attributes}
577+
mockProvider.EXPECT().BooleanEvaluation(gomock.Any(), gomock.Any(), attributes)
578+
579+
_, err := client.BooleanValue(
580+
"foo", false, evalCtx, NewEvaluationOptions([]Hook{hookNilContext}, HookHints{}),
581+
)
582+
if err != nil {
583+
t.Fatal(err)
584+
}
585+
}

pkg/openfeature/hooks.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,26 @@ func (h HookContext) ProviderMetadata() Metadata {
6565
func (h HookContext) EvaluationContext() EvaluationContext {
6666
return h.evaluationContext
6767
}
68+
69+
// check at compile time that UnimplementedHook implements the Hook interface
70+
var _ Hook = UnimplementedHook{}
71+
72+
// UnimplementedHook implements all hook methods with empty functions
73+
// Include UnimplementedHook in your hook struct to avoid defining empty functions
74+
// e.g.
75+
// type MyHook struct {
76+
// UnimplementedHook
77+
// }
78+
type UnimplementedHook struct{}
79+
80+
func (u UnimplementedHook) Before(hookContext HookContext, hookHints HookHints) (*EvaluationContext, error) {
81+
return nil, nil
82+
}
83+
84+
func (u UnimplementedHook) After(hookContext HookContext, flagEvaluationDetails EvaluationDetails, hookHints HookHints) error {
85+
return nil
86+
}
87+
88+
func (u UnimplementedHook) Error(hookContext HookContext, err error, hookHints HookHints) {}
89+
90+
func (u UnimplementedHook) Finally(hookContext HookContext, hookHints HookHints) {}

0 commit comments

Comments
 (0)