Skip to content

Commit d87396a

Browse files
committed
[FME-10091] Added prerequisites
1 parent 360ab72 commit d87396a

File tree

6 files changed

+298
-12
lines changed

6 files changed

+298
-12
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/splitio/go-client/v6
33
go 1.18
44

55
require (
6-
github.com/splitio/go-split-commons/v6 v6.1.1-0.20250912200801-1a8d4e3ca70b
6+
github.com/splitio/go-split-commons/v6 v6.1.1-0.20250917114718-912b35ecd63c
77
github.com/splitio/go-toolkit/v5 v5.4.0
88

99
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
1818
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1919
github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc=
2020
github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
21-
github.com/splitio/go-split-commons/v6 v6.1.1-0.20250912200801-1a8d4e3ca70b h1:M/WRMUM2GZzZIymNbgrbW4PSN1zicZSdfK61MDXwlgo=
22-
github.com/splitio/go-split-commons/v6 v6.1.1-0.20250912200801-1a8d4e3ca70b/go.mod h1:cuW2HjbJJCVjlmmgJyoTs5UFxz8ET8vCgMUvZg1/EBg=
21+
github.com/splitio/go-split-commons/v6 v6.1.1-0.20250917114718-912b35ecd63c h1:mWSkeg2oNXzheiEZ/LzEIjsOBvEpr/scYWkLBQhPnBM=
22+
github.com/splitio/go-split-commons/v6 v6.1.1-0.20250917114718-912b35ecd63c/go.mod h1:cuW2HjbJJCVjlmmgJyoTs5UFxz8ET8vCgMUvZg1/EBg=
2323
github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM=
2424
github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko=
2525
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=

splitio/client/client_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3106,3 +3106,105 @@ func TestUnsupportedandSemverMatcherRedis(t *testing.T) {
31063106
prefixedClient.Del(k)
31073107
}
31083108
}
3109+
3110+
func TestPrerequisites(t *testing.T) {
3111+
var isDestroyCalled = false
3112+
var splitsMock, _ = ioutil.ReadFile("../../testdata/splits_mock_5.json")
3113+
3114+
postChannel := make(chan string, 1)
3115+
3116+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3117+
switch r.URL.Path {
3118+
case "/api/v2/auth":
3119+
if r.URL.Query().Get("s") != "1.3" {
3120+
t.Error("should be parameter s, for flags spec")
3121+
}
3122+
fmt.Fprintln(w, "{\"pushEnabled\": false, \"token\": \"token\"}")
3123+
return
3124+
case "/splitChanges":
3125+
fmt.Fprintln(w, string(splitsMock))
3126+
return
3127+
case "/testImpressions/bulk":
3128+
if r.Header.Get("SplitSDKImpressionsMode") != commonsCfg.ImpressionsModeOptimized {
3129+
t.Error("Wrong header")
3130+
}
3131+
3132+
if isDestroyCalled {
3133+
rBody, _ := ioutil.ReadAll(r.Body)
3134+
var dataInPost []map[string]interface{}
3135+
err := json.Unmarshal(rBody, &dataInPost)
3136+
if err != nil {
3137+
t.Error(err)
3138+
return
3139+
}
3140+
if len(dataInPost) != 1 {
3141+
t.Error("It should send one impressions in optimized mode")
3142+
}
3143+
for _, ki := range dataInPost {
3144+
if asISlice, ok := ki["i"].([]interface{}); !ok || len(asISlice) != 3 {
3145+
t.Error("It should send three impressions per featureName", dataInPost)
3146+
}
3147+
if ki["f"] == "unsupported" {
3148+
message := ki["i"].([]interface{})[0].(map[string]interface{})["r"]
3149+
if message != "targeting rule type unsupported by sdk" {
3150+
t.Error("message sould be: targeting rule type unsupported by sdk")
3151+
}
3152+
}
3153+
}
3154+
}
3155+
3156+
fmt.Fprintln(w, "ok")
3157+
postChannel <- "finished"
3158+
case "/testImpressions/count":
3159+
fallthrough
3160+
case "/keys/ss":
3161+
fallthrough
3162+
case "/events/bulk":
3163+
fallthrough
3164+
case "/segmentChanges":
3165+
default:
3166+
fmt.Fprintln(w, "ok")
3167+
return
3168+
}
3169+
}))
3170+
defer ts.Close()
3171+
3172+
cfg := conf.Default()
3173+
cfg.Advanced.AuthServiceURL = ts.URL
3174+
cfg.Advanced.EventsURL = ts.URL
3175+
cfg.Advanced.SdkURL = ts.URL
3176+
cfg.Advanced.TelemetryServiceURL = ts.URL
3177+
3178+
factory, _ := NewSplitFactory("test", cfg)
3179+
client := factory.Client()
3180+
client.BlockUntilReady(2)
3181+
3182+
// Calls treatments to generate one valid impression
3183+
time.Sleep(300 * time.Millisecond) // Let's wait until first call of recorders have finished
3184+
3185+
evaluation := client.Treatment("mauro@split.io", "always_on_if_prerequisite", nil)
3186+
if evaluation != "off" {
3187+
t.Error("evaluation for mauro@split.io should be off")
3188+
}
3189+
3190+
evaluation = client.Treatment("bilal@split.io", "always_on_if_prerequisite", nil)
3191+
if evaluation != "on" {
3192+
t.Error("evaluation for bilal@split.io should be on")
3193+
}
3194+
3195+
evaluation = client.Treatment("other_key", "always_on_if_prerequisite", nil)
3196+
if evaluation != "off" {
3197+
t.Error("evaluation for other_key should be off")
3198+
}
3199+
3200+
isDestroyCalled = true
3201+
client.Destroy()
3202+
3203+
select {
3204+
case <-postChannel:
3205+
return
3206+
case <-time.After(4 * time.Second):
3207+
t.Error("The test couldn't send impressions to check headers")
3208+
return
3209+
}
3210+
}

