Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
5.12.0 (Dec 30, 2025)
- Split Proxy:
- Added support for new impression properties to be appended to the impressions sent to the Split backend. Read more in our docs.
- Split-Sync:
- Added support for new impression properties to be appended to the impressions sent to the Split backend. Read more in our docs.

5.11.1 (Dec 16, 2025)
- Fixed vulnerabilities:
- Updated golang image to 1.24.11
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/gin-gonic/gin v1.10.1
github.com/google/uuid v1.3.0
github.com/splitio/gincache v1.0.1
github.com/splitio/go-split-commons/v8 v8.0.0
github.com/splitio/go-split-commons/v9 v9.1.0
github.com/splitio/go-toolkit/v5 v5.4.1
github.com/stretchr/testify v1.11.1
go.etcd.io/bbolt v1.3.6
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU=
github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY=
github.com/splitio/go-split-commons/v8 v8.0.0 h1:wLk5eT6WU2LfxtaWG3ZHlTbNMGWP2eYsZTb1o+tFpkI=
github.com/splitio/go-split-commons/v8 v8.0.0/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI=
github.com/splitio/go-split-commons/v9 v9.1.0 h1:sfmPMuEDTtbIOJ+MeWNbfYl2/xKB/25d4/J95OUD+X0=
github.com/splitio/go-split-commons/v9 v9.1.0/go.mod h1:gJuaKo04Swlh4w9C1b2jBAqAdFxEd/Vpd8jnFINOeDY=
github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE=
github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down
4 changes: 2 additions & 2 deletions splitio/admin/common/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package common

import (
"github.com/splitio/go-split-commons/v8/engine/grammar/constants"
"github.com/splitio/go-split-commons/v8/storage"
"github.com/splitio/go-split-commons/v9/engine/grammar/constants"
"github.com/splitio/go-split-commons/v9/storage"
)

var ProducerFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween,
Expand Down
4 changes: 2 additions & 2 deletions splitio/admin/controllers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"strings"
"time"

"github.com/splitio/go-split-commons/v8/storage"
"github.com/splitio/go-split-commons/v8/telemetry"
"github.com/splitio/go-split-commons/v9/storage"
"github.com/splitio/go-split-commons/v9/telemetry"

