diff --git a/confidence/EventUploader.go b/confidence/EventUploader.go
new file mode 100644
index 0000000..94f9109
--- /dev/null
+++ b/confidence/EventUploader.go
@@ -0,0 +1,33 @@
+package confidence
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "net/http"
+)
+
+type EventUploader struct {
+ client http.Client
+ config APIConfig
+}
+
+func (e EventUploader) upload(ctx context.Context, request EventBatchRequest) {
+ jsonRequest, err := json.Marshal(request)
+ if err != nil {
+ return
+ }
+
+ payload := bytes.NewBuffer(jsonRequest)
+ req, err := http.NewRequestWithContext(ctx,
+ http.MethodPost, "https://events.eu.confidence.dev/v1/events:publish", payload)
+ if err != nil {
+ return
+ }
+
+ resp, err := e.client.Do(req)
+ if err != nil {
+ return
+ }
+ defer resp.Body.Close()
+}
\ No newline at end of file
diff --git a/confidence/confidence.go b/confidence/confidence.go
index 7c39d71..f1132f0 100644
--- a/confidence/confidence.go
+++ b/confidence/confidence.go
@@ -3,6 +3,7 @@ package confidence
import (
"context"
"fmt"
+ "time"
"net/http"
"reflect"
"strings"
@@ -18,24 +19,28 @@ type ContextProvider interface {
}
var (
- SDK_ID = "SDK_ID_GO_PROVIDER"
+ SDK_ID = "SDK_ID_GO_CONFIDENCE"
SDK_VERSION = "0.1.8" // x-release-please-version
)
type Confidence struct {
parent ContextProvider
+ uploader EventUploader
contextMap map[string]interface{}
Config APIConfig
ResolveClient ResolveClient
}
func (e Confidence) GetContext() map[string]interface{} {
- currentMap := e.contextMap
+ currentMap := map[string]interface{}{}
parentMap := make(map[string]interface{})
if e.parent != nil {
parentMap = e.parent.GetContext()
}
- for key, value := range parentMap {
+for key, value := range parentMap {
+ currentMap[key] = value
+ }
+ for key, value := range e.contextMap {
currentMap[key] = value
}
return currentMap
@@ -69,14 +74,48 @@ func NewConfidenceBuilder() ConfidenceBuilder {
}
}
-func (e Confidence) putContext(key string, value interface{}) {
+func (e Confidence) PutContext(key string, value interface{}) {
e.contextMap[key] = value
}
+func (e Confidence) Track(ctx context.Context, eventName string, message map[string]interface{}) {
+ newMap := e.GetContext()
+
+ for key, value := range message {
+ newMap[key] = value
+ }
+
+ go func() {
+ currentTime := time.Now()
+ iso8601Time := currentTime.Format(time.RFC3339)
+ event := Event {
+ EventDefinition: fmt.Sprintf("eventDefinitions/%s", eventName),
+ EventTime: iso8601Time,
+ Payload: newMap,
+ }
+ batch := EventBatchRequest{
+ CclientSecret: e.Config.APIKey,
+ Sdk: sdk{SDK_ID, SDK_VERSION},
+ SendTime: iso8601Time,
+ Events: []Event{event},
+ }
+ e.uploader.upload(ctx, batch)
+ }()
+}
+
func (e Confidence) WithContext(context map[string]interface{}) Confidence {
+ newMap := map[string]interface{}{}
+ for key, value := range e.GetContext() {
+ newMap[key] = value
+ }
+
+ for key, value := range context {
+ newMap[key] = value
+ }
+
return Confidence{
parent: &e,
- contextMap: context,
+ contextMap: newMap,
Config: e.Config,
ResolveClient: e.ResolveClient,
}
diff --git a/confidence/confidence_context_test.go b/confidence/confidence_context_test.go
new file mode 100644
index 0000000..9a17a57
--- /dev/null
+++ b/confidence/confidence_context_test.go
@@ -0,0 +1,43 @@
+package confidence
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestContextIsInConfidenceObject(t *testing.T) {
+ client := create_confidence(t, templateResponse())
+ client.PutContext("hello", "hey")
+ assert.Equal(t, client.GetContext(), map[string]interface{}{"hello": "hey"})
+}
+
+func TestWithContextIsInChildContext(t *testing.T) {
+ client := create_confidence(t, templateResponse())
+ client.PutContext("hello", "hey")
+ child := client.WithContext(map[string]interface{}{"west": "world"})
+ assert.Equal(t, child.GetContext(), map[string]interface{}{"hello": "hey", "west": "world"})
+ client.PutContext("hello2", "hey2")
+ assert.Equal(t, child.GetContext(), map[string]interface{}{"hello": "hey", "west": "world", "hello2": "hey2"})
+}
+
+
+func TestChildContextOverrideParentContext(t *testing.T) {
+ client := create_confidence(t, templateResponse())
+ client.PutContext("hello", "hey")
+ child := client.WithContext(map[string]interface{}{"hello": "boom"})
+ assert.Equal(t, child.GetContext(), map[string]interface{}{"hello": "boom"})
+ assert.Equal(t, client.GetContext(), map[string]interface{}{"hello": "hey"})
+}
+
+func create_confidence(t *testing.T, response ResolveResponse) *Confidence {
+ config := APIConfig{
+ APIKey: "apiKey",
+ Region: APIRegionGlobal,
+ }
+ return &Confidence{
+ Config: config,
+ ResolveClient: MockResolveClient{MockedResponse: response, MockedError: nil, TestingT: t},
+ contextMap: make(map[string]interface{}),
+ }
+}
+
diff --git a/confidence/confidence_internal_test.go b/confidence/confidence_internal_test.go
index 9b71b0f..4bdc9b2 100644
--- a/confidence/confidence_internal_test.go
+++ b/confidence/confidence_internal_test.go
@@ -24,7 +24,7 @@ func (r MockResolveClient) SendResolveRequest(_ context.Context,
func TestResolveBoolValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetBoolFlag(context.Background(), "test-flag.boolean-key", false)
assert.Equal(t, true, evalDetails.Value)
@@ -34,7 +34,7 @@ func TestResolveBoolValue(t *testing.T) {
func TestResolveIntValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetIntFlag(context.Background(), "test-flag.integer-key", 99)
@@ -43,7 +43,7 @@ func TestResolveIntValue(t *testing.T) {
func TestResolveDoubleValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetDoubleFlag(context.Background(), "test-flag.double-key", 99.99)
@@ -52,7 +52,7 @@ func TestResolveDoubleValue(t *testing.T) {
func TestResolveStringValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetStringFlag(context.Background(), "test-flag.string-key", "default")
@@ -61,7 +61,7 @@ func TestResolveStringValue(t *testing.T) {
func TestResolveObjectValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetObjectFlag(context.Background(), "test-flag.struct-key", "default")
_, ok := evalDetails.Value.(map[string]interface{})
@@ -70,7 +70,7 @@ func TestResolveObjectValue(t *testing.T) {
func TestResolveNestedValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetBoolFlag(context.Background(), "test-flag.struct-key.boolean-key", true)
assert.Equal(t, false, evalDetails.Value)
@@ -78,7 +78,7 @@ func TestResolveNestedValue(t *testing.T) {
func TestResolveDoubleNestedValue(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetBoolFlag(context.Background(), "test-flag.struct-key.nested-struct-key.nested-boolean-key", true)
assert.Equal(t, false, evalDetails.Value)
@@ -86,7 +86,7 @@ func TestResolveDoubleNestedValue(t *testing.T) {
func TestResolveWholeFlagAsObject(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetObjectFlag(context.Background(), "test-flag", "default")
_, ok := evalDetails.Value.(map[string]interface{})
@@ -95,7 +95,7 @@ func TestResolveWholeFlagAsObject(t *testing.T) {
func TestResolveWholeFlagAsObjectWithInts(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetObjectFlag(context.Background(), "test-flag", "default")
@@ -113,7 +113,7 @@ func TestResolveWholeFlagAsObjectWithInts(t *testing.T) {
func TestResolveWithWrongType(t *testing.T) {
client := client(t, templateResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetBoolFlag(context.Background(), "test-flag.integer-key", false)
@@ -124,7 +124,7 @@ func TestResolveWithWrongType(t *testing.T) {
func TestResolveWithUnexpectedFlag(t *testing.T) {
client := client(t, templateResponseWithFlagName("wrong-flag"), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetBoolFlag(context.Background(), "test-flag.boolean-key", true)
@@ -136,7 +136,7 @@ func TestResolveWithUnexpectedFlag(t *testing.T) {
func TestResolveWithNonExistingFlag(t *testing.T) {
client := client(t, emptyResponse(), nil)
- client.putContext("targeting_key", "user1")
+ client.PutContext("targeting_key", "user1")
evalDetails := client.GetBoolFlag(context.Background(), "test-flag.boolean-key", true)
diff --git a/confidence/models.go b/confidence/models.go
index 6cb4d25..0da7486 100644
--- a/confidence/models.go
+++ b/confidence/models.go
@@ -109,6 +109,19 @@ type ResolveClient interface {
var errFlagNotFound = errors.New("flag not found")
+type EventBatchRequest struct {
+ CclientSecret string `json:"clientSecret"`
+ Sdk sdk `json:"sdk"`
+ SendTime string `json:"sendTime"`
+ Events []Event `json:"events"`
+}
+
+type Event struct {
+ EventDefinition string `json:"eventDefinition"`
+ EventTime string `json:"eventTime"`
+ Payload map[string]interface{} `json:"payload"`
+}
+
type ResolveRequest struct {
ClientSecret string `json:"client_secret"`
Apply bool `json:"apply"`
diff --git a/demo/GoDemoApp.go b/demo/GoDemoApp.go
index 253842e..3556b2f 100644
--- a/demo/GoDemoApp.go
+++ b/demo/GoDemoApp.go
@@ -16,6 +16,11 @@ func main() {
confidence := c.NewConfidenceBuilder().SetAPIConfig(c.APIConfig{APIKey: clientSecret}).Build()
+ confidence.PutContext("hello", "world")
+
+ for range make([]struct{}, 10) {
+ confidence.Track(context.Background(), "navigate", map[string]interface{}{"test": "value"})
+ }
provider := p.NewFlagProvider(confidence)
openfeature.SetProvider(provider)
diff --git a/demo/go.sum b/demo/go.sum
index 39c4ad6..6c6175d 100644
--- a/demo/go.sum
+++ b/demo/go.sum
@@ -1,20 +1,31 @@
+github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0=
+github.com/cucumber/godog v0.14.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces=
+github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/open-feature/go-sdk v1.10.0 h1:druQtYOrN+gyz3rMsXp0F2jW1oBXJb0V26PVQnUGLbM=
github.com/open-feature/go-sdk v1.10.0/go.mod h1:+rkJhLBtYsJ5PZNddAgFILhRAAxwrJ32aU7UEUm4zQI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
+golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/provider/google-java-format.xml b/provider/google-java-format.xml
deleted file mode 100644
index 2aa056d..0000000
--- a/provider/google-java-format.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/provider/misc.xml b/provider/misc.xml
deleted file mode 100644
index 3d3ab27..0000000
--- a/provider/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/provider/provider.go b/provider/provider.go
index c71579f..06ff769 100644
--- a/provider/provider.go
+++ b/provider/provider.go
@@ -43,6 +43,8 @@ func (e FlagProvider) StringEvaluation(ctx context.Context, flag string, default
}
}
+
+
func (e FlagProvider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64,
evalCtx openfeature.FlattenedContext) openfeature.FloatResolutionDetail {
confidence := e.confidence.WithContext(processTargetingKey(evalCtx))
diff --git a/provider/provider.iml b/provider/provider.iml
deleted file mode 100644
index 25ed3f6..0000000
--- a/provider/provider.iml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file