Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: go context in hook calls #163

Merged
merged 1 commit into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions pkg/openfeature/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,18 +598,18 @@ func (c *Client) evaluate(
}

defer func() {
c.finallyHooks(hookCtx, providerInvocationClientApiHooks, options)
c.finallyHooks(ctx, hookCtx, providerInvocationClientApiHooks, options)
}()

evalCtx, err = c.beforeHooks(hookCtx, apiClientInvocationProviderHooks, evalCtx, options)
evalCtx, err = c.beforeHooks(ctx, hookCtx, apiClientInvocationProviderHooks, evalCtx, options)
hookCtx.evaluationContext = evalCtx
if err != nil {
c.logger().Error(
err, "before hook", "flag", flag, "defaultValue", defaultValue,
"evaluationContext", evalCtx, "evaluationOptions", options, "type", flagType.String(),
)
err = fmt.Errorf("before hook: %w", err)
c.errorHooks(hookCtx, providerInvocationClientApiHooks, err, options)
c.errorHooks(ctx, hookCtx, providerInvocationClientApiHooks, err, options)
return evalDetails, err
}

Expand Down Expand Up @@ -648,21 +648,21 @@ func (c *Client) evaluate(
"errMessage", resolution.ResolutionError.message,
)
err = fmt.Errorf("error code: %w", err)
c.errorHooks(hookCtx, providerInvocationClientApiHooks, err, options)
c.errorHooks(ctx, hookCtx, providerInvocationClientApiHooks, err, options)
evalDetails.ResolutionDetail = resolution.ResolutionDetail()
evalDetails.Reason = ErrorReason
return evalDetails, err
}
evalDetails.Value = resolution.Value
evalDetails.ResolutionDetail = resolution.ResolutionDetail()

