Skip to content

Commit e52d0e9

Browse files
fix: implement the StateHandler interface and updates to user mapping (#723)
Signed-off-by: Josh Kaplinsky <[email protected]>
1 parent d7963ff commit e52d0e9

File tree

4 files changed

+79
-45
lines changed

4 files changed

+79
-45
lines changed

providers/statsig/go.mod

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,20 @@ toolchain go1.24.4
66

77
require (
88
github.com/open-feature/go-sdk v1.15.1
9-
github.com/statsig-io/go-sdk v1.32.1
9+
github.com/statsig-io/go-sdk v1.36.4
1010
github.com/stretchr/testify v1.10.0
1111
)
1212

1313
require (
1414
github.com/davecgh/go-spew v1.1.1 // indirect
1515
github.com/go-logr/logr v1.4.3 // indirect
16-
github.com/google/uuid v1.3.0 // indirect
16+
github.com/google/uuid v1.6.0 // indirect
17+
github.com/hashicorp/golang-lru v1.0.2 // indirect
1718
github.com/kr/pretty v0.3.1 // indirect
1819
github.com/pmezard/go-difflib v1.0.0 // indirect
19-
github.com/statsig-io/ip3country-go v0.2.0 // indirect
20-
github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f // indirect
20+
github.com/statsig-io/ip3country-go v0.2.1 // indirect
21+
github.com/ua-parser/uap-go v0.0.0-20250326155420-f7f5a2f9f5bc // indirect
2122
go.uber.org/mock v0.5.2 // indirect
22-
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
2323
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
24-
gopkg.in/yaml.v2 v2.4.0 // indirect
2524
gopkg.in/yaml.v3 v3.0.1 // indirect
2625
)

providers/statsig/go.sum

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,44 @@
11
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
22
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
33
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4-
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
5-
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
64
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
75
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
8-
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
9-
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
10-
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
116
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
8+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9+
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
10+
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
1211
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
1312
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
1413
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
1514
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
1615
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1716
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1817
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
19-
github.com/open-feature/go-sdk v1.11.0 h1:4cp9rXl16ZvlMCef7O+I3vQSXae8DzAF0SfV9mvYInw=
20-
github.com/open-feature/go-sdk v1.11.0/go.mod h1:+rkJhLBtYsJ5PZNddAgFILhRAAxwrJ32aU7UEUm4zQI=
2118
github.com/open-feature/go-sdk v1.15.1 h1:TC3FtHtOKlGlIbSf3SEpxXVhgTd/bCbuc39XHIyltkw=
2219
github.com/open-feature/go-sdk v1.15.1/go.mod h1:2WAFYzt8rLYavcubpCoiym3iSCXiHdPB6DxtMkv2wyo=
2320
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
2421
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2522
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2623
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
2724
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
28-
github.com/statsig-io/go-sdk v1.32.1 h1:z0VjBSRuNzguNb+PK/4+8YsfcLClOxs3aZr2hUj4xm4=
29-
github.com/statsig-io/go-sdk v1.32.1/go.mod h1:Pej0D6R75gTHj7FdS6pbXQ7ayF0HL1cwOgiz5zDNdyc=
30-
github.com/statsig-io/ip3country-go v0.2.0 h1:4z4ovVCx7GnQAKJC753bjcOgxLQJFsrDdcCKda4I2U8=
31-
github.com/statsig-io/ip3country-go v0.2.0/go.mod h1:PKuA/VSpe4puBXw3BNGAHyP8IOZOiXAh/xIz+iYYoMQ=
32-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
33-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
25+
github.com/statsig-io/go-sdk v1.36.4 h1:zcwa20SMw2xL6iMTtLwgK+yvEz+EwnmhA9F1Fgb34BU=
26+
github.com/statsig-io/go-sdk v1.36.4/go.mod h1:vrJZ6T+axiTSIWQ180F09aoWAdfXLyOBnjXGp3Bk7ZI=
27+
github.com/statsig-io/ip3country-go v0.2.1 h1:bLkWWM0PGz9OQrcGF2qqtfxQeVwxUYP46peNjJKnM+k=
28+
github.com/statsig-io/ip3country-go v0.2.1/go.mod h1:PKuA/VSpe4puBXw3BNGAHyP8IOZOiXAh/xIz+iYYoMQ=
3429
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
3530
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
36-
github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f h1:A+MmlgpvrHLeUP8dkBVn4Pnf5Bp5Yk2OALm7SEJLLE8=
3731
github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f/go.mod h1:OBcG9bn7sHtXgarhUEb3OfCnNsgtGnkVf41ilSZ3K3E=
32+
github.com/ua-parser/uap-go v0.0.0-20250326155420-f7f5a2f9f5bc h1:reH9QQKGFOq39MYOvU9+SYrB8uzXtWNo51fWK3g0gGc=
33+
github.com/ua-parser/uap-go v0.0.0-20250326155420-f7f5a2f9f5bc/go.mod h1:gwANdYmo9R8LLwGnyDFWK2PMsaXXX2HhAvCnb/UhZsM=
3834
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
3935
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
40-
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo=
41-
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
42-
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
43-
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
36+
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
37+
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
4438
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4539
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
4640
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
4741
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
48-
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
4942
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
5043
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
5144
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

providers/statsig/pkg/provider.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ func NewProvider(providerConfig ProviderConfig) (*Provider, error) {
2727
return provider, nil
2828
}
2929

30-
func (p *Provider) Init(evaluationContext of.EvaluationContext) {
31-
statsig.InitializeWithOptions(p.providerConfig.SdkKey, &p.providerConfig.Options)
32-
p.status = of.ReadyState
33-
}
30+
func (p *Provider) Init(_ of.EvaluationContext) error {
31+
if details := statsig.InitializeWithOptions(p.providerConfig.SdkKey, &p.providerConfig.Options); details.Error != nil {
32+
p.status = of.ErrorState
33+
return details.Error
34+
}
3435

35-
func (p *Provider) Status() of.State {
36-
return p.status
36+
p.status = of.ReadyState
37+
return nil
3738
}
3839

3940
func (p *Provider) Shutdown() {
@@ -399,7 +400,10 @@ func ToStatsigUser(evalCtx of.FlattenedContext) (*statsig.User, error) {
399400
statsigUser.Custom[key] = origVal
400401
}
401402
}
402-
if statsigUser.UserID == "" {
403+
404+
// Statsig requires UserID or CustomIDs to be set
405+
// https://github.com/statsig-io/go-sdk/blob/fb8d78a5d5a2c7100936c711a5fffc8ae4146e37/client.go#L348-L355
406+
if statsigUser.UserID == "" && len(statsigUser.CustomIDs) == 0 {
403407
return nil, of.NewTargetingKeyMissingResolutionError("UserID/targetingKey is missing")
404408
}
405409

providers/statsig/pkg/provider_test.go

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import (
1010
"github.com/stretchr/testify/assert"
1111
"github.com/stretchr/testify/require"
1212

13-
statsigProvider "github.com/open-feature/go-sdk-contrib/providers/statsig/pkg"
1413
of "github.com/open-feature/go-sdk/openfeature"
1514
statsig "github.com/statsig-io/go-sdk"
15+
16+
statsigProvider "github.com/open-feature/go-sdk-contrib/providers/statsig/pkg"
1617
)
1718

1819
var provider *statsigProvider.Provider
@@ -159,15 +160,50 @@ func TestConvertsValidEvaluationContextToStatsigUser(t *testing.T) {
159160
}
160161
}
161162

162-
// Handles missing TargetingKey by checking for "key" in EvaluationContext
163-
func TestHandlesMissingTargetingKey(t *testing.T) {
164-
evalCtx := of.FlattenedContext{
165-
"dummy-key": "test-key",
163+
// Handles missing TargetingKey, UserID, and/or CustomID using a table-driven test
164+
func TestHandlesMissingTargetingKeyOrUserIDOrCustomID(t *testing.T) {
165+
tests := []struct {
166+
name string
167+
evalCtx of.FlattenedContext
168+
wantErr require.ErrorAssertionFunc
169+
}{
170+
{
171+
name: "only unrelated key",
172+
evalCtx: of.FlattenedContext{"dummy-key": "test-key"},
173+
wantErr: require.Error,
174+
},
175+
{
176+
name: "has UserID",
177+
evalCtx: of.FlattenedContext{"UserID": "test_user"},
178+
wantErr: require.NoError,
179+
},
180+
{
181+
name: "has targetingKey",
182+
evalCtx: of.FlattenedContext{of.TargetingKey: "targeting-key"},
183+
wantErr: require.NoError,
184+
},
185+
{
186+
name: "has CustomIDs",
187+
evalCtx: of.FlattenedContext{"CustomIDs": map[string]string{"custom": "id"}},
188+
wantErr: require.NoError,
189+
},
190+
{
191+
name: "has UserID and CustomIDs",
192+
evalCtx: of.FlattenedContext{"UserID": "test_user", "CustomIDs": map[string]string{"custom": "id"}},
193+
wantErr: require.NoError,
194+
},
195+
{
196+
name: "has targetingKey and CustomIDs",
197+
evalCtx: of.FlattenedContext{of.TargetingKey: "targeting-key", "CustomIDs": map[string]string{"custom": "id"}},
198+
wantErr: require.NoError,
199+
},
166200
}
167201

168-
_, err := statsigProvider.ToStatsigUser(evalCtx)
169-
if err == nil {
170-
t.Fatalf("expected error")
202+
for _, tt := range tests {
203+
t.Run(tt.name, func(t *testing.T) {
204+
_, err := statsigProvider.ToStatsigUser(tt.evalCtx)
205+
tt.wantErr(t, err)
206+
})
171207
}
172208
}
173209

@@ -255,18 +291,20 @@ func TestMain(m *testing.M) {
255291
}
256292

257293
providerOptions := statsigProvider.ProviderConfig{
258-
Options: statsig.Options{
259-
BootstrapValues: string(bytes[:]),
260-
},
261-
SdkKey: "secret-key",
294+
Options: statsig.Options{BootstrapValues: string(bytes[:])},
295+
SdkKey: "secret-key",
262296
}
263297

264298
provider, err = statsigProvider.NewProvider(providerOptions)
265299
if err != nil {
266300
fmt.Printf("Error during new provider: %v\n", err)
267301
os.Exit(1)
268302
}
269-
provider.Init(of.EvaluationContext{})
303+
304+
if err := provider.Init(of.EvaluationContext{}); err != nil {
305+
fmt.Printf("Error during provider initialization: %v\n", err)
306+
os.Exit(1)
307+
}
270308

271309
// Run the tests
272310
exitCode := m.Run()

0 commit comments

Comments
 (0)