Hooks
A Hook taps into one or more of the flag evaluation's lifecycle events (before/after/error/finally) to perform the same action at that point for every evaluation.
Objective
Create and integrate a spec compliant hook that validates that the result of a flag evaluation is a hex color.Prerequisites
- Golang 1.17+
Repository setup
Hooks
written for the go-sdk
are all maintained in the
go-sdk-contrib
repository, containing both hooks
and providers
.
The following commands can be used to setup the go-sdk-contrib
repository,
this clones the repository and sets up your hook specific go module under /hooks/MY-NEW-HOOK-NAME
adding a go.mod
and README.md
file. The module will then be referenced in the top level go.work
file.
git clone https://github.com/open-feature/go-sdk-contrib.git
cd go-sdk-contrib
make HOOK=MY-NEW-HOOK-NAME new-hook
make workspace-init
Creating the hook
In order for the Hook
to be compatible with the OpenFeature go-sdk
, it will need to comply to the OpenFeature spec.
For the go-sdk
this means that it will need to conform to the following interface:
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)
}
In order to conform to the interface we are forced to implement all of these functions, despite only wanting to tap into
the After
lifecycle event. Let's leave the other functions empty to achieve this:
// Hook validates the flag evaluation details After flag resolution
type Hook struct {
Validator validator
}
func (h Hook) Before(hookContext of.HookContext, hookHints of.HookHints) (*of.EvaluationContext, error) {
return nil, nil
}
func (h Hook) After(hookContext of.HookContext, flagEvaluationDetails of.InterfaceEvaluationDetails, hookHints of.HookHints) error {
err := h.Validator.IsValid(flagEvaluationDetails)
if err != nil {
return err
}
return nil
}
func (h Hook) Error(hookContext of.HookContext, err error, hookHints of.HookHints) {}
func (h Hook) Finally(hookContext of.HookContext, hookHints of.HookHints) {}
Notice the Validator
field of type validator
in the Hook
struct. This is defined as such:
type validator interface {
IsValid(flagEvaluationDetails of.InterfaceEvaluationDetails) error
}
This allows us to supply any validator that implements this function signature, you can either create your own validator or use one of the existing validators. This tutorial uses the existing hex regex validator.
Integrating the hook
- Install dependencies
go get github.com/open-feature/go-sdk
go get github.com/open-feature/go-sdk-contrib/hooks/validator
- Import the dependencies
package main
import (
"context"
"fmt"
"github.com/open-feature/go-sdk-contrib/hooks/validator/pkg/regex"
"github.com/open-feature/go-sdk-contrib/hooks/validator/pkg/validator"
"github.com/open-feature/go-sdk/pkg/openfeature"
"log"
)
- Create an instance of the
validator hook
struct using the regex hex validator
func main() {
hexValidator, err := regex.Hex()
if err != nil {
log.Fatal(err)
}
v := validator.Hook{Validator: hexValidator}
}
- Register the
NoopProvider
, this simply returns the given default value on flag evaluation. This step is optional, the sdk uses theNoopProvider
by default but we're explicitly setting it for completeness
openfeature.SetProvider(openfeature.NoopProvider{})
- Create the client, call the flag evaluation using the
validator hook
at the point of invocation
client := openfeature.NewClient("foo")
result, err := client.
StringValueDetails(
context.Background(),
"blue",
"invalidhex",
openfeature.EvaluationContext{},
openfeature.WithHooks(v),
)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("result:", result)
- Check that the flag evaluation returns an error as
invalidhex
is not a valid hex color
go run main.go
err: execute after hook: regex doesn't match on flag value
result {blue 1 {invalidhex }}
Note that despite getting an error we still get a result.