Skip to content

Commit 2e76790

Browse files
committed
Merge branch 'main' of github.com:splitio/go-split-commons
2 parents bfac7f1 + 0510832 commit 2e76790

File tree

214 files changed

+3407
-2832
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

214 files changed

+3407
-2832
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ jobs:
1818
- 6379:6379
1919
steps:
2020
- name: Checkout code
21-
uses: actions/checkout@v4
21+
uses: actions/checkout@v5
2222
with:
2323
fetch-depth: 0
2424

2525
- name: Set up Go version
26-
uses: actions/setup-go@v5
26+
uses: actions/setup-go@v6
2727
with:
2828
go-version: '1.21'
2929

.github/workflows/update-license-year.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Checkout
16-
uses: actions/checkout@v4
16+
uses: actions/checkout@v5
1717
with:
1818
fetch-depth: 0
1919

dtos/split.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ type FeatureFlagsDTO struct {
1616
Splits []SplitDTO `json:"d"`
1717
}
1818

19+
type SplitsDTO struct {
20+
Since int64 `json:"since"`
21+
Till int64 `json:"till"`
22+
Splits []SplitDTO `json:"splits"`
23+
}
24+
1925
type RuleBasedSegmentsDTO struct {
2026
Since int64 `json:"s"`
2127
Till int64 `json:"t"`

dtos/splitchangewrapper.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package dtos
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// FFResponse is an interface that abstracts the differences between different versions of feature flag responses.
8+
type FFResponse interface {
9+
NeedsAnotherFetch() bool
10+
RuleBasedSegments() []RuleBasedSegmentDTO
11+
FeatureFlags() []SplitDTO
12+
FFTill() int64
13+
RBTill() int64
14+
FFSince() int64
15+
RBSince() int64
16+
}
17+
18+
// FFResponseLegacy handles the legacy format of feature flag responses.
19+
type FFResponseLegacy struct {
20+
SplitChanges SplitsDTO
21+
}
22+
23+
// NewFFResponseLegacy creates a new FFResponseLegacy instance from the provided JSON data.
24+
func NewFFResponseLegacy(data []byte) (FFResponse, error) {
25+
var splitChangesDto SplitsDTO
26+
err := json.Unmarshal(data, &splitChangesDto)
27+
if err == nil {
28+
return &FFResponseLegacy{
29+
SplitChanges: splitChangesDto,
30+
}, nil
31+
}
32+
return nil, err
33+
}
34+
35+
// NeedsAnotherFetch checks if another fetch is needed based on the since and till values.
36+
func (ffLegacy *FFResponseLegacy) NeedsAnotherFetch() bool {
37+
return ffLegacy.SplitChanges.Since == ffLegacy.SplitChanges.Till
38+
}
39+
40+
// FeatureFlags returns the list of feature flags (splits) from the response.
41+
func (ffLegacy *FFResponseLegacy) FeatureFlags() []SplitDTO {
42+
return ffLegacy.SplitChanges.Splits
43+
}
44+
45+
// RuleBasedSegments returns an empty list as legacy responses do not contain rule-based segments.
46+
func (ffLegacy *FFResponseLegacy) RuleBasedSegments() []RuleBasedSegmentDTO {
47+
return []RuleBasedSegmentDTO{}
48+
}
49+
50+
// FFTill returns the till value for feature flags.
51+
func (ffLegacy *FFResponseLegacy) FFTill() int64 {
52+
return ffLegacy.SplitChanges.Till
53+
}
54+
55+
// RBTill returns 0 as legacy responses do not contain rule-based segments.
56+
func (ffLegacy *FFResponseLegacy) RBTill() int64 {
57+
return 0
58+
}
59+
60+
// FFSince returns the since value for feature flags.
61+
func (ffLegacy *FFResponseLegacy) FFSince() int64 {
62+
return ffLegacy.SplitChanges.Since
63+
}
64+
65+
// RBSince returns 0 as legacy responses do not contain rule-based segments.
66+
func (ffLegacy *FFResponseLegacy) RBSince() int64 {
67+
return 0
68+
}
69+
70+
// FFResponseV13 handles the version 1.3 format of feature flag responses.
71+
type FFResponseV13 struct {
72+
SplitChanges SplitChangesDTO
73+
}
74+
75+
// NewFFResponseV13 creates a new FFResponseV13 instance from the provided JSON data.
76+
func NewFFResponseV13(data []byte) (FFResponse, error) {
77+
var splitChangesDto SplitChangesDTO
78+
err := json.Unmarshal(data, &splitChangesDto)
79+
if err == nil {
80+
return &FFResponseV13{
81+
SplitChanges: splitChangesDto,
82+
}, nil
83+
}
84+
return nil, err
85+
}
86+
87+
// FeatureFlags returns the list of feature flags (splits) from the response.
88+
func (f13 *FFResponseV13) FeatureFlags() []SplitDTO {
89+
return f13.SplitChanges.FeatureFlags.Splits
90+
}
91+
92+
// RuleBasedSegments returns the list of rule-based segments from the response.
93+
func (f13 *FFResponseV13) RuleBasedSegments() []RuleBasedSegmentDTO {
94+
return f13.SplitChanges.RuleBasedSegments.RuleBasedSegments
95+
}
96+
97+
// NeedsAnotherFetch checks if another fetch is needed based on the since and till values for both feature flags and rule-based segments.
98+
func (f13 FFResponseV13) NeedsAnotherFetch() bool {
99+
return f13.SplitChanges.FeatureFlags.Since == f13.SplitChanges.FeatureFlags.Till && f13.SplitChanges.RuleBasedSegments.Since == f13.SplitChanges.RuleBasedSegments.Till
100+
}
101+
102+
// FFTill returns the till value for feature flags.
103+
func (f13 *FFResponseV13) FFTill() int64 {
104+
return f13.SplitChanges.FeatureFlags.Till
105+
}
106+
107+
// RBTill returns the till value for rule-based segments.
108+
func (f13 *FFResponseV13) RBTill() int64 {
109+
return f13.SplitChanges.RuleBasedSegments.Till
110+
}
111+
112+
// RBSince returns the since value for rule-based segments.
113+
func (f13 *FFResponseV13) RBSince() int64 {
114+
return f13.SplitChanges.RuleBasedSegments.Since
115+
}
116+
117+
// FFSince returns the since value for feature flags.
118+
func (f13 *FFResponseV13) FFSince() int64 {
119+
return f13.SplitChanges.FeatureFlags.Since
120+
}
121+
122+
// FFResponseLocalV13 is a local version of FFResponseV13 for internal use.
123+
type FFResponseLocalV13 struct {
124+
SplitChanges SplitChangesDTO
125+
}
126+
127+
// NewFFResponseLocalV13 creates a new FFResponseLocalV13 instance from the provided JSON data.
128+
func NewFFResponseLocalV13(data []byte) (*FFResponseLocalV13, error) {
129+
var splitChangesDto SplitChangesDTO
130+
err := json.Unmarshal(data, &splitChangesDto)
131+
if err == nil {
132+
return &FFResponseLocalV13{
133+
SplitChanges: splitChangesDto,
134+
}, nil
135+
}
136+
return nil, err
137+
}
138+
139+
// FeatureFlags returns the list of feature flags (splits) from the response.
140+
func (f *FFResponseLocalV13) FeatureFlags() []SplitDTO {
141+
return f.SplitChanges.FeatureFlags.Splits
142+
}
143+
144+
// RuleBasedSegments returns the list of rule-based segments from the response.
145+
func (f *FFResponseLocalV13) RuleBasedSegments() []RuleBasedSegmentDTO {
146+
return f.SplitChanges.RuleBasedSegments.RuleBasedSegments
147+
}
148+
149+
// NeedsAnotherFetch checks if another fetch is needed based on the since and till values for both feature flags and rule-based segments.
150+
func (f FFResponseLocalV13) NeedsAnotherFetch() bool {
151+
return f.SplitChanges.FeatureFlags.Since == f.SplitChanges.FeatureFlags.Till && f.SplitChanges.RuleBasedSegments.Since == f.SplitChanges.RuleBasedSegments.Till
152+
}
153+
154+
// FFTill returns the till value for feature flags.
155+
func (f *FFResponseLocalV13) FFTill() int64 {
156+
return f.SplitChanges.FeatureFlags.Till
157+
}
158+
159+
// RBTill returns the till value for rule-based segments.
160+
func (f *FFResponseLocalV13) RBTill() int64 {
161+
return f.SplitChanges.RuleBasedSegments.Till
162+
}
163+
164+
// RBSince returns the since value for rule-based segments.
165+
func (f *FFResponseLocalV13) RBSince() int64 {
166+
return f.SplitChanges.RuleBasedSegments.Since
167+
}
168+
169+
// FFSince returns the since value for feature flags.
170+
func (f *FFResponseLocalV13) SetFFTill(till int64) {
171+
f.SplitChanges.FeatureFlags.Till = till
172+
}
173+
174+
// SetFFSince sets the since value for feature flags.
175+
func (f *FFResponseLocalV13) SetFFSince(since int64) {
176+
f.SplitChanges.FeatureFlags.Since = since
177+
}
178+
179+
// SetRBTill sets the till value for rule-based segments.
180+
func (f *FFResponseLocalV13) SetRBTill(till int64) {
181+
f.SplitChanges.RuleBasedSegments.Till = till
182+
}
183+
184+
// FFSince returns the since value for feature flags.
185+
func (f *FFResponseLocalV13) FFSince() int64 {
186+
return f.SplitChanges.FeatureFlags.Since
187+
}
188+
189+
// SetRBSince sets the since value for rule-based segments.
190+
func (f *FFResponseLocalV13) SetRBSince(since int64) {
191+
f.SplitChanges.RuleBasedSegments.Since = since
192+
}
193+
194+
// ReplaceFF replaces the feature flags (splits) in the response with the provided list.
195+
func (f *FFResponseLocalV13) ReplaceFF(featureFlags []SplitDTO) {
196+
f.SplitChanges.FeatureFlags.Splits = featureFlags
197+
}
198+
199+
// ReplaceRB replaces the rule-based segments in the response with the provided list.
200+
func (f *FFResponseLocalV13) ReplaceRB(ruleBasedSegments []RuleBasedSegmentDTO) {
201+
f.SplitChanges.RuleBasedSegments.RuleBasedSegments = ruleBasedSegments
202+
}
203+
204+
var _ FFResponse = (*FFResponseLegacy)(nil)
205+
var _ FFResponse = (*FFResponseV13)(nil)
206+
var _ FFResponse = (*FFResponseLocalV13)(nil)

dtos/splitchangewrapper_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package dtos
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestFFResponseLegacy(t *testing.T) {
10+
some, err := NewFFResponseLegacy([]byte(`{wrong json}`))
11+
assert.Nil(t, some)
12+
assert.NotNil(t, err)
13+
14+
ok, err := NewFFResponseLegacy([]byte(`{"since":1,"till":2,"splits":[{"name":"split1"},{"name":"split2"}]}`))
15+
assert.NotNil(t, ok)
16+
assert.Nil(t, err)
17+
assert.Equal(t, int64(2), ok.FFTill())
18+
assert.Equal(t, int64(0), ok.RBTill())
19+
assert.Equal(t, int64(1), ok.FFSince())
20+
assert.Equal(t, int64(0), ok.RBSince())
21+
assert.False(t, ok.NeedsAnotherFetch())
22+
assert.Len(t, ok.FeatureFlags(), 2)
23+
assert.Empty(t, ok.RuleBasedSegments())
24+
25+
noFetch, err := NewFFResponseLegacy([]byte(`{"since":2,"till":2,"splits":[]}`))
26+
assert.NotNil(t, noFetch)
27+
assert.Nil(t, err)
28+
assert.Equal(t, int64(2), noFetch.FFTill())
29+
assert.Equal(t, int64(0), noFetch.RBTill())
30+
assert.Equal(t, int64(2), noFetch.FFSince())
31+
assert.Equal(t, int64(0), noFetch.RBSince())
32+
assert.True(t, noFetch.NeedsAnotherFetch())
33+
assert.Len(t, noFetch.FeatureFlags(), 0)
34+
assert.Empty(t, noFetch.RuleBasedSegments())
35+
}
36+
37+
func TestFFResponseLatest(t *testing.T) {
38+
some, err := NewFFResponseV13([]byte(`{wrong json}`))
39+
assert.Nil(t, some)
40+
assert.NotNil(t, err)
41+
42+
ok, err := NewFFResponseV13([]byte(`{
43+
"ff":{
44+
"s":1,
45+
"t":2,
46+
"d":[{"name":"split1"},{"name":"split2"}]
47+
},
48+
"rbs":{
49+
"s":3,
50+
"t":4,
51+
"d":[{"name":"rb1"},{"name":"rb2"},{"name":"rb3"}]
52+
}
53+
}`))
54+
assert.NotNil(t, ok)
55+
assert.Nil(t, err)
56+
assert.Equal(t, int64(2), ok.FFTill())
57+
assert.Equal(t, int64(4), ok.RBTill())
58+
assert.Equal(t, int64(1), ok.FFSince())
59+
assert.Equal(t, int64(3), ok.RBSince())
60+
assert.Len(t, ok.FeatureFlags(), 2)
61+
assert.Len(t, ok.RuleBasedSegments(), 3)
62+
assert.False(t, ok.NeedsAnotherFetch())
63+
64+
noFetch, err := NewFFResponseV13([]byte(`{
65+
"ff":{
66+
"s":2,
67+
"t":2,
68+
"d":[]
69+
},
70+
"rbs":{
71+
"s":4,
72+
"t":4,
73+
"d":[]
74+
}
75+
}`))
76+
assert.NotNil(t, noFetch)
77+
assert.Nil(t, err)
78+
assert.Equal(t, int64(2), noFetch.FFTill())
79+
assert.Equal(t, int64(4), noFetch.RBTill())
80+
assert.Equal(t, int64(2), noFetch.FFSince())
81+
assert.Equal(t, int64(4), noFetch.RBSince())
82+
assert.Len(t, noFetch.FeatureFlags(), 0)
83+
assert.Len(t, noFetch.RuleBasedSegments(), 0)
84+
assert.True(t, noFetch.NeedsAnotherFetch())
85+
}
86+
87+
func TestFFResponseLocalV13(t *testing.T) {
88+
some, err := NewFFResponseLocalV13([]byte(`{wrong json}`))
89+
assert.Nil(t, some)
90+
assert.NotNil(t, err)
91+
92+
ok, err := NewFFResponseLocalV13([]byte(`{
93+
"ff":{
94+
"s":1,
95+
"t":2,
96+
"d":[{"name":"split1"},{"name":"split2"}]
97+
},
98+
"rbs":{
99+
"s":3,
100+
"t":4,
101+
"d":[{"name":"rb1"},{"name":"rb2"},{"name":"rb3"}]
102+
}
103+
}`))
104+
assert.NotNil(t, ok)
105+
assert.Nil(t, err)
106+
assert.Equal(t, int64(2), ok.FFTill())
107+
assert.Equal(t, int64(4), ok.RBTill())
108+
assert.Equal(t, int64(1), ok.FFSince())
109+
assert.Equal(t, int64(3), ok.RBSince())
110+
assert.Len(t, ok.FeatureFlags(), 2)
111+
assert.Len(t, ok.RuleBasedSegments(), 3)
112+
assert.False(t, ok.NeedsAnotherFetch())
113+
114+
ok.SetFFTill(5)
115+
ok.SetFFSince(5)
116+
ok.SetRBSince(5)
117+
ok.SetRBTill(5)
118+
assert.Equal(t, int64(5), ok.FFTill())
119+
assert.Equal(t, int64(5), ok.RBTill())
120+
assert.Equal(t, int64(5), ok.FFSince())
121+
assert.Equal(t, int64(5), ok.RBSince())
122+
ok.ReplaceFF([]SplitDTO{{Name: "other"}})
123+
ok.ReplaceRB([]RuleBasedSegmentDTO{{Name: "other_rb"}})
124+
assert.Len(t, ok.FeatureFlags(), 1)
125+
assert.Len(t, ok.RuleBasedSegments(), 1)
126+
assert.Equal(t, "other", ok.FeatureFlags()[0].Name)
127+
assert.Equal(t, "other_rb", ok.RuleBasedSegments()[0].Name)
128+
129+
noFetch, err := NewFFResponseLocalV13([]byte(`{
130+
"ff":{
131+
"s":2,
132+
"t":2,
133+
"d":[]
134+
},
135+
"rbs":{
136+
"s":4,
137+
"t":4,
138+
"d":[]
139+
}
140+
}`))
141+
assert.NotNil(t, noFetch)
142+
assert.Nil(t, err)
143+
assert.Equal(t, int64(2), noFetch.FFTill())
144+
assert.Equal(t, int64(4), noFetch.RBTill())
145+
assert.Equal(t, int64(2), noFetch.FFSince())
146+
assert.Equal(t, int64(4), noFetch.RBSince())
147+
assert.Len(t, noFetch.FeatureFlags(), 0)
148+
assert.Len(t, noFetch.RuleBasedSegments(), 0)
149+
assert.True(t, noFetch.NeedsAnotherFetch())
150+
}

0 commit comments

Comments
 (0)