OpenFeature: Use types from Go Feature Flag pkg (#108802)

* Use go feature flag types

* Add go-feature-flag module

* Update go.mod

* Update test

* Fix
This commit is contained in:
Tania
2025-07-29 15:05:59 +02:00
committed by GitHub
parent ef9f9c2d8e
commit 2c7349f863
13 changed files with 189 additions and 65 deletions
+1 -1
View File
@@ -101,7 +101,7 @@ require (
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
go.mongodb.org/mongo-driver v1.16.1 // indirect
go.mongodb.org/mongo-driver v1.17.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect
+2 -2
View File
@@ -310,8 +310,8 @@ github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8=
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
+4 -3
View File
@@ -14,8 +14,9 @@ import (
"strconv"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/util/proxyutil"
goffmodel "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/model"
)
func (b *APIBuilder) proxyAllFlagReq(isAuthedUser bool, w http.ResponseWriter, r *http.Request) {
@@ -27,13 +28,13 @@ func (b *APIBuilder) proxyAllFlagReq(isAuthedUser bool, w http.ResponseWriter, r
proxy.ModifyResponse = func(resp *http.Response) error {
if resp.StatusCode == http.StatusOK && !isAuthedUser {
var result featuremgmt.OFREPBulkResponse
var result goffmodel.OFREPBulkEvaluateSuccessResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}
_ = resp.Body.Close()
var filteredFlags []featuremgmt.OFREPFlag
var filteredFlags []goffmodel.OFREPFlagBulkEvaluateSuccessResponse
for _, f := range result.Flags {
if isPublicFlag(f.Key) {
filteredFlags = append(filteredFlags, f)
+2 -2
View File
@@ -3,7 +3,7 @@ package ofrep
import (
"net/http"
"github.com/grafana/grafana/pkg/services/featuremgmt"
goffmodel "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/model"
)
func (b *APIBuilder) evalAllFlagsStatic(isAuthedUser bool, w http.ResponseWriter, r *http.Request) {
@@ -15,7 +15,7 @@ func (b *APIBuilder) evalAllFlagsStatic(isAuthedUser bool, w http.ResponseWriter
}
if !isAuthedUser {
var publicOnly []featuremgmt.OFREPFlag
var publicOnly []goffmodel.OFREPFlagBulkEvaluateSuccessResponse
for _, flag := range result.Flags {
if isPublicFlag(flag.Key) {
+25 -31
View File
@@ -7,13 +7,14 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/open-feature/go-sdk/openfeature"
goffmodel "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/model"
)
// StaticFlagEvaluator provides methods for evaluating static feature flags
// it is only used when static provider is configured
type StaticFlagEvaluator interface {
EvalFlag(ctx context.Context, flagKey string) (openfeature.BooleanEvaluationDetails, error)
EvalAllFlags(ctx context.Context) (OFREPBulkResponse, error)
EvalFlag(ctx context.Context, flagKey string) (goffmodel.OFREPEvaluateSuccessResponse, error)
EvalAllFlags(ctx context.Context) (goffmodel.OFREPBulkEvaluateSuccessResponse, error)
}
// CreateStaticEvaluator is a dependency for ofrep APIBuilder
@@ -53,22 +54,29 @@ type staticEvaluator struct {
log log.Logger
}
func (s *staticEvaluator) EvalFlag(ctx context.Context, flagKey string) (openfeature.BooleanEvaluationDetails, error) {
func (s *staticEvaluator) EvalFlag(ctx context.Context, flagKey string) (goffmodel.OFREPEvaluateSuccessResponse, error) {
result, err := s.client.BooleanValueDetails(ctx, flagKey, false, openfeature.TransactionContext(ctx))
if err != nil {
return openfeature.BooleanEvaluationDetails{}, fmt.Errorf("failed to evaluate flag %s: %w", flagKey, err)
return goffmodel.OFREPEvaluateSuccessResponse{}, fmt.Errorf("failed to evaluate flag %s: %w", flagKey, err)
}
resp := goffmodel.OFREPEvaluateSuccessResponse{
Key: flagKey,
Value: result.Value,
Reason: "static provider evaluation result",
Variant: result.Variant,
Metadata: result.FlagMetadata,
}
return result, nil
return resp, nil
}
func (s *staticEvaluator) EvalAllFlags(ctx context.Context) (OFREPBulkResponse, error) {
func (s *staticEvaluator) EvalAllFlags(ctx context.Context) (goffmodel.OFREPBulkEvaluateSuccessResponse, error) {
flags, err := s.provider.ListFlags()
if err != nil {
return OFREPBulkResponse{}, fmt.Errorf("static provider failed to list all flags: %w", err)
return goffmodel.OFREPBulkEvaluateSuccessResponse{}, fmt.Errorf("static provider failed to list all flags: %w", err)
}
allFlags := make([]OFREPFlag, 0, len(flags))
allFlags := make([]goffmodel.OFREPFlagBulkEvaluateSuccessResponse, 0, len(flags))
for _, flagKey := range flags {
result, err := s.client.BooleanValueDetails(ctx, flagKey, false, openfeature.TransactionContext(ctx))
if err != nil {
@@ -76,32 +84,18 @@ func (s *staticEvaluator) EvalAllFlags(ctx context.Context) (OFREPBulkResponse,
continue
}
allFlags = append(allFlags, OFREPFlag{
Key: flagKey,
Value: result.Value,
Reason: "static provider evaluation result",
Variant: result.Variant,
allFlags = append(allFlags, goffmodel.OFREPFlagBulkEvaluateSuccessResponse{
OFREPEvaluateSuccessResponse: goffmodel.OFREPEvaluateSuccessResponse{
Key: flagKey,
Value: result.Value,
Reason: "static provider evaluation result",
Variant: result.Variant,
Metadata: result.FlagMetadata,
},
ErrorCode: string(result.ErrorCode),
ErrorDetails: result.ErrorMessage,
})
}
return OFREPBulkResponse{Flags: allFlags}, nil
}
// OFREPBulkResponse represents the response for bulk flag evaluation
type OFREPBulkResponse struct {
Flags []OFREPFlag `json:"flags"`
Metadata map[string]any `json:"metadata,omitempty"`
}
// OFREPFlag represents a single flag in the bulk response
type OFREPFlag struct {
Key string `json:"key"`
Value any `json:"value"`
Reason string `json:"reason"`
Variant string `json:"variant,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorDetails string `json:"errorDetails,omitempty"`
return goffmodel.OFREPBulkEvaluateSuccessResponse{Flags: allFlags}, nil
}
+4 -9
View File
@@ -40,14 +40,9 @@ func TestIntegrationFeatures(t *testing.T) {
require.Equal(t, 200, rsp.Response.StatusCode)
require.JSONEq(t, `{
"Value": true,
"FlagKey": "`+flag+`",
"FlagType": 0,
"Variant": "enabled",
"Reason": "STATIC",
"ErrorCode": "",
"ErrorMessage": "",
"FlagMetadata": {}
}`, string(rsp.Body))
"value": true,
"key":"`+flag+`",
"reason":"static provider evaluation result",
"variant":"enabled"}`, string(rsp.Body))
})
}