"github.com/splitio/split-synchronizer/v5/splitio/admin/views/dashboard"
"github.com/splitio/split-synchronizer/v5/splitio/producer/evcalc"
Expand Down
4 changes: 2 additions & 2 deletions splitio/admin/controllers/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (

"github.com/splitio/split-synchronizer/v5/splitio/admin/views/dashboard"

"github.com/splitio/go-split-commons/v8/dtos"
"github.com/splitio/go-split-commons/v8/storage/mocks"
"github.com/splitio/go-split-commons/v9/dtos"
"github.com/splitio/go-split-commons/v9/storage/mocks"
"github.com/splitio/go-toolkit/v5/datastructures/set"

"github.com/stretchr/testify/assert"
Expand Down
4 changes: 2 additions & 2 deletions splitio/admin/controllers/observability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/splitio/split-synchronizer/v5/splitio/provisional/observability"
"github.com/splitio/split-synchronizer/v5/splitio/proxy/storage"

"github.com/splitio/go-split-commons/v8/dtos"
"github.com/splitio/go-split-commons/v8/storage/mocks"
"github.com/splitio/go-split-commons/v9/dtos"
"github.com/splitio/go-split-commons/v9/storage/mocks"
"github.com/splitio/go-toolkit/v5/datastructures/set"
"github.com/splitio/go-toolkit/v5/logging"

Expand Down
2 changes: 1 addition & 1 deletion splitio/common/conf/advanced.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package conf
import (
"os"

"github.com/splitio/go-split-commons/v8/conf"
"github.com/splitio/go-split-commons/v9/conf"
)

// InitAdvancedOptions initializes an advanced config with default values + overriden urls.
Expand Down
2 changes: 1 addition & 1 deletion splitio/common/conf/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package conf
import (
"strings"

"github.com/splitio/go-split-commons/v8/flagsets"
"github.com/splitio/go-split-commons/v9/flagsets"
)

type FlagSetValidationError struct {
Expand Down
2 changes: 1 addition & 1 deletion splitio/common/conf/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package conf
import (
"testing"

"github.com/splitio/go-split-commons/v8/dtos"
"github.com/splitio/go-split-commons/v9/dtos"

"github.com/stretchr/testify/assert"
)
Expand Down
1 change: 1 addition & 0 deletions splitio/common/impressionlistener/dtos.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type ImpressionForListener struct {
Label string `json:"label"`
BucketingKey string `json:"bucketingKey,omitempty"`
Pt int64 `json:"pt,omitempty"`
Properties string `json:"properties,omitempty"`
}

// ImpressionsForListener struct for payload
Expand Down
2 changes: 1 addition & 1 deletion splitio/common/impressionlistener/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"fmt"
"net/http"

"github.com/splitio/go-split-commons/v8/dtos"
"github.com/splitio/go-split-commons/v9/dtos"
"github.com/splitio/go-toolkit/v5/struct/traits/lifecycle"
)

Expand Down
214 changes: 191 additions & 23 deletions splitio/common/impressionlistener/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,229 @@ import (
"net/http/httptest"
"testing"

"github.com/splitio/go-split-commons/v8/dtos"
"github.com/splitio/go-split-commons/v9/dtos"
"github.com/stretchr/testify/assert"
)

func TestImpressionListener(t *testing.T) {

reqsDone := make(chan struct{}, 1)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() { reqsDone <- struct{}{} }()
if r.URL.Path != "/someUrl" && r.Method != "POST" {
t.Error("Invalid request. Should be POST to /someUrl")
}
// Verify request method and path
assert.False(t, r.URL.Path != "/someUrl" && r.Method != "POST", "Invalid request. Should be POST to /someUrl")

// Read and parse request body
body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
assert.False(t, err != nil, "Error reading body")
if err != nil {
t.Error("Error reading body")
return
}

var all impressionListenerPostBody
err = json.Unmarshal(body, &all)
assert.False(t, err != nil, "Error parsing json: %v", err)
if err != nil {
t.Errorf("Error parsing json: %s", err)
return
}

if all.SdkVersion != "go-1.1.1" || all.MachineIP != "1.2.3.4" || all.MachineName != "ip-1-2-3-4" {
t.Error("invalid metadata")
}
// Verify metadata
assert.False(t, all.SdkVersion != "go-1.1.1" || all.MachineIP != "1.2.3.4" || all.MachineName != "ip-1-2-3-4", "invalid metadata")

// Verify impressions
imps := all.Impressions
if len(imps) != 2 {
t.Error("invalid number of impression groups received")
return
}
assert.Equal(t, 2, len(imps), "Should have 2 impression groups")

// Verify first impression group (t1)
assert.Equal(t, "t1", imps[0].TestName, "First group should be t1")
assert.Equal(t, 2, len(imps[0].KeyImpressions), "t1 should have 2 impressions")

// Verify first impression of t1
assert.Equal(t, "k1", imps[0].KeyImpressions[0].KeyName, "t1 first impression should have correct key name")
assert.Equal(t, "on", imps[0].KeyImpressions[0].Treatment, "t1 first impression should have correct treatment")
assert.Equal(t, int64(1), imps[0].KeyImpressions[0].Time, "t1 first impression should have correct time")
assert.Equal(t, int64(2), imps[0].KeyImpressions[0].ChangeNumber, "t1 first impression should have correct change number")
assert.Equal(t, "l1", imps[0].KeyImpressions[0].Label, "t1 first impression should have correct label")
assert.Equal(t, "b1", imps[0].KeyImpressions[0].BucketingKey, "t1 first impression should have correct bucketing key")
assert.Equal(t, int64(1), imps[0].KeyImpressions[0].Pt, "t1 first impression should have correct pt")

// Verify second impression of t1
assert.Equal(t, "k2", imps[0].KeyImpressions[1].KeyName, "t1 second impression should have correct key name")
assert.Equal(t, "on", imps[0].KeyImpressions[1].Treatment, "t1 second impression should have correct treatment")
assert.Equal(t, int64(1), imps[0].KeyImpressions[1].Time, "t1 second impression should have correct time")
assert.Equal(t, int64(2), imps[0].KeyImpressions[1].ChangeNumber, "t1 second impression should have correct change number")
assert.Equal(t, "l1", imps[0].KeyImpressions[1].Label, "t1 second impression should have correct label")
assert.Equal(t, "b1", imps[0].KeyImpressions[1].BucketingKey, "t1 second impression should have correct bucketing key")
assert.Equal(t, int64(1), imps[0].KeyImpressions[1].Pt, "t1 second impression should have correct pt")

// Verify second impression group (t2)
assert.Equal(t, "t2", imps[1].TestName, "Second group should be t2")
assert.Equal(t, 2, len(imps[1].KeyImpressions), "t2 should have 2 impressions")

// Verify first impression of t2
assert.Equal(t, "k1", imps[1].KeyImpressions[0].KeyName, "t2 first impression should have correct key name")
assert.Equal(t, "off", imps[1].KeyImpressions[0].Treatment, "t2 first impression should have correct treatment")
assert.Equal(t, int64(2), imps[1].KeyImpressions[0].Time, "t2 first impression should have correct time")
assert.Equal(t, int64(3), imps[1].KeyImpressions[0].ChangeNumber, "t2 first impression should have correct change number")
assert.Equal(t, "l2", imps[1].KeyImpressions[0].Label, "t2 first impression should have correct label")
assert.Equal(t, "b2", imps[1].KeyImpressions[0].BucketingKey, "t2 first impression should have correct bucketing key")
assert.Equal(t, int64(2), imps[1].KeyImpressions[0].Pt, "t2 first impression should have correct pt")

// Verify second impression of t2
assert.Equal(t, "k2", imps[1].KeyImpressions[1].KeyName, "t2 second impression should have correct key name")
assert.Equal(t, "off", imps[1].KeyImpressions[1].Treatment, "t2 second impression should have correct treatment")
assert.Equal(t, int64(2), imps[1].KeyImpressions[1].Time, "t2 second impression should have correct time")
assert.Equal(t, int64(3), imps[1].KeyImpressions[1].ChangeNumber, "t2 second impression should have correct change number")
assert.Equal(t, "l2", imps[1].KeyImpressions[1].Label, "t2 second impression should have correct label")
assert.Equal(t, "b2", imps[1].KeyImpressions[1].BucketingKey, "t2 second impression should have correct bucketing key")
assert.Equal(t, int64(3), imps[1].KeyImpressions[1].Pt, "t2 second impression should have correct pt")
}))
defer ts.Close()