if err := c.afterHooks(hookCtx, providerInvocationClientApiHooks, evalDetails, options); err != nil {
if err := c.afterHooks(ctx, hookCtx, providerInvocationClientApiHooks, evalDetails, options); err != nil {
c.logger().Error(
err, "after hook", "flag", flag, "defaultValue", defaultValue,
"evaluationContext", evalCtx, "evaluationOptions", options, "type", flagType.String(),
)
err = fmt.Errorf("after hook: %w", err)
c.errorHooks(hookCtx, providerInvocationClientApiHooks, err, options)
c.errorHooks(ctx, hookCtx, providerInvocationClientApiHooks, err, options)
return evalDetails, err
}

Expand All @@ -682,13 +682,13 @@ func flattenContext(evalCtx EvaluationContext) FlattenedContext {
}

func (c *Client) beforeHooks(
hookCtx HookContext, hooks []Hook, evalCtx EvaluationContext, options EvaluationOptions,
ctx context.Context, hookCtx HookContext, hooks []Hook, evalCtx EvaluationContext, options EvaluationOptions,
) (EvaluationContext, error) {
c.logger().V(debug).Info("executing before hooks")
defer c.logger().V(debug).Info("executed before hooks")

for _, hook := range hooks {
resultEvalCtx, err := hook.Before(hookCtx, options.hookHints)
resultEvalCtx, err := hook.Before(ctx, hookCtx, options.hookHints)
if resultEvalCtx != nil {
hookCtx.evaluationContext = *resultEvalCtx
}
Expand All @@ -701,35 +701,35 @@ func (c *Client) beforeHooks(
}

func (c *Client) afterHooks(
hookCtx HookContext, hooks []Hook, evalDetails InterfaceEvaluationDetails, options EvaluationOptions,
ctx context.Context, hookCtx HookContext, hooks []Hook, evalDetails InterfaceEvaluationDetails, options EvaluationOptions,
) error {
c.logger().V(debug).Info("executing after hooks")
defer c.logger().V(debug).Info("executed after hooks")

for _, hook := range hooks {
if err := hook.After(hookCtx, evalDetails, options.hookHints); err != nil {
if err := hook.After(ctx, hookCtx, evalDetails, options.hookHints); err != nil {
return err
}
}

return nil
}

func (c *Client) errorHooks(hookCtx HookContext, hooks []Hook, err error, options EvaluationOptions) {
func (c *Client) errorHooks(ctx context.Context, hookCtx HookContext, hooks []Hook, err error, options EvaluationOptions) {
c.logger().V(debug).Info("executing error hooks")
defer c.logger().V(debug).Info("executed error hooks")

for _, hook := range hooks {
hook.Error(hookCtx, err, options.hookHints)
hook.Error(ctx, hookCtx, err, options.hookHints)
}
}

func (c *Client) finallyHooks(hookCtx HookContext, hooks []Hook, options EvaluationOptions) {
func (c *Client) finallyHooks(ctx context.Context, hookCtx HookContext, hooks []Hook, options EvaluationOptions) {
c.logger().V(debug).Info("executing finally hooks")
defer c.logger().V(debug).Info("executed finally hooks")

for _, hook := range hooks {
hook.Finally(hookCtx, options.hookHints)
hook.Finally(ctx, hookCtx, options.hookHints)
}
}

Expand Down
11 changes: 5 additions & 6 deletions pkg/openfeature/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func TestRequirement_1_4_2__1_4_5__1_4_6(t *testing.T) {
incorrectVariant = "Incorrect variant returned!"
incorrectReason = "Incorrect reason returned!"
)
var objectValue = map[string]interface{}{"foo": 1, "bar": true, "baz": "buz"}
objectValue := map[string]interface{}{"foo": 1, "bar": true, "baz": "buz"}

ctrl := gomock.NewController(t)
mockProvider := NewMockFeatureProvider(ctrl)
Expand Down Expand Up @@ -168,7 +168,6 @@ func TestRequirement_1_4_2__1_4_5__1_4_6(t *testing.T) {
})

evDetails, err := client.StringValueDetails(context.Background(), flagKey, "", EvaluationContext{})

if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -819,17 +818,17 @@ func TestSwitchingProvidersMidEvaluationCausesNoImpactToEvaluation(t *testing.T)
mockProvider1.EXPECT().Hooks().Return([]Hook{mockProvider1Hook}).AnyTimes()

// set new provider during initial provider's Before hook
mockProvider1Hook.EXPECT().Before(gomock.Any(), gomock.Any()).
DoAndReturn(func(_ HookContext, _ HookHints) (*EvaluationContext, error) {
mockProvider1Hook.EXPECT().Before(gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ context.Context, _ HookContext, _ HookHints) (*EvaluationContext, error) {
SetProvider(mockProvider2)
return nil, nil
})
SetProvider(mockProvider1)

mockProvider1.EXPECT().BooleanEvaluation(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
// ensure that the first provider's hooks are still fired
mockProvider1Hook.EXPECT().After(gomock.Any(), gomock.Any(), gomock.Any())
mockProvider1Hook.EXPECT().Finally(gomock.Any(), gomock.Any())
mockProvider1Hook.EXPECT().After(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
mockProvider1Hook.EXPECT().Finally(gomock.Any(), gomock.Any(), gomock.Any())

client := NewClient("test")
_, err := client.BooleanValue(context.Background(), "foo", true, EvaluationContext{})
Expand Down
20 changes: 10 additions & 10 deletions pkg/openfeature/hooks.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package openfeature

import "context"

// Hook allows application developers to add arbitrary behavior to the flag evaluation lifecycle.
// They operate similarly to middleware in many web frameworks.
// https://github.com/open-feature/spec/blob/main/specification/hooks.md
type Hook interface {
Before(hookContext HookContext, hookHints HookHints) (*EvaluationContext, error)
After(hookContext HookContext, flagEvaluationDetails InterfaceEvaluationDetails, hookHints HookHints) error
Error(hookContext HookContext, err error, hookHints HookHints)
Finally(hookContext HookContext, hookHints HookHints)
Before(ctx context.Context, hookContext HookContext, hookHints HookHints) (*EvaluationContext, error)
After(ctx context.Context, hookContext HookContext, flagEvaluationDetails InterfaceEvaluationDetails, hookHints HookHints) error
Error(ctx context.Context, hookContext HookContext, err error, hookHints HookHints)
Finally(ctx context.Context, hookContext HookContext, hookHints HookHints)
}

// HookHints contains a map of hints for hooks
Expand Down Expand Up @@ -98,14 +100,12 @@ var _ Hook = UnimplementedHook{}
// }
type UnimplementedHook struct{}

func (u UnimplementedHook) Before(hookContext HookContext, hookHints HookHints) (*EvaluationContext, error) {
func (UnimplementedHook) Before(context.Context, HookContext, HookHints) (*EvaluationContext, error) {
return nil, nil
}

func (u UnimplementedHook) After(hookContext HookContext, flagEvaluationDetails InterfaceEvaluationDetails, hookHints HookHints) error {
func (UnimplementedHook) After(context.Context, HookContext, InterfaceEvaluationDetails, HookHints) error {
return nil
}

func (u UnimplementedHook) Error(hookContext HookContext, err error, hookHints HookHints) {}

func (u UnimplementedHook) Finally(hookContext HookContext, hookHints HookHints) {}
func (UnimplementedHook) Error(context.Context, HookContext, error, HookHints) {}
func (UnimplementedHook) Finally(context.Context, HookContext, HookHints) {}
70 changes: 35 additions & 35 deletions pkg/openfeature/hooks_mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading