Skip to content

Commit f25e5e3

Browse files
committed
feat: add server support for storing tag filters
1 parent 52c28e6 commit f25e5e3

File tree

8 files changed

+175
-14
lines changed

8 files changed

+175
-14
lines changed

dashboard/convert/dashboardtag.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package convert
2+
3+
import (
4+
"github.com/traggo/server/generated/gqlmodel"
5+
"github.com/traggo/server/model"
6+
)
7+
8+
func excludedTagsToExternal(tags []model.DashboardExcludedTag) []*gqlmodel.TimeSpanTag {
9+
result := []*gqlmodel.TimeSpanTag{}
10+
for _, tag := range tags {
11+
result = append(result, &gqlmodel.TimeSpanTag{
12+
Key: tag.Key,
13+
Value: tag.StringValue,
14+
})
15+
}
16+
return result
17+
}
18+
19+
func ExcludedTagsToInternal(gqls []*gqlmodel.InputTimeSpanTag) []model.DashboardExcludedTag {
20+
result := make([]model.DashboardExcludedTag, 0)
21+
for _, tag := range gqls {
22+
result = append(result, excludedTagToInternal(*tag))
23+
}
24+
return result
25+
}
26+
27+
func excludedTagToInternal(gqls gqlmodel.InputTimeSpanTag) model.DashboardExcludedTag {
28+
return model.DashboardExcludedTag{
29+
Key: gqls.Key,
30+
StringValue: gqls.Value,
31+
}
32+
}
33+
34+
func includedTagsToExternal(tags []model.DashboardIncludedTag) []*gqlmodel.TimeSpanTag {
35+
result := []*gqlmodel.TimeSpanTag{}
36+
for _, tag := range tags {
37+
result = append(result, &gqlmodel.TimeSpanTag{
38+
Key: tag.Key,
39+
Value: tag.StringValue,
40+
})
41+
}
42+
return result
43+
}
44+
45+
func IncludedTagsToInternal(gqls []*gqlmodel.InputTimeSpanTag) []model.DashboardIncludedTag {
46+
result := make([]model.DashboardIncludedTag, 0)
47+
for _, tag := range gqls {
48+
result = append(result, includedTagToInternal(*tag))
49+
}
50+
return result
51+
}
52+
53+
func includedTagToInternal(gqls gqlmodel.InputTimeSpanTag) model.DashboardIncludedTag {
54+
return model.DashboardIncludedTag{
55+
Key: gqls.Key,
56+
StringValue: gqls.Value,
57+
}
58+
}