listener, err := NewImpressionBulkListener(ts.URL, 10, nil)
assert.False(t, err != nil, "error cannot be nil: %v", err)

err = listener.Start()
assert.False(t, err != nil, "start() should not fail. Got: %v", err)
defer listener.Stop(true)

listener.Submit([]ImpressionsForListener{
ImpressionsForListener{
TestName: "t1",
KeyImpressions: []ImpressionForListener{
ImpressionForListener{
KeyName: "k1",
Treatment: "on",
Time: 1,
ChangeNumber: 2,
Label: "l1",
BucketingKey: "b1",
Pt: 1,
},
ImpressionForListener{
KeyName: "k2",
Treatment: "on",
Time: 1,
ChangeNumber: 2,
Label: "l1",
BucketingKey: "b1",
Pt: 1,
},
},
},
ImpressionsForListener{
TestName: "t2",
KeyImpressions: []ImpressionForListener{
ImpressionForListener{
KeyName: "k1",
Treatment: "off",
Time: 2,
ChangeNumber: 3,
Label: "l2",
BucketingKey: "b2",
Pt: 2,
},
ImpressionForListener{
KeyName: "k2",
Treatment: "off",
Time: 2,
ChangeNumber: 3,
Label: "l2",
BucketingKey: "b2",
Pt: 3,
},
},
},
}, &dtos.Metadata{SDKVersion: "go-1.1.1", MachineIP: "1.2.3.4", MachineName: "ip-1-2-3-4"})

<-reqsDone
}

