From 887c5cf86a194d6164a9422fcc09e5f52d168c63 Mon Sep 17 00:00:00 2001 From: Eli Treuherz Date: Thu, 13 Feb 2025 11:24:44 +0000 Subject: [PATCH] refactor: remove dependency on golang.org/x/exp The exp module does not have the same compatibility guarantees as the standard library. The fact that it stays on a pseudo-version instead of a proper semver tag, and the possibility for backwards-incompatible changes, makes depending on exp in a widely-imported library a bit risky. If a dependent of the SDK updated the version of exp they relied on, the MVS algorithm could select a version that broke the SDK's usage of exp's packages. Out of an abundance of caution, I'm trying to reduce the use of exp in our private shared libraries for this reason, and the OpenFeature SDK is our only external dependency that depends on it it. The uses are pretty small. I've replaced them with existing standard library functiosn where possible, or in one place implemented the functionality myself, since `maps.Values` is only available in the standard library from go 1.23. While in the area, I've also updated a couple of functions that are already replaceable with functions from the standard slices package. Signed-off-by: Eli Treuherz --- go.mod | 1 - go.sum | 10 -------- openfeature/event_executor.go | 40 +++++++++++++----------------- openfeature/event_executor_test.go | 3 +-- openfeature/openfeature_api.go | 14 ++--------- 5 files changed, 20 insertions(+), 48 deletions(-) diff --git a/go.mod b/go.mod index ecacff92..9c0695e5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/cucumber/godog v0.15.0 github.com/go-logr/logr v1.4.2 github.com/golang/mock v1.6.0 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/text v0.22.0 ) diff --git a/go.sum b/go.sum index 3ea50bfa..5b18458c 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= -github.com/cucumber/godog v0.14.1 h1:HGZhcOyyfaKclHjJ+r/q93iaTJZLKYW6Tv3HkmUE6+M= -github.com/cucumber/godog v0.14.1/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo= github.com/cucumber/godog v0.15.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= @@ -53,8 +51,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -69,12 +65,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/openfeature/event_executor.go b/openfeature/event_executor.go index ff381e44..10d69806 100644 --- a/openfeature/event_executor.go +++ b/openfeature/event_executor.go @@ -2,12 +2,10 @@ package openfeature import ( "fmt" + "log/slog" + "slices" "sync" "time" - - "log/slog" - - "golang.org/x/exp/maps" ) const defaultDomain = "" @@ -260,7 +258,7 @@ func (e *eventExecutor) startListeningAndShutdownOld(newProvider providerReferen // shutdown old provider handling // check if this provider is still bound - 1:N binding capability - if isBound(oldReference, e.defaultProviderReference, maps.Values(e.namedProviderReference)) { + if isBound(oldReference, e.defaultProviderReference, mapValues(e.namedProviderReference)) { return nil } @@ -360,26 +358,22 @@ func (e *eventExecutor) executeHandler(f func(details EventDetails), event Event }() } -// isRunning is a helper till we bump to the latest go version with slices.contains support -func isRunning(provider providerReference, activeProviders []providerReference) bool { - for _, activeProvider := range activeProviders { - if activeProvider.equals(provider) { - return true - } +// mapValues is a helper until we bump to a go version with maps.Values and slices.Collect +func mapValues[K comparable, V any](m map[K]V) []V { + var values []V + for _, value := range m { + values = append(values, value) } - return false + return values } -// isRunning is a helper to check if given provider is already in use -func isBound(provider providerReference, defaultProvider providerReference, namedProviders []providerReference) bool { - if provider.equals(defaultProvider) { - return true - } +// isRunning is a helper to check if the given provider is in the given list of providers +func isRunning(provider providerReference, activeProviders []providerReference) bool { + return slices.ContainsFunc(activeProviders, provider.equals) +} - for _, namedProvider := range namedProviders { - if provider.equals(namedProvider) { - return true - } - } - return false +// isBound is a helper to check if given provider is in the given provider or list of providers +func isBound(provider providerReference, defaultProvider providerReference, namedProviders []providerReference) bool { + return provider.equals(defaultProvider) || + slices.ContainsFunc(namedProviders, provider.equals) } diff --git a/openfeature/event_executor_test.go b/openfeature/event_executor_test.go index 0315c45f..fcad5e6c 100644 --- a/openfeature/event_executor_test.go +++ b/openfeature/event_executor_test.go @@ -3,10 +3,9 @@ package openfeature import ( "errors" "reflect" + "slices" "testing" "time" - - "golang.org/x/exp/slices" ) func init() { diff --git a/openfeature/openfeature_api.go b/openfeature/openfeature_api.go index 645a9218..893729dd 100644 --- a/openfeature/openfeature_api.go +++ b/openfeature/openfeature_api.go @@ -3,10 +3,10 @@ package openfeature import ( "errors" "fmt" + "slices" "sync" "github.com/go-logr/logr" - "golang.org/x/exp/maps" ) // evaluationAPI wraps OpenFeature evaluation API functionalities @@ -234,7 +234,7 @@ func (api *evaluationAPI) initNewAndShutdownOld(clientName string, newProvider F } // check for multiple bindings - if oldProvider == api.defaultProvider || contains(oldProvider, maps.Values(api.namedProviders)) { + if oldProvider == api.defaultProvider || slices.Contains(mapValues(api.namedProviders), oldProvider) { return nil } @@ -278,16 +278,6 @@ func initializer(provider FeatureProvider, apiCtx EvaluationContext) (Event, err return event, err } -func contains(provider FeatureProvider, in []FeatureProvider) bool { - for _, p := range in { - if provider == p { - return true - } - } - - return false -} - var statesMap = map[EventType]func(ProviderEventDetails) State{ ProviderReady: func(_ ProviderEventDetails) State { return ReadyState }, ProviderConfigChange: func(_ ProviderEventDetails) State { return ReadyState },