Skip to content

Commit 9aad07f

Browse files
authored
Merge pull request #328 from splitio/task/changesItem
Task/changes item
2 parents 8fd26d7 + fc57a59 commit 9aad07f

19 files changed

Lines changed: 608 additions & 194 deletions

splitio/admin/admin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func NewServer(options *Options) (*AdminServer, error) {
9696
observabilityController.Register(admin)
9797

9898
if options.Snapshotter != nil {
99-
snapshotController := controllers.NewSnapshotController(options.Logger, options.Snapshotter)
99+
snapshotController := controllers.NewSnapshotController(options.Logger, options.Snapshotter, options.FlagSpecVersion)
100100
snapshotController.Register(admin)
101101
}
102102

splitio/admin/controllers/dashboard.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ func (c *DashboardController) gatherStats() *dashboard.GlobalStats {
150150
FeatureFlags: bundleSplitInfo(c.storages.SplitStorage),
151151
Segments: bundleSegmentInfo(c.storages.SplitStorage, c.storages.SegmentStorage),
152152
LargeSegments: bundleLargeSegmentInfo(c.storages.SplitStorage, c.storages.LargeSegmentStorage),
153+
RuleBasedSegments: bundleRuleBasedInfo(c.storages.SplitStorage, c.storages.RuleBasedSegmentsStorage),
153154
Latencies: bundleProxyLatencies(c.storages.LocalTelemetryStorage),
154155
BackendLatencies: bundleLocalSyncLatencies(c.storages.LocalTelemetryStorage),
155156
ImpressionsQueueSize: getImpressionSize(c.storages.ImpressionStorage),

splitio/admin/controllers/helpers.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,45 @@ func bundleSegmentInfo(splitStorage storage.SplitStorage, segmentStorage storage
108108
return summaries
109109
}
110110

111+
func bundleRuleBasedInfo(splitStorage storage.SplitStorage, ruleBasedSegmentStorage storage.RuleBasedSegmentStorageConsumer) []dashboard.RuleBasedSegmentSummary {
112+
names := splitStorage.RuleBasedSegmentNames()
113+
summaries := make([]dashboard.RuleBasedSegmentSummary, 0, names.Size())
114+
115+
for _, name := range names.List() {
116+
strName, ok := name.(string)
117+
if !ok {
118+
continue
119+
}
120+
121+
ruleBased, err := ruleBasedSegmentStorage.GetRuleBasedSegmentByName(strName)
122+
if err != nil {
123+
continue
124+
}
125+
126+
excluededSegments := make([]dashboard.ExcludedSegments, 0, len(ruleBased.Excluded.Segments))
127+
for _, excludedSegment := range ruleBased.Excluded.Segments {
128+
excluededSegments = append(excluededSegments, dashboard.ExcludedSegments{
129+
Name: excludedSegment.Name,
130+
Type: excludedSegment.Type,
131+
})
132+
}
133+
134+
if ruleBased.Excluded.Keys == nil {
135+
ruleBased.Excluded.Keys = make([]string, 0)
136+
}
137+
138+
summaries = append(summaries, dashboard.RuleBasedSegmentSummary{
139+
Name: ruleBased.Name,
140+
Active: ruleBased.Status == "ACTIVE",
141+
ExcludedKeys: ruleBased.Excluded.Keys,
142+
ExcludedSegments: excluededSegments,
143+
LastModified: time.Unix(0, ruleBased.ChangeNumber*int64(time.Millisecond)).UTC().Format(time.UnixDate),
144+
ChangeNumber: ruleBased.ChangeNumber,
145+
})
146+
}
147+
return summaries
148+
}
149+
111150
func bundleSegmentKeysInfo(name string, segmentStorage storage.SegmentStorageConsumer) []dashboard.SegmentKeySummary {
112151

113152
keys := segmentStorage.Keys(name)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package controllers
2+
3+
import (
4+
"testing"
5+
6+
"github.com/splitio/split-synchronizer/v5/splitio/admin/views/dashboard"
7+
8+
"github.com/splitio/go-split-commons/v8/dtos"
9+
"github.com/splitio/go-split-commons/v8/storage/mocks"
10+
"github.com/splitio/go-toolkit/v5/datastructures/set"
11+
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestBundleRBInfo(t *testing.T) {
16+
split := &mocks.SplitStorageMock{}
17+
split.On("RuleBasedSegmentNames").Return(set.NewSet("rb1", "rb2"), nil).Once()
18+
rb := &mocks.MockRuleBasedSegmentStorage{}
19+
rb.On("GetRuleBasedSegmentByName", "rb1").Return(&dtos.RuleBasedSegmentDTO{Name: "rb1", ChangeNumber: 1, Status: "ACTIVE", Excluded: dtos.ExcludedDTO{Keys: []string{"one"}}}, nil).Once()
20+
rb.On("GetRuleBasedSegmentByName", "rb2").Return(&dtos.RuleBasedSegmentDTO{Name: "rb2", ChangeNumber: 2, Status: "ARCHIVED"}, nil).Once()
21+
result := bundleRuleBasedInfo(split, rb)
22+
assert.Len(t, result, 2)
23+
assert.ElementsMatch(t, result, []dashboard.RuleBasedSegmentSummary{
24+
{Name: "rb1", ChangeNumber: 1, Active: true, ExcludedKeys: []string{"one"}, ExcludedSegments: []dashboard.ExcludedSegments{}, LastModified: "Thu Jan 1 00:00:00 UTC 1970"},
25+
{Name: "rb2", ChangeNumber: 2, Active: false, ExcludedKeys: []string{}, ExcludedSegments: []dashboard.ExcludedSegments{}, LastModified: "Thu Jan 1 00:00:00 UTC 1970"},
26+
})
27+
split.AssertExpectations(t)
28+
rb.AssertExpectations(t)
29+
}

splitio/admin/controllers/snapshot.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import (
1414

1515
// SnapshotController bundles endpoints associated to snapshot management
1616
type SnapshotController struct {
17-
logger logging.LoggerInterface
18-
db storage.Snapshotter
17+
logger logging.LoggerInterface
18+
db storage.Snapshotter
19+
version string
1920
}
2021

2122
// NewSnapshotController constructs a new snapshot controller
22-
func NewSnapshotController(logger logging.LoggerInterface, db storage.Snapshotter) *SnapshotController {
23-
return &SnapshotController{logger: logger, db: db}
23+
func NewSnapshotController(logger logging.LoggerInterface, db storage.Snapshotter, version string) *SnapshotController {
24+
return &SnapshotController{logger: logger, db: db, version: version}
2425
}
2526

2627
// Register mounts the endpoints int he provided router
@@ -38,7 +39,7 @@ func (c *SnapshotController) downloadSnapshot(ctx *gin.Context) {
3839
return
3940
}
4041

41-
s, err := snapshot.New(snapshot.Metadata{Version: 1, Storage: snapshot.StorageBoltDB}, b)
42+
s, err := snapshot.New(snapshot.Metadata{Version: 1, Storage: snapshot.StorageBoltDB, SpecVersion: c.version}, b)
4243
if err != nil {
4344
c.logger.Error("error building snapshot: ", err)
4445
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "error building snapshot"})

splitio/admin/controllers/snapshot_test.go

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package controllers
22

33
import (
44
"bytes"
5-
"io/ioutil"
5+
"io"
66
"net/http"
77
"net/http/httptest"
88
"testing"
@@ -13,31 +13,23 @@ import (
1313
"github.com/splitio/go-toolkit/v5/logging"
1414

1515
"github.com/gin-gonic/gin"
16+
"github.com/stretchr/testify/assert"
1617
)
1718

1819
func TestDownloadProxySnapshot(t *testing.T) {
1920
// Read DB snapshot for test
2021
path := "../../../test/snapshot/proxy.snapshot"
2122
snap, err := snapshot.DecodeFromFile(path)
22-
if err != nil {
23-
t.Error(err)
24-
return
25-
}
23+
assert.Nil(t, err)
2624

2725
tmpDataFile, err := snap.WriteDataToTmpFile()
28-
if err != nil {
29-
t.Error(err)
30-
return
31-
}
26+
assert.Nil(t, err)
3227

3328
// loading snapshot from disk
3429
dbInstance, err := persistent.NewBoltWrapper(tmpDataFile, nil)
35-
if err != nil {
36-
t.Error(err)
37-
return
38-
}
30+
assert.Nil(t, err)
3931

40-
ctrl := NewSnapshotController(logging.NewLogger(nil), dbInstance)
32+
ctrl := NewSnapshotController(logging.NewLogger(nil), dbInstance, "1.3")
4133

4234
resp := httptest.NewRecorder()
4335
ctx, router := gin.CreateTestContext(resp)
@@ -46,35 +38,19 @@ func TestDownloadProxySnapshot(t *testing.T) {
4638
ctx.Request, _ = http.NewRequest(http.MethodGet, "/snapshot", nil)
4739
router.ServeHTTP(resp, ctx.Request)
4840

49-
responseBody, err := ioutil.ReadAll(resp.Body)
50-
if err != nil {
51-
t.Error(err)
52-
return
53-
}
41+
responseBody, err := io.ReadAll(resp.Body)
42+
assert.Nil(t, err)
5443

5544
snapRes, err := snapshot.Decode(responseBody)
56-
if err != nil {
57-
t.Error(err)
58-
return
59-
}
45+
assert.Nil(t, err)
6046

61-
if snapRes.Meta().Version != 1 {
62-
t.Error("Invalid Metadata version")
63-
}
64-
65-
if snapRes.Meta().Storage != 1 {
66-
t.Error("Invalid Metadata storage")
67-
}
47+
assert.Equal(t, uint64(1), snapRes.Meta().Version)
48+
assert.Equal(t, uint64(1), snapRes.Meta().Storage)
49+
assert.Equal(t, "1.3", snapRes.Meta().SpecVersion)
6850

6951
dat, err := snap.Data()
70-
if err != nil {
71-
t.Error(err)
72-
}
52+
assert.Nil(t, err)
7353
resData, err := snapRes.Data()
74-
if err != nil {
75-
t.Error(err)
76-
}
77-
if bytes.Compare(dat, resData) != 0 {
78-
t.Error("loaded snapshot is different to downloaded")
79-
}
54+
assert.Nil(t, err)
55+
assert.Equal(t, 0, bytes.Compare(dat, resData))
8056
}

splitio/admin/views/dashboard/datainspector.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ const dataInspector = `
3838
</a>
3939
</li>
4040
{{end}}
41+
<li role="presentation" class="">
42+
<a href="#rule-based-segments-data" aria-controls="rule-based" role="tab" data-toggle="tab">
43+
<span class="glyphicon" style="vertical-align:bottom" aria-hidden="true">
44+
<svg fill="none" height="24" width="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
45+
<path
46+
d="M6 4C5.44772 4 5 4.44772 5 5V9C5 9.55228 5.44772 10 6 10H10C10.5523 10 11 9.55228 11 9V8H13V11H10V13H13V16H11V15C11 14.4477 10.5523 14 10 14H6C5.44772 14 5 14.4477 5 15V19C5 19.5523 5.44772 20 6 20H10C10.5523 20 11 19.5523 11 19V18H13V20H18C18.5523 20 19 19.5523 19 19V15C19 14.4477 18.5523 14 18 14H15V10H18C18.5523 10 19 9.55228 19 9V5C19 4.44772 18.5523 4 18 4H13V6H11V5C11 4.44772 10.5523 4 10 4H6Z"
47+
fill="currentColor"
48+
/>
49+
</svg>
50+
</span>
51+
&nbsp;Rule-based Segments
52+
</a>
53+
</li>
4154
<li role="presentation" class="">
4255
<a href="#flag-sets-data" aria-controls="profile" role="tab" data-toggle="tab">
4356
<span class="glyphicon" style="vertical-align:bottom" aria-hidden="true">
@@ -154,6 +167,50 @@ const dataInspector = `
154167
</div>
155168
{{end}}
156169
170+
<!-- RULE-BASED SEGMENT DATA -->
171+
<div role="tabpanel" class="tab-pane" id="rule-based-segments-data">
172+
<div class="row">
173+
<div class="col-md-12">
174+
<div class="bg-primary metricBox">
175+
<!-- <h4>Rule-based Segments in proxy</h4> -->
176+
<div class="row">
177+
<div class="col-md-4 col-md-offset-8">
178+
<div class="input-group">
179+
<input type="text" id="filterRuleBasedSegmentNameInput" class="form-control" placeholder="Filter by Rule-based Segment name">
180+
<span class="input-group-btn">
181+
<button class="btn btn-default" type="button" onclick="javascript:filterRuleBasedSegments();">
182+
<span class="glyphicon glyphicon-filter" aria-hidden="true"></span>
183+
</button>
184+
<button class="btn btn-default" type="button" onclick="javascript:resetFilterRuleBasedSegments();">
185+
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
186+
</button>
187+
</span>
188+
</div>
189+
</div>
190+
</div>
191+
<div class="row">
192+
<div class="col-md-12">
193+
<table id="rule_based_segment_rows" class="table table-condensed table-hover">
194+
<thead>
195+
<tr>
196+
<th>Rule-based segment</th>
197+
<th>Status</th>
198+
<th>Excluded Keys</th>
199+
<th>Excluded segments</th>
200+
<th>Last Modified</th>
201+
</tr>
202+
</thead>
203+
<tbody>
204+
</tbody>
205+
</table>
206+
</div>
207+
</div>
208+
</div>
209+
</div>
210+
</div>
211+
</div>
212+
213+
157214
<!-- FLAG SETS DATA -->
158215
<div role="tabpanel" class="tab-pane" id="flag-sets-data">
159216
<div class="row">

splitio/admin/views/dashboard/js.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,23 @@ const mainScript = `
8686
}
8787
});
8888
}
89+
90+
function resetFilterRuleBasedSegments(){
91+
$("tr.ruleBasedItem").removeClass("filterDisplayNone");
92+
$("#filterRuleBasedSegmentNameInput").val("");
93+
}
94+
95+
function filterRuleBasedSegments(){
96+
$("tr.ruleBasedItem").removeClass("filterDisplayNone");
97+
var filter = $("#filterRuleBasedSegmentNameInput").val();
98+
$("tr.ruleBasedItem").each(function() {
99+
$this = $(this);
100+
var ruleBasedName = $this.find("span.ruleBasedItemName").html();
101+
if (ruleBasedName.indexOf(filter.trim()) == -1) {
102+
$this.addClass("filterDisplayNone");
103+
}
104+
});
105+
}
89106
90107
$(function () {
91108
$('[data-toggle="tooltip"]').tooltip()
@@ -255,6 +272,42 @@ const mainScript = `
255272
}
256273
};
257274
275+
function formatRuleBasedSegment(ruleBasedSegment) {
276+
var excludedSegments = Array.isArray(ruleBasedSegment.excludedSegments)
277+
? ruleBasedSegment.excludedSegments
278+
: [];
279+
280+
var excludedSegmentsHtml = excludedSegments.length
281+
? excludedSegments.map(function(seg, i) {
282+
var segName = seg && seg.name ? seg.name : 'Unnamed';
283+
var segType = seg && seg.type ? seg.type : 'Unknown';
284+
var separator = i < excludedSegments.length - 1 ? ', ' : '';
285+
return '<span>' + segName + ' (' + segType + ')</span>' + separator;
286+
}).join('')
287+
: '—';
288+
289+
return (
290+
'<tr class="ruleBasedItem">' +
291+
'<td><span class="ruleBasedItemName">' + ruleBasedSegment.name + '</span></td>' +
292+
(ruleBasedSegment.active
293+
? '<td class="">ACTIVE</td>'
294+
: '<td class="danger">ARCHIVED</td>') +
295+
'<td>' + (ruleBasedSegment.excludedKeys || '') + '</td>' +
296+
'<td>' + excludedSegmentsHtml + '</td>' +
297+
'<td>' + (ruleBasedSegment.cn || '') + '</td>' +
298+
'</tr>\n'
299+
);
300+
}
301+
302+
function updateRuleBasedSegments(ruleBasedSegments) {
303+
ruleBasedSegments.sort((a, b) => parseFloat(b.changeNumber) - parseFloat(a.changeNumber));
304+
const formatted = ruleBasedSegments.map(formatRuleBasedSegment).join('\n');
305+
if (document.getElementById('filterRuleBasedSegmentNameInput').value.length == 0) {
306+
$('#rule_based_segment_rows tbody').empty();
307+
$('#rule_based_segment_rows tbody').append(formatted);
308+
}
309+
};
310+
258311
function formatFlagSet(flagSet) {
259312
return (
260313
'<tr class="flagSetItem">' +
@@ -443,6 +496,7 @@ const mainScript = `
443496
updateFeatureFlags(stats.featureFlags);
444497
updateSegments(stats.segments);
445498
updateLargeSegments(stats.largesegments);
499+
updateRuleBasedSegments(stats.rulebasedsegments)
446500
updateLogEntries(stats.loggedMessages);
447501
updateFlagSets(stats.flagSets)
448502

0 commit comments

Comments
 (0)