diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..075ff531 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,10 @@ +--- +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + reviewers: + - "splitio/sdk" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 582d7c05..03758e9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,12 +19,12 @@ jobs: - 6379:6379 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go version - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '1.18.0' diff --git a/.github/workflows/update-license-year.yml b/.github/workflows/update-license-year.yml index 79640443..f46ed9d7 100644 --- a/.github/workflows/update-license-year.yml +++ b/.github/workflows/update-license-year.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -24,7 +24,7 @@ jobs: run: "echo PREVIOUS=$(($CURRENT-1)) >> $GITHUB_ENV" - name: Update LICENSE - uses: jacobtomlinson/gha-find-replace@v2 + uses: jacobtomlinson/gha-find-replace@v3 with: find: ${{ env.PREVIOUS }} replace: ${{ env.CURRENT }} @@ -38,7 +38,7 @@ jobs: git commit -m "Updated License Year" -a - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} title: Update License Year diff --git a/CHANGES.txt b/CHANGES.txt index 65b01aff..bbc4e8a6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +6.6.0 (May 14, 2024) +- Updated go-split-commons to v6 + - Added support for targeting rules based on semantic versions (https://semver.org/). + - Added the logic to handle correctly when the SDK receives an unsupported Matcher type. + 6.5.2 (Dec 21, 2023) - Updated telemetry name methods for flagSets. diff --git a/go.mod b/go.mod index bd05b1fa..e5646e8f 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/splitio/go-client/v6 go 1.18 require ( - github.com/splitio/go-split-commons/v5 v5.1.1 - github.com/splitio/go-toolkit/v5 v5.3.2 + github.com/splitio/go-split-commons/v6 v6.0.0 + github.com/splitio/go-toolkit/v5 v5.4.0 ) require ( diff --git a/go.sum b/go.sum index 91050b9a..72667601 100644 --- a/go.sum +++ b/go.sum @@ -10,10 +10,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc= github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/splitio/go-split-commons/v5 v5.1.1 h1:lLOqNQMdZA5Z7FBBh4YODWdE2QFgxSPMptX9ty14x4c= -github.com/splitio/go-split-commons/v5 v5.1.1/go.mod h1:9vAZrlhKvhensyRC11hyVFdgLIBrkX9D5vdYc9qB13w= -github.com/splitio/go-toolkit/v5 v5.3.2 h1:Yy9YBcHRmK5WVZjeA/klLGEdF38xpsL1ejnC3ro8a2M= -github.com/splitio/go-toolkit/v5 v5.3.2/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko= +github.com/splitio/go-split-commons/v6 v6.0.0 h1:qenr5qbXafjvM832C64CVpjtlShuQiWCwtR5I2h4ogM= +github.com/splitio/go-split-commons/v6 v6.0.0/go.mod h1:TsvIh3XP7yjc7ly4vpj06AkoBND36SodPs5qfhb8rHc= +github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM= +github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= diff --git a/splitio/client/client.go b/splitio/client/client.go index 4febed7a..ae1c774c 100644 --- a/splitio/client/client.go +++ b/splitio/client/client.go @@ -10,13 +10,13 @@ import ( "github.com/splitio/go-client/v6/splitio/conf" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" - "github.com/splitio/go-split-commons/v5/dtos" - "github.com/splitio/go-split-commons/v5/engine/evaluator" - "github.com/splitio/go-split-commons/v5/engine/evaluator/impressionlabels" - "github.com/splitio/go-split-commons/v5/flagsets" - "github.com/splitio/go-split-commons/v5/provisional" - "github.com/splitio/go-split-commons/v5/storage" - "github.com/splitio/go-split-commons/v5/telemetry" + "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v6/engine/evaluator" + "github.com/splitio/go-split-commons/v6/engine/evaluator/impressionlabels" + "github.com/splitio/go-split-commons/v6/flagsets" + "github.com/splitio/go-split-commons/v6/provisional" + "github.com/splitio/go-split-commons/v6/storage" + "github.com/splitio/go-split-commons/v6/telemetry" "github.com/splitio/go-toolkit/v5/logging" ) @@ -241,8 +241,6 @@ func (c *SplitClient) processResult(result evaluator.Results, operation string, // doTreatmentsCall retrieves treatments of an specific array of feature flag names with configurations object if it is present for a certain key and set of attributes func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []string, attributes map[string]interface{}, operation string, metricsLabel string) (t map[string]TreatmentResult) { - treatments := make(map[string]TreatmentResult) - // Set up a guard deferred function to recover if the SDK starts panicking defer func() { if r := recover(); r != nil { @@ -275,9 +273,7 @@ func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []strin evaluationsResult := c.getEvaluationsResult(matchingKey, bucketingKey, filteredFeatures, attributes, operation) - treatments = c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel) - - return treatments + return c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel) } // doTreatmentsCallByFlagSets retrieves treatments of a specific array of feature flag names, that belong to flag sets, with configurations object if it is present for a certain key and set of attributes diff --git a/splitio/client/client_test.go b/splitio/client/client_test.go index 2105f5db..96c7aecf 100644 --- a/splitio/client/client_test.go +++ b/splitio/client/client_test.go @@ -18,24 +18,24 @@ import ( "github.com/splitio/go-client/v6/splitio/conf" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" - commonsCfg "github.com/splitio/go-split-commons/v5/conf" - "github.com/splitio/go-split-commons/v5/dtos" - "github.com/splitio/go-split-commons/v5/engine/evaluator" - "github.com/splitio/go-split-commons/v5/engine/evaluator/impressionlabels" - evaluatorMock "github.com/splitio/go-split-commons/v5/engine/evaluator/mocks" - "github.com/splitio/go-split-commons/v5/healthcheck/application" - "github.com/splitio/go-split-commons/v5/provisional" - "github.com/splitio/go-split-commons/v5/provisional/strategy" - authMocks "github.com/splitio/go-split-commons/v5/service/mocks" - "github.com/splitio/go-split-commons/v5/storage" - "github.com/splitio/go-split-commons/v5/storage/inmemory" - "github.com/splitio/go-split-commons/v5/storage/inmemory/mutexqueue" - "github.com/splitio/go-split-commons/v5/storage/mocks" - "github.com/splitio/go-split-commons/v5/storage/redis" - "github.com/splitio/go-split-commons/v5/synchronizer" - syncMock "github.com/splitio/go-split-commons/v5/synchronizer/mocks" - "github.com/splitio/go-split-commons/v5/telemetry" - "github.com/splitio/go-split-commons/v5/util" + commonsCfg "github.com/splitio/go-split-commons/v6/conf" + "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v6/engine/evaluator" + "github.com/splitio/go-split-commons/v6/engine/evaluator/impressionlabels" + evaluatorMock "github.com/splitio/go-split-commons/v6/engine/evaluator/mocks" + "github.com/splitio/go-split-commons/v6/healthcheck/application" + "github.com/splitio/go-split-commons/v6/provisional" + "github.com/splitio/go-split-commons/v6/provisional/strategy" + authMocks "github.com/splitio/go-split-commons/v6/service/mocks" + "github.com/splitio/go-split-commons/v6/storage" + "github.com/splitio/go-split-commons/v6/storage/inmemory" + "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexqueue" + "github.com/splitio/go-split-commons/v6/storage/mocks" + "github.com/splitio/go-split-commons/v6/storage/redis" + "github.com/splitio/go-split-commons/v6/synchronizer" + syncMock "github.com/splitio/go-split-commons/v6/synchronizer/mocks" + "github.com/splitio/go-split-commons/v6/telemetry" + "github.com/splitio/go-split-commons/v6/util" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" @@ -622,6 +622,9 @@ func compareListener(ilTest map[string]interface{}, f string, k string, l string if ilTest["Version"] != v { return false } + if ilTest["InstanceName"] != i { + return false + } attr1, _ := ilTest["Attributes"].(map[string]interface{}) return attr1["One"] == a } @@ -679,7 +682,6 @@ func getClientForListener() SplitClient { } func TestImpressionListener(t *testing.T) { client := getClientForListener() - cfg := conf.Default() attributes := make(map[string]interface{}) attributes["One"] = "test" @@ -687,7 +689,7 @@ func TestImpressionListener(t *testing.T) { expectedTreatment(client.Treatment("user1", "feature", attributes), "TreatmentA", t) expectedVersion := "go-" + splitio.Version - if !compareListener(ilResult["feature"].(map[string]interface{}), "feature", "user1", "aLabel", "TreatmentA", int64(123), "", "test", cfg.InstanceName, expectedVersion) { + if !compareListener(ilResult["feature"].(map[string]interface{}), "feature", "user1", "aLabel", "TreatmentA", int64(123), "", "test", "ip-123-123-123-123", expectedVersion) { t.Error("Impression should match") } ilResult = make(map[string]interface{}) @@ -697,7 +699,6 @@ func TestImpressionListener(t *testing.T) { func TestImpressionListenerForTreatments(t *testing.T) { client := getClientForListener() - cfg := conf.Default() attributes := make(map[string]interface{}) attributes["One"] = "test" @@ -713,11 +714,11 @@ func TestImpressionListenerForTreatments(t *testing.T) { expectedVersion := "go-" + splitio.Version - if !compareListener(ilResult["feature"].(map[string]interface{}), "feature", "user1", "aLabel", "TreatmentA", int64(123), "", "test", cfg.InstanceName, expectedVersion) { + if !compareListener(ilResult["feature"].(map[string]interface{}), "feature", "user1", "aLabel", "TreatmentA", int64(123), "", "test", "ip-123-123-123-123", expectedVersion) { t.Error("Impression should match") } - if !compareListener(ilResult["feature2"].(map[string]interface{}), "feature2", "user1", "bLabel", "TreatmentB", int64(123), "", "test", cfg.InstanceName, expectedVersion) { + if !compareListener(ilResult["feature2"].(map[string]interface{}), "feature2", "user1", "bLabel", "TreatmentB", int64(123), "", "test", "ip-123-123-123-123", expectedVersion) { t.Error("Impression should match") } ilResult = make(map[string]interface{}) @@ -2113,6 +2114,122 @@ func TestClientDebug(t *testing.T) { } } +func TestUnsupportedMatcherAndSemver(t *testing.T) { + var isDestroyCalled = false + var splitsMock, _ = ioutil.ReadFile("../../testdata/splits_mock_3.json") + + postChannel := make(chan string, 1) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v2/auth": + if r.URL.Query().Get("s") != "1.1" { + t.Error("should be parameter s, for flags spec") + } + fmt.Fprintln(w, "{\"pushEnabled\": false, \"token\": \"token\"}") + return + case "/splitChanges": + fmt.Fprintln(w, string(splitsMock)) + return + case "/testImpressions/bulk": + if r.Header.Get("SplitSDKImpressionsMode") != commonsCfg.ImpressionsModeOptimized { + t.Error("Wrong header") + } + + if isDestroyCalled { + rBody, _ := ioutil.ReadAll(r.Body) + var dataInPost []map[string]interface{} + err := json.Unmarshal(rBody, &dataInPost) + if err != nil { + t.Error(err) + return + } + if len(dataInPost) != 6 { + t.Error("It should send two impressions in optimized mode") + } + for _, ki := range dataInPost { + if asISlice, ok := ki["i"].([]interface{}); !ok || len(asISlice) != 1 { + t.Error("It should send only one impression per featureName", dataInPost) + } + if ki["f"] == "unsupported" { + message := ki["i"].([]interface{})[0].(map[string]interface{})["r"] + if message != "targeting rule type unsupported by sdk" { + t.Error("message sould be: targeting rule type unsupported by sdk") + } + } + } + } + + fmt.Fprintln(w, "ok") + postChannel <- "finished" + case "/testImpressions/count": + fallthrough + case "/keys/ss": + fallthrough + case "/events/bulk": + fallthrough + case "/segmentChanges": + fallthrough + default: + fmt.Fprintln(w, "ok") + } + })) + defer ts.Close() + + cfg := conf.Default() + cfg.Advanced.AuthServiceURL = ts.URL + cfg.Advanced.EventsURL = ts.URL + cfg.Advanced.SdkURL = ts.URL + cfg.Advanced.TelemetryServiceURL = ts.URL + + factory, _ := NewSplitFactory("test", cfg) + client := factory.Client() + client.BlockUntilReady(2) + + // Calls treatments to generate one valid impression + time.Sleep(300 * time.Millisecond) // Let's wait until first call of recorders have finished + attributes := make(map[string]interface{}) + attributes["version"] = "1.22.9" + evaluation := client.Treatment("user1", "semver", attributes) + if evaluation != "on" { + t.Error("evaluation for semver should be on") + } + attributes["version"] = "2.0.0" + evaluation = client.Treatment("user1", "semver1", attributes) + if evaluation != "on" { + t.Error("evaluation for semver should be on") + } + evaluation = client.Treatment("user1", "semver2", attributes) + if evaluation != "on" { + t.Error("evaluation for semver should be on") + } + attributes["version"] = "1.0.0" + evaluation = client.Treatment("user1", "semver3", attributes) + if evaluation != "on" { + t.Error("evaluation for semver should be on") + } + attributes["version"] = "2.1.0" + evaluation = client.Treatment("user1", "semver4", attributes) + if evaluation != "on" { + t.Error("evaluation for semver should be on") + } + evaluation = client.Treatment("user1", "unsupported", nil) + if evaluation != "control" { + t.Error("evaluation for unsupported should be control") + } + + isDestroyCalled = true + client.Destroy() + + select { + case <-postChannel: + return + case <-time.After(4 * time.Second): + t.Error("The test couldn't send impressions to check headers") + return + } +} + func TestTelemetryMemory(t *testing.T) { factoryInstances = make(map[string]int64) var metricsInitCalled int64 @@ -2632,3 +2749,157 @@ func TestClientDebugRedis(t *testing.T) { prefixedClient.Del(k) } } + +var semver string = "3.4.5" +var attribute string = "version" + +var splitSemver = &dtos.SplitDTO{ + Algo: 2, + ChangeNumber: 1494593336752, + DefaultTreatment: "off", + Killed: false, + Name: "semver", + Seed: -1992295819, + Status: "ACTIVE", + TrafficAllocation: 100, + TrafficAllocationSeed: -285565213, + TrafficTypeName: "user", + Configurations: map[string]string{"on": "{\"color\": \"blue\",\"size\": 13}"}, + Conditions: []dtos.ConditionDTO{ + { + ConditionType: "ROLLOUT", + Label: "default rule", + MatcherGroup: dtos.MatcherGroupDTO{ + Combiner: "AND", + Matchers: []dtos.MatcherDTO{ + { + KeySelector: &dtos.KeySelectorDTO{ + TrafficType: "user", + Attribute: &attribute, + }, + MatcherType: "EQUAL_TO_SEMVER", + String: &semver, + Whitelist: nil, + Negate: false, + }, + }, + }, + Partitions: []dtos.PartitionDTO{ + { + Size: 100, + Treatment: "on", + }, + { + Size: 0, + Treatment: "off", + }, + }, + }, + }, +} + +var splitUnsupported = &dtos.SplitDTO{ + Algo: 2, + ChangeNumber: 1494593336752, + DefaultTreatment: "off", + Killed: false, + Name: "unsupported", + Seed: -1992295819, + Status: "ACTIVE", + TrafficAllocation: 100, + TrafficAllocationSeed: -285565213, + TrafficTypeName: "user", + Configurations: map[string]string{"on": "{\"color\": \"blue\",\"size\": 13}"}, + Conditions: []dtos.ConditionDTO{ + { + ConditionType: "ROLLOUT", + Label: "default rule", + MatcherGroup: dtos.MatcherGroupDTO{ + Combiner: "AND", + Matchers: []dtos.MatcherDTO{ + { + KeySelector: &dtos.KeySelectorDTO{ + TrafficType: "user", + Attribute: nil, + }, + MatcherType: "UNSUPPORTED", + Whitelist: nil, + Negate: false, + }, + }, + }, + Partitions: []dtos.PartitionDTO{ + { + Size: 100, + Treatment: "on", + }, + }, + }, + }, +} + +func TestUnsupportedandSemverMatcherRedis(t *testing.T) { + redisConfig := &commonsCfg.RedisConfig{ + Host: "localhost", + Port: 6379, + Password: "", + Prefix: "test-prefix-semver", + } + + prefixedClient, _ := redis.NewRedisClient(redisConfig, logging.NewLogger(&logging.LoggerOptions{})) + raw, _ := json.Marshal(*splitSemver) + prefixedClient.Set("SPLITIO.split.semver", raw, 0) + raw, _ = json.Marshal(*splitUnsupported) + prefixedClient.Set("SPLITIO.split.unsupported", raw, 0) + + impTest := &ImpressionListenerTest{} + cfg := conf.Default() + cfg.LabelsEnabled = true + cfg.Advanced.ImpressionListener = impTest + cfg.ImpressionsMode = commonsCfg.ImpressionsModeOptimized + cfg.OperationMode = conf.RedisConsumer + cfg.Redis = *redisConfig + + factory, _ := NewSplitFactory("test", cfg) + client := factory.Client() + client.BlockUntilReady(2) + + // Calls treatments to generate one valid impression + time.Sleep(300 * time.Millisecond) // Let's wait until first call of recorders have finished + attributes := make(map[string]interface{}) + attributes["version"] = "3.4.5" + evaluation := client.Treatment("user1", "semver", attributes) + if evaluation != "on" { + t.Error("evaluation for semver should be on") + } + evaluation = client.Treatment("user2", "unsupported", nil) + if evaluation != "control" { + t.Error("evaluation for unsupported should be control") + } + client.Destroy() + + // Validate impressions + impressions, _ := prefixedClient.LRange("SPLITIO.impressions", 0, -1) + + if len(impressions) != 2 { + t.Error("Impression length shold be 2") + } + + for _, imp := range impressions { + var imprObject dtos.ImpressionQueueObject + _ = json.Unmarshal([]byte(imp), &imprObject) + + if imprObject.Impression.KeyName == "user1" && imprObject.Impression.FeatureName == "semver" && imprObject.Impression.Pt != 0 { + t.Error("Pt should be 0.") + } + if imprObject.Impression.KeyName == "user2" && imprObject.Impression.FeatureName == "unsupported" && imprObject.Impression.Pt != 0 { + t.Error("Pt should be 0.") + } + } + + // Clean redis + keys, _ := prefixedClient.Keys("SPLITIO*") + for _, k := range keys { + prefixedClient.Del(k) + } +} diff --git a/splitio/client/factory.go b/splitio/client/factory.go index 4efe994c..a5d55664 100644 --- a/splitio/client/factory.go +++ b/splitio/client/factory.go @@ -14,31 +14,32 @@ import ( "github.com/splitio/go-client/v6/splitio/conf" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" - config "github.com/splitio/go-split-commons/v5/conf" - "github.com/splitio/go-split-commons/v5/dtos" - "github.com/splitio/go-split-commons/v5/engine" - "github.com/splitio/go-split-commons/v5/engine/evaluator" - "github.com/splitio/go-split-commons/v5/flagsets" - "github.com/splitio/go-split-commons/v5/healthcheck/application" - "github.com/splitio/go-split-commons/v5/provisional" - "github.com/splitio/go-split-commons/v5/provisional/strategy" - "github.com/splitio/go-split-commons/v5/service/api" - "github.com/splitio/go-split-commons/v5/service/local" - "github.com/splitio/go-split-commons/v5/storage" - "github.com/splitio/go-split-commons/v5/storage/filter" - "github.com/splitio/go-split-commons/v5/storage/inmemory" - "github.com/splitio/go-split-commons/v5/storage/inmemory/mutexmap" - "github.com/splitio/go-split-commons/v5/storage/inmemory/mutexqueue" - "github.com/splitio/go-split-commons/v5/storage/mocks" - "github.com/splitio/go-split-commons/v5/storage/redis" - "github.com/splitio/go-split-commons/v5/synchronizer" - "github.com/splitio/go-split-commons/v5/synchronizer/worker/event" - "github.com/splitio/go-split-commons/v5/synchronizer/worker/impression" - "github.com/splitio/go-split-commons/v5/synchronizer/worker/impressionscount" - "github.com/splitio/go-split-commons/v5/synchronizer/worker/segment" - "github.com/splitio/go-split-commons/v5/synchronizer/worker/split" - "github.com/splitio/go-split-commons/v5/tasks" - "github.com/splitio/go-split-commons/v5/telemetry" + config "github.com/splitio/go-split-commons/v6/conf" + "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v6/engine" + "github.com/splitio/go-split-commons/v6/engine/evaluator" + "github.com/splitio/go-split-commons/v6/flagsets" + "github.com/splitio/go-split-commons/v6/healthcheck/application" + "github.com/splitio/go-split-commons/v6/provisional" + "github.com/splitio/go-split-commons/v6/provisional/strategy" + "github.com/splitio/go-split-commons/v6/service/api" + "github.com/splitio/go-split-commons/v6/service/api/specs" + "github.com/splitio/go-split-commons/v6/service/local" + "github.com/splitio/go-split-commons/v6/storage" + "github.com/splitio/go-split-commons/v6/storage/filter" + "github.com/splitio/go-split-commons/v6/storage/inmemory" + "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexmap" + "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexqueue" + "github.com/splitio/go-split-commons/v6/storage/mocks" + "github.com/splitio/go-split-commons/v6/storage/redis" + "github.com/splitio/go-split-commons/v6/synchronizer" + "github.com/splitio/go-split-commons/v6/synchronizer/worker/event" + "github.com/splitio/go-split-commons/v6/synchronizer/worker/impression" + "github.com/splitio/go-split-commons/v6/synchronizer/worker/impressionscount" + "github.com/splitio/go-split-commons/v6/synchronizer/worker/segment" + "github.com/splitio/go-split-commons/v6/synchronizer/worker/split" + "github.com/splitio/go-split-commons/v6/tasks" + "github.com/splitio/go-split-commons/v6/telemetry" "github.com/splitio/go-toolkit/v5/logging" ) @@ -204,7 +205,7 @@ func (f *SplitFactory) subscribe(name int, subscriptor chan int) { } // removes a particular subscriptor from the list -func (f *SplitFactory) unsubscribe(name int, subscriptor chan int) { +func (f *SplitFactory) unsubscribe(name int) { f.mutex.Lock() defer f.mutex.Unlock() _, ok := f.readinessSubscriptors[name] @@ -233,7 +234,7 @@ func (f *SplitFactory) BlockUntilReady(timer int) error { defer func() { // Unsubscription will happen only if a block channel has been created if block != nil { - f.unsubscribe(subscriptorName, block) + f.unsubscribe(subscriptorName) close(block) } }() @@ -287,6 +288,8 @@ func setupInMemoryFactory( metadata dtos.Metadata, ) (*SplitFactory, error) { advanced, warnings := conf.NormalizeSDKConf(cfg.Advanced) + advanced.AuthSpecVersion = specs.FLAG_V1_1 + advanced.FlagsSpecVersion = specs.FLAG_V1_1 printWarnings(logger, warnings) flagSetsInvalid := int64(len(cfg.Advanced.FlagSetsFilter) - len(advanced.FlagSetsFilter)) if strings.TrimSpace(cfg.SplitSyncProxyURL) != "" { @@ -336,7 +339,7 @@ func setupInMemoryFactory( cfg.ImpressionsMode = config.ImpressionsModeOptimized } - impressionManager, err := buildImpressionManager(cfg, advanced, logger, true, &splitTasks, &workers, storages, metadata, splitAPI, nil) + impressionManager, err := buildImpressionManager(cfg, advanced, logger, true, &splitTasks, &workers, storages, metadata, splitAPI) if err != nil { return nil, err } @@ -430,7 +433,7 @@ func setupRedisFactory(apikey string, cfg *conf.SplitSdkConfig, logger logging.L cfg.ImpressionsMode = config.ImpressionsModeDebug } - impressionManager, err := buildImpressionManager(cfg, advanced, logger, false, &splitTasks, &workers, storages, metadata, nil, impressionStorage) + impressionManager, err := buildImpressionManager(cfg, advanced, logger, false, &splitTasks, &workers, storages, metadata, nil) if err != nil { return nil, err } @@ -600,7 +603,6 @@ func buildImpressionManager( storages sdkStorages, metadata dtos.Metadata, splitAPI *api.SplitAPI, - impressionRedisStorage storage.ImpressionStorageProducer, ) (provisional.ImpressionManager, error) { listenerEnabled := cfg.Advanced.ImpressionListener != nil switch cfg.ImpressionsMode { diff --git a/splitio/client/factory_test.go b/splitio/client/factory_test.go index a7512605..19801691 100644 --- a/splitio/client/factory_test.go +++ b/splitio/client/factory_test.go @@ -3,7 +3,7 @@ package client import ( "testing" - "github.com/splitio/go-split-commons/v5/flagsets" + "github.com/splitio/go-split-commons/v6/flagsets" ) func TestPrintWarnings(t *testing.T) { diff --git a/splitio/client/input_validator.go b/splitio/client/input_validator.go index d5044789..9b32f953 100644 --- a/splitio/client/input_validator.go +++ b/splitio/client/input_validator.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "github.com/splitio/go-split-commons/v5/engine/evaluator/impressionlabels" - "github.com/splitio/go-split-commons/v5/storage" + "github.com/splitio/go-split-commons/v6/engine/evaluator/impressionlabels" + "github.com/splitio/go-split-commons/v6/storage" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" ) @@ -263,12 +263,11 @@ func (i *inputValidation) validateTrackProperties(properties map[string]interfac size := 1024 // Average event size is ~750 bytes. Using 1kbyte as a starting point. for name, value := range properties { size += len(name) - switch value.(type) { + switch value := value.(type) { case int, int32, int64, uint, uint32, uint64, float32, float64, bool, nil: processed[name] = value case string: - asStr := value.(string) - size += len(asStr) + size += len(value) processed[name] = value default: i.logger.Warning("Property %s is of invalid type. Setting value to nil") diff --git a/splitio/client/input_validator_test.go b/splitio/client/input_validator_test.go index 5277a6f6..cc56e23a 100644 --- a/splitio/client/input_validator_test.go +++ b/splitio/client/input_validator_test.go @@ -15,20 +15,19 @@ import ( "time" "github.com/splitio/go-client/v6/splitio/conf" - commonsCfg "github.com/splitio/go-split-commons/v5/conf" - spConf "github.com/splitio/go-split-commons/v5/conf" - "github.com/splitio/go-split-commons/v5/dtos" - "github.com/splitio/go-split-commons/v5/flagsets" - "github.com/splitio/go-split-commons/v5/healthcheck/application" - "github.com/splitio/go-split-commons/v5/provisional" - "github.com/splitio/go-split-commons/v5/provisional/strategy" - "github.com/splitio/go-split-commons/v5/service/api" - authMocks "github.com/splitio/go-split-commons/v5/service/mocks" - "github.com/splitio/go-split-commons/v5/storage/inmemory/mutexmap" - "github.com/splitio/go-split-commons/v5/storage/inmemory/mutexqueue" - "github.com/splitio/go-split-commons/v5/storage/mocks" - "github.com/splitio/go-split-commons/v5/storage/redis" - "github.com/splitio/go-split-commons/v5/synchronizer" + commonsCfg "github.com/splitio/go-split-commons/v6/conf" + "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v6/flagsets" + "github.com/splitio/go-split-commons/v6/healthcheck/application" + "github.com/splitio/go-split-commons/v6/provisional" + "github.com/splitio/go-split-commons/v6/provisional/strategy" + "github.com/splitio/go-split-commons/v6/service/api" + authMocks "github.com/splitio/go-split-commons/v6/service/mocks" + "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexmap" + "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexqueue" + "github.com/splitio/go-split-commons/v6/storage/mocks" + "github.com/splitio/go-split-commons/v6/storage/redis" + "github.com/splitio/go-split-commons/v6/synchronizer" "github.com/splitio/go-toolkit/v5/logging" ) @@ -359,7 +358,7 @@ func TestValidatorOnDestroy(t *testing.T) { sync, _ := synchronizer.NewSynchronizerManager( synchronizer.NewLocal(localConfig, &api.SplitAPI{}, mocks.MockSplitStorage{}, mocks.MockSegmentStorage{}, logger, telemetryMockedStorage, &application.Dummy{}), logger, - spConf.AdvancedConfig{}, + commonsCfg.AdvancedConfig{}, authMocks.MockAuthClient{}, mocks.MockSplitStorage{}, make(chan int, 1), @@ -534,7 +533,7 @@ func TestInMemoryFactoryFlagSets(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/splitChanges": - if r.RequestURI != "/splitChanges?sets=a%2Cc%2Cd&since=-1" { + if r.RequestURI != "/splitChanges?s=1.1&since=-1&sets=a%2Cc%2Cd" { t.Error("wrong RequestURI for flag sets") } fmt.Fprintln(w, fmt.Sprintf(string(splitsMock), splitMock)) diff --git a/splitio/client/manager.go b/splitio/client/manager.go index 756340b0..fd65046d 100644 --- a/splitio/client/manager.go +++ b/splitio/client/manager.go @@ -3,8 +3,8 @@ package client import ( "fmt" - "github.com/splitio/go-split-commons/v5/dtos" - "github.com/splitio/go-split-commons/v5/storage" + "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v6/storage" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/client/manager_test.go b/splitio/client/manager_test.go index 9708101c..0934b478 100644 --- a/splitio/client/manager_test.go +++ b/splitio/client/manager_test.go @@ -3,9 +3,9 @@ package client import ( "testing" - "github.com/splitio/go-split-commons/v5/dtos" - "github.com/splitio/go-split-commons/v5/flagsets" - "github.com/splitio/go-split-commons/v5/storage/inmemory/mutexmap" + "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v6/flagsets" + "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexmap" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/conf/sdkconf.go b/splitio/conf/sdkconf.go index f7d8dcb9..b4317615 100644 --- a/splitio/conf/sdkconf.go +++ b/splitio/conf/sdkconf.go @@ -10,7 +10,7 @@ import ( "strings" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" - "github.com/splitio/go-split-commons/v5/conf" + "github.com/splitio/go-split-commons/v6/conf" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/nethelpers" diff --git a/splitio/conf/sdkconf_test.go b/splitio/conf/sdkconf_test.go index c5efd078..86991bb1 100644 --- a/splitio/conf/sdkconf_test.go +++ b/splitio/conf/sdkconf_test.go @@ -3,7 +3,7 @@ package conf import ( "testing" - "github.com/splitio/go-split-commons/v5/conf" + "github.com/splitio/go-split-commons/v6/conf" ) func TestSdkConfNormalization(t *testing.T) { diff --git a/splitio/conf/util.go b/splitio/conf/util.go index 2fdf5ddf..29c94c1a 100644 --- a/splitio/conf/util.go +++ b/splitio/conf/util.go @@ -3,8 +3,8 @@ package conf import ( "strings" - "github.com/splitio/go-split-commons/v5/conf" - "github.com/splitio/go-split-commons/v5/flagsets" + "github.com/splitio/go-split-commons/v6/conf" + "github.com/splitio/go-split-commons/v6/flagsets" ) // NormalizeSDKConf compares against SDK Config to set defaults diff --git a/splitio/impressionListener/impressions_listener_wrapper.go b/splitio/impressionListener/impressions_listener_wrapper.go index 31ff600e..70dcfd63 100644 --- a/splitio/impressionListener/impressions_listener_wrapper.go +++ b/splitio/impressionListener/impressions_listener_wrapper.go @@ -1,7 +1,7 @@ package impressionlistener import ( - "github.com/splitio/go-split-commons/v5/dtos" + "github.com/splitio/go-split-commons/v6/dtos" ) // ILObject struct to map entire data for listener diff --git a/splitio/version.go b/splitio/version.go index e682605a..2341ac5b 100644 --- a/splitio/version.go +++ b/splitio/version.go @@ -1,4 +1,4 @@ package splitio // Version contains a string with the split sdk version -const Version = "6.5.2" +const Version = "6.6.0" diff --git a/testdata/splits_mock_3.json b/testdata/splits_mock_3.json new file mode 100644 index 00000000..48a2abcf --- /dev/null +++ b/testdata/splits_mock_3.json @@ -0,0 +1,319 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver", + "trafficAllocation": 100, + "trafficAllocationSeed": 1314112417, + "seed": -2059033614, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "of", + "changeNumber": 1491244291288, + "algo": 2, + "configurations": { + "on": "{\"color\": \"blue\",\"size\": 13}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType":"EQUAL_TO_SEMVER", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "dependencyMatcherData":null, + "booleanMatcherData":null, + "stringMatcherData":"1.22.9" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "of", + "size": 0 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver1", + "trafficAllocation": 100, + "trafficAllocationSeed": 1314112417, + "seed": -2059033614, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "of", + "changeNumber": 1491244291288, + "algo": 2, + "configurations": { + "on": "{\"color\": \"blue\",\"size\": 13}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType":"BETWEEN_SEMVER", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "dependencyMatcherData":null, + "booleanMatcherData":null, + "stringMatcherData":null, + "betweenStringMatcherData":{ + "start":"1.22.9", + "end":"2.1.0" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "of", + "size": 0 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver2", + "trafficAllocation": 100, + "trafficAllocationSeed": 1314112417, + "seed": -2059033614, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "of", + "changeNumber": 1491244291288, + "algo": 2, + "configurations": { + "on": "{\"color\": \"blue\",\"size\": 13}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType":"GREATER_THAN_OR_EQUAL_TO_SEMVER", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "dependencyMatcherData":null, + "booleanMatcherData":null, + "stringMatcherData":"1.22.9" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "of", + "size": 0 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver3", + "trafficAllocation": 100, + "trafficAllocationSeed": 1314112417, + "seed": -2059033614, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "of", + "changeNumber": 1491244291288, + "algo": 2, + "configurations": { + "on": "{\"color\": \"blue\",\"size\": 13}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType":"LESS_THAN_OR_EQUAL_TO_SEMVER", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "dependencyMatcherData":null, + "booleanMatcherData":null, + "stringMatcherData":"1.22.9" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "of", + "size": 0 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver4", + "trafficAllocation": 100, + "trafficAllocationSeed": 1314112417, + "seed": -2059033614, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "of", + "changeNumber": 1491244291288, + "algo": 2, + "configurations": { + "on": "{\"color\": \"blue\",\"size\": 13}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType":"IN_LIST_SEMVER", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "1.22.9", + "2.1.0" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "dependencyMatcherData":null, + "booleanMatcherData":null, + "stringMatcherData":null, + "betweenStringMatcherData":null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "of", + "size": 0 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "unsupported", + "trafficAllocation": 100, + "trafficAllocationSeed": 1314112417, + "seed": -2059033614, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "of", + "changeNumber": 1491244291288, + "algo": 2, + "configurations": { + "on": "{\"color\": \"blue\",\"size\": 13}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "UNSUPPORTED", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "of", + "size": 100 + } + ], + "label": "in segment all" + } + ] + } + ], + "since": 1491244291288, + "till": 1491244291288 + } \ No newline at end of file