splitio/client/manager.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@ type SplitManager struct {
1919

2020
// SplitView is a partial representation of a currently stored split
2121
type SplitView struct {
22-
Name string `json:"name"`
23-
TrafficType string `json:"trafficType"`
24-
Killed bool `json:"killed"`
25-
Treatments []string `json:"treatments"`
26-
ChangeNumber int64 `json:"changeNumber"`
27-
Configs map[string]string `json:"configs"`
28-
DefaultTreatment string `json:"defaultTreatment"`
29-
Sets []string `json:"sets"`
30-
ImpressionsDisabled bool `json:"impressionsDisabled"`
22+
Name string `json:"name"`
23+
TrafficType string `json:"trafficType"`
24+
Killed bool `json:"killed"`
25+
Treatments []string `json:"treatments"`
26+
ChangeNumber int64 `json:"changeNumber"`
27+
Configs map[string]string `json:"configs"`
28+
DefaultTreatment string `json:"defaultTreatment"`
29+
Sets []string `json:"sets"`
30+
ImpressionsDisabled bool `json:"impressionsDisabled"`
31+
Prerequisites []dtos.Prerequisite `json:"prerequisites"`
3132
}
3233

3334
func newSplitView(splitDto *dtos.SplitDTO) *SplitView {
@@ -51,6 +52,7 @@ func newSplitView(splitDto *dtos.SplitDTO) *SplitView {
5152
DefaultTreatment: splitDto.DefaultTreatment,
5253
Sets: sets,
5354
ImpressionsDisabled: splitDto.ImpressionsDisabled,
55+
Prerequisites: splitDto.Prerequisites,
5456
}
5557
}
5658

splitio/client/manager_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ func TestSplitManager(t *testing.T) {
3636
Name: "split2",
3737
Killed: true,
3838
TrafficTypeName: "tt2",
39+
Prerequisites: []dtos.Prerequisite{
40+
{
41+
FeatureFlagName: "ff1",
42+
Treatments: []string{
43+
"off",
44+
"v1",
45+
},
46+
},
47+
},
3948
Conditions: []dtos.ConditionDTO{
4049
{
4150
Partitions: []dtos.PartitionDTO{
@@ -85,6 +94,10 @@ func TestSplitManager(t *testing.T) {
8594
t.Error("track impressions for split1 should be false")
8695
}
8796

97+
if s1.Prerequisites != nil {
98+
t.Error("prerequisistes should be nil for s1")
99+
}
100+
88101
s2 := manager.Split("split2")
89102
if s2.Name != "split2" || !s2.Killed || s2.TrafficType != "tt2" || s2.ChangeNumber != 123 {
90103
t.Error("Split 2 stored incorrectly")
@@ -101,6 +114,10 @@ func TestSplitManager(t *testing.T) {
101114
t.Error("track impressions for split2 should be false")
102115
}
103116

117+
if len(s2.Prerequisites) != 1 {
118+
t.Error("prerequisites size should be 1")
119+
}
120+
104121
all := manager.Splits()
105122
if len(all) != 2 {
106123
t.Error("Incorrect number of splits returned")

testdata/splits_mock_5.json

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
{
2+
"ff": {
3+
"s": 1602796638344,
4+
"t": 1602796638344,
5+
"d": [
6+
{
7+
"name": "always_on_if_prerequisite",
8+
"trafficTypeName": "user",
9+
"changeNumber": 5,
10+
"seed": -790401604,
11+
"trafficAllocation": 0,
12+
"trafficAllocationSeed": 1828377380,
13+
"status": "ACTIVE",
14+
"killed": false,
15+
"defaultTreatment": "off",
16+
"prerequisites": [
17+
{
18+
"n": "rbs_test_flag",
19+
"ts": [
20+
"v1"
21+
]
22+
}
23+
],
24+
"conditions": [
25+
{
26+
"matcherGroup": {
27+
"combiner": "AND",
28+
"matchers": [
29+
{
30+
"keySelector": {
31+
"trafficType": "user",
32+
"attribute": null
33+
},
34+
"matcherType": "ALL_KEYS",
35+
"negate": false,
36+
"userDefinedSegmentMatcherData": null,
37+
"whitelistMatcherData": null,
38+
"unaryNumericMatcherData": null,
39+
"betweenMatcherData": null
40+
}
41+
]
42+
},
43+
"partitions": [
44+
{
45+
"treatment": "on",
46+
"size": 100
47+
}
48+
],
49+
"label": "default rule"
50+
}
51+
]
52+
},
53+
{
54+
"name": "rbs_test_flag",
55+
"trafficTypeName": "user",
56+
"trafficAllocation": 100,
57+
"trafficAllocationSeed": 1828377380,
58+
"seed": -286617921,
59+
"status": "ACTIVE",
60+
"killed": false,
61+
"defaultTreatment": "off",
62+
"algo": 2,
63+
"conditions": [
64+
{
65+
"conditionType": "ROLLOUT",
66+
"matcherGroup": {
67+
"combiner": "AND",
68+
"matchers": [
69+
{
70+
"keySelector": {
71+
"trafficType": "user"
72+
},
73+
"matcherType": "IN_RULE_BASED_SEGMENT",
74+
"negate": false,
75+
"userDefinedSegmentMatcherData": {
76+
"segmentName": "test_rule_based_segment"
77+
}
78+
}
79+
]
80+
},
81+
"partitions": [
82+
{
83+
"treatment": "v1",
84+
"size": 100
85+
},
86+
{
87+
"treatment": "v2",
88+
"size": 0
89+
}
90+
],
91+
"label": "in rule based segment test_rule_based_segment"
92+
},
93+
{
94+
"conditionType": "ROLLOUT",
95+
"matcherGroup": {
96+
"combiner": "AND",
97+
"matchers": [
98+
{
99+
"keySelector": {
100+
"trafficType": "user"
101+
},
102+
"matcherType": "ALL_KEYS",
103+
"negate": false
104+
}
105+
]
106+
},
107+
"partitions": [
108+
{
109+
"treatment": "v1",
110+
"size": 0
111+
},
112+
{
113+
"treatment": "v2",
114+
"size": 100
115+
}
116+
],
117+
"label": "default rule"
118+
}
119+
],
120+
"configurations": {},
121+
"sets": [],
122+
"impressionsDisabled": false
123+
}
124+
]
125+
},
126+
"rbs": {
127+
"s": 100,
128+
"t": 100,
129+
"d": [
130+
{
131+
"name": "test_rule_based_segment",
132+
"status": "ACTIVE",
133+
"trafficTypeName": "user",
134+
"excluded": {
135+
"keys": [
136+
"mauro@split.io",
137+
"gaston@split.io"
138+
],
139+
"segments": []
140+
},
141+
"conditions": [
142+
{
143+
"matcherGroup": {
144+
"combiner": "AND",
145+
"matchers": [
146+
{
147+
"keySelector": {
148+
"trafficType": "user"
149+
},
150+
"matcherType": "ENDS_WITH",
151+
"negate": false,
152+
"whitelistMatcherData": {
153+
"whitelist": [
154+
"@split.io"
155+
]
156+
}
157+
}
158+
]
159+
}
160+
}
161+
]
162+
}
163+
]
164+
}
165+
}

0 commit comments

Comments
 (0)