func TestImpressionListenerWithProperties(t *testing.T) {

if imps[0].TestName != "t1" || len(imps[0].KeyImpressions) != 2 {
t.Errorf("invalid ipmressions for t1")
reqsDone := make(chan struct{}, 1)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() { reqsDone <- struct{}{} }()
// Verify request method and path
assert.False(t, r.URL.Path != "/someUrl" && r.Method != "POST", "Invalid request. Should be POST to /someUrl")

// Read and parse request body
body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
assert.False(t, err != nil, "Error reading body")
if err != nil {
return
}

if imps[1].TestName != "t2" || len(imps[1].KeyImpressions) != 2 {
t.Errorf("invalid ipmressions for t2")
var all impressionListenerPostBody
err = json.Unmarshal(body, &all)
assert.False(t, err != nil, "Error parsing json: %v", err)
if err != nil {
return
}

// Verify metadata
assert.False(t, all.SdkVersion != "go-1.1.1" || all.MachineIP != "1.2.3.4" || all.MachineName != "ip-1-2-3-4", "invalid metadata")

// Verify impressions
imps := all.Impressions
assert.Equal(t, 2, len(imps), "Should have 2 impression groups")

// Verify first impression group (t1)
assert.Equal(t, "t1", imps[0].TestName, "First group should be t1")
assert.Equal(t, 2, len(imps[0].KeyImpressions), "t1 should have 2 impressions")

// Verify first impression of t1
assert.Equal(t, "k1", imps[0].KeyImpressions[0].KeyName, "t1 first impression should have correct key name")
assert.Equal(t, "on", imps[0].KeyImpressions[0].Treatment, "t1 first impression should have correct treatment")
assert.Equal(t, int64(1), imps[0].KeyImpressions[0].Time, "t1 first impression should have correct time")
assert.Equal(t, int64(2), imps[0].KeyImpressions[0].ChangeNumber, "t1 first impression should have correct change number")
assert.Equal(t, "l1", imps[0].KeyImpressions[0].Label, "t1 first impression should have correct label")
assert.Equal(t, "b1", imps[0].KeyImpressions[0].BucketingKey, "t1 first impression should have correct bucketing key")
assert.Equal(t, int64(1), imps[0].KeyImpressions[0].Pt, "t1 first impression should have correct pt")
assert.Equal(t, "{'prop':'val'}", imps[0].KeyImpressions[0].Properties, "First impression of t1 should have properties")

// Verify second impression of t1
assert.Equal(t, "k2", imps[0].KeyImpressions[1].KeyName, "t1 second impression should have correct key name")
assert.Equal(t, "on", imps[0].KeyImpressions[1].Treatment, "t1 second impression should have correct treatment")
assert.Equal(t, int64(1), imps[0].KeyImpressions[1].Time, "t1 second impression should have correct time")
assert.Equal(t, int64(2), imps[0].KeyImpressions[1].ChangeNumber, "t1 second impression should have correct change number")
assert.Equal(t, "l1", imps[0].KeyImpressions[1].Label, "t1 second impression should have correct label")
assert.Equal(t, "b1", imps[0].KeyImpressions[1].BucketingKey, "t1 second impression should have correct bucketing key")
assert.Equal(t, int64(1), imps[0].KeyImpressions[1].Pt, "t1 second impression should have correct pt")
// Second impression should not have properties
assert.Empty(t, imps[0].KeyImpressions[1].Properties, "Second impression of t1 should not have properties")

// Verify second impression group (t2)
assert.Equal(t, "t2", imps[1].TestName, "Second group should be t2")
assert.Equal(t, 2, len(imps[1].KeyImpressions), "t2 should have 2 impressions")

// Verify first impression of t2
assert.Equal(t, "k1", imps[1].KeyImpressions[0].KeyName, "t2 first impression should have correct key name")
assert.Equal(t, "off", imps[1].KeyImpressions[0].Treatment, "t2 first impression should have correct treatment")
assert.Equal(t, int64(2), imps[1].KeyImpressions[0].Time, "t2 first impression should have correct time")
assert.Equal(t, int64(3), imps[1].KeyImpressions[0].ChangeNumber, "t2 first impression should have correct change number")
assert.Equal(t, "l2", imps[1].KeyImpressions[0].Label, "t2 first impression should have correct label")
assert.Equal(t, "b2", imps[1].KeyImpressions[0].BucketingKey, "t2 first impression should have correct bucketing key")
assert.Equal(t, int64(2), imps[1].KeyImpressions[0].Pt, "t2 first impression should have correct pt")

// Verify second impression of t2
assert.Equal(t, "k2", imps[1].KeyImpressions[1].KeyName, "t2 second impression should have correct key name")
assert.Equal(t, "off", imps[1].KeyImpressions[1].Treatment, "t2 second impression should have correct treatment")
assert.Equal(t, int64(2), imps[1].KeyImpressions[1].Time, "t2 second impression should have correct time")
assert.Equal(t, int64(3), imps[1].KeyImpressions[1].ChangeNumber, "t2 second impression should have correct change number")
assert.Equal(t, "l2", imps[1].KeyImpressions[1].Label, "t2 second impression should have correct label")
assert.Equal(t, "b2", imps[1].KeyImpressions[1].BucketingKey, "t2 second impression should have correct bucketing key")
assert.Equal(t, int64(3), imps[1].KeyImpressions[1].Pt, "t2 second impression should have correct pt")
}))
defer ts.Close()

listener, err := NewImpressionBulkListener(ts.URL, 10, nil)
if err != nil {
t.Error("error cannot be nil: ", err)
}
assert.False(t, err != nil, "error cannot be nil: %v", err)

if err = listener.Start(); err != nil {
t.Error("start() should not fail. Got: ", err)
}
err = listener.Start()
assert.False(t, err != nil, "start() should not fail. Got: %v", err)
defer listener.Stop(true)

listener.Submit([]ImpressionsForListener{
Expand All @@ -77,6 +244,7 @@ func TestImpressionListener(t *testing.T) {
Label: "l1",
BucketingKey: "b1",
Pt: 1,
Properties: "{'prop':'val'}",
},
ImpressionForListener{
KeyName: "k2",
Expand Down
Loading