dashboard/convert/entry.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ func ToExternalEntry(entry model.DashboardEntry) (*gqlmodel.DashboardEntry, erro
3838
To: entry.RangeTo,
3939
}
4040
stats := &gqlmodel.StatsSelection{
41-
Interval: ExternalInterval(entry.Interval),
42-
Tags: strings.Split(entry.Keys, ","),
43-
Range: dateRange,
41+
Interval: ExternalInterval(entry.Interval),
42+
Tags: strings.Split(entry.Keys, ","),
43+
Range: dateRange,
44+
ExcludeTags: excludedTagsToExternal(entry.ExcludedTags),
45+
IncludeTags: includedTagsToExternal(entry.IncludedTags),
4446
}
4547
if entry.RangeID != model.NoRangeIDDefined {
4648
stats.RangeID = &entry.RangeID

dashboard/entry/add.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ func (r *ResolverForEntry) AddDashboardEntry(ctx context.Context, dashboardID in
2424
return nil, err
2525
}
2626

27+
if tag := tagsDuplicates(stats.ExcludeTags, stats.IncludeTags); tag != nil {
28+
return nil, fmt.Errorf("tag '%s' is present in both exclude tags and include tags", tag.Key+":"+tag.Value)
29+
}
30+
31+
if err := tagsExist(r.DB, auth.GetUser(ctx).ID, stats.ExcludeTags); err != nil {
32+
return nil, fmt.Errorf("exclude tags: %s", err.Error())
33+
}
34+
35+
if err := tagsExist(r.DB, auth.GetUser(ctx).ID, stats.IncludeTags); err != nil {
36+
return nil, fmt.Errorf("include tags: %s", err.Error())
37+
}
38+
2739
entry := model.DashboardEntry{
2840
Keys: strings.Join(stats.Tags, ","),
2941
Type: convert.InternalEntryType(entryType),
@@ -34,6 +46,8 @@ func (r *ResolverForEntry) AddDashboardEntry(ctx context.Context, dashboardID in
3446
MobilePosition: convert.EmptyPos(),
3547
DesktopPosition: convert.EmptyPos(),
3648
RangeID: -1,
49+
ExcludedTags: convert.ExcludedTagsToInternal(stats.ExcludeTags),
50+
IncludedTags: convert.IncludedTagsToInternal(stats.IncludeTags),
3751
}
3852

3953
if len(stats.Tags) == 0 {

dashboard/entry/tagcheck.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package entry
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/jinzhu/gorm"
7+
"github.com/traggo/server/generated/gqlmodel"
8+
"github.com/traggo/server/model"
9+
)
10+
11+
func tagsDuplicates(src []*gqlmodel.InputTimeSpanTag, dst []*gqlmodel.InputTimeSpanTag) *gqlmodel.InputTimeSpanTag {
12+
existingTags := make(map[string]struct{})
13+
for _, tag := range src {
14+
existingTags[tag.Key+":"+tag.Value] = struct{}{}
15+
}
16+
17+
for _, tag := range dst {
18+
if _, ok := existingTags[tag.Key+":"+tag.Value]; ok {
19+
return tag
20+
}
21+
22+
existingTags[tag.Key] = struct{}{}
23+
}
24+
25+
return nil
26+
}
27+
28+
func tagsExist(db *gorm.DB, userID int, tags []*gqlmodel.InputTimeSpanTag) error {
29+
existingTags := make(map[string]struct{})
30+
31+
for _, tag := range tags {
32+
if _, ok := existingTags[tag.Key]; ok {
33+
return fmt.Errorf("tag '%s' is present multiple times", tag.Key)
34+
}
35+
36+
if db.Where("key = ?", tag.Key).Where("user_id = ?", userID).Find(new(model.TagDefinition)).RecordNotFound() {
37+
return fmt.Errorf("tag '%s' does not exist", tag.Key)
38+
}
39+
40+
existingTags[tag.Key] = struct{}{}
41+
}
42+
return nil
43+
}

dashboard/entry/update.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ func (r *ResolverForEntry) UpdateDashboardEntry(ctx context.Context, id int, ent
5353
entry.RangeFrom = stats.Range.From
5454
entry.RangeTo = stats.Range.To
5555
}
56+
57+
if tag := tagsDuplicates(stats.ExcludeTags, stats.IncludeTags); tag != nil {
58+
return nil, fmt.Errorf("tag '%s' is present in both exclude tags and include tags", tag.Key+":"+tag.Value)
59+
}
60+
61+
r.DB.Where("dashboard_entry_id = ?", id).Delete(new(model.DashboardExcludedTag))
62+
r.DB.Where("dashboard_entry_id = ?", id).Delete(new(model.DashboardIncludedTag))
63+
64+
if err := tagsExist(r.DB, auth.GetUser(ctx).ID, stats.ExcludeTags); err != nil {
65+
return nil, fmt.Errorf("exclude tags: %s", err.Error())
66+
}
67+
68+
if err := tagsExist(r.DB, auth.GetUser(ctx).ID, stats.IncludeTags); err != nil {
69+
return nil, fmt.Errorf("include tags: %s", err.Error())
70+
}
71+
72+
entry.ExcludedTags = convert.ExcludedTagsToInternal(stats.ExcludeTags)
73+
entry.IncludedTags = convert.IncludedTagsToInternal(stats.IncludeTags)
74+
5675
entry.Keys = strings.Join(stats.Tags, ",")
5776
entry.Interval = convert.InternalInterval(stats.Interval)
5877
}

dashboard/get.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ func (r *ResolverForDashboard) Dashboards(ctx context.Context) ([]*gqlmodel.Dash
1515
userID := auth.GetUser(ctx).ID
1616

1717
dashboards := []model.Dashboard{}
18-
find := r.DB.Preload("Entries").Preload("Ranges").Where(&model.Dashboard{UserID: userID}).Find(&dashboards)
18+
19+
q := r.DB
20+
q = q.Preload("Entries")
21+
q = q.Preload("Entries.ExcludedTags")
22+
q = q.Preload("Entries.IncludedTags")
23+
q = q.Preload("Ranges")
24+
25+
find := q.Where(&model.Dashboard{UserID: userID}).Find(&dashboards)
1926

2027
if find.Error != nil {
2128
return nil, find.Error

model/all.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ func All() []interface{} {
1111
new(UserSetting),
1212
new(Dashboard),
1313
new(DashboardEntry),
14+
new(DashboardExcludedTag),
15+
new(DashboardIncludedTag),
1416
new(DashboardRange),
1517
}
1618
}

model/dashboard.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,37 @@ type Dashboard struct {
2626

2727
// DashboardEntry an entry which represents a diagram in a dashboard.
2828
type DashboardEntry struct {
29-
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
30-
DashboardID int `gorm:"type:int REFERENCES dashboards(id) ON DELETE CASCADE"`
31-
Title string
32-
Total bool `gorm:"default:false"`
33-
Type DashboardType
34-
Keys string
35-
Interval Interval
36-
RangeID int
37-
RangeFrom string
38-
RangeTo string
29+
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
30+
DashboardID int `gorm:"type:int REFERENCES dashboards(id) ON DELETE CASCADE"`
31+
Title string
32+
Total bool `gorm:"default:false"`
33+
Type DashboardType
34+
Keys string
35+
Interval Interval
36+
RangeID int
37+
RangeFrom string
38+
RangeTo string
39+
ExcludedTags []DashboardExcludedTag
40+
IncludedTags []DashboardIncludedTag
3941

4042
MobilePosition string
4143
DesktopPosition string
4244
}
4345

46+
// DashboardExcludedTag a tag for filtering timespans
47+
type DashboardExcludedTag struct {
48+
DashboardEntryID int `gorm:"type:int REFERENCES dashboard_entries(id) ON DELETE CASCADE"`
49+
Key string
50+
StringValue string
51+
}
52+
53+
// DashboardIncludedTag a tag for filtering timespans
54+
type DashboardIncludedTag struct {
55+
DashboardEntryID int `gorm:"type:int REFERENCES dashboard_entries(id) ON DELETE CASCADE"`
56+
Key string
57+
StringValue string
58+
}
59+
4460
// DashboardType the dashboard type
4561
type DashboardType string
4662

0 commit comments

Comments
 (0)