snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
build / build (push) Failing after 3s
Some checks failed
build / build (push) Failing after 3s
This commit is contained in:
37
dashboard/convert/dashboard.go
Normal file
37
dashboard/convert/dashboard.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// ToExternalDashboards converts dashboards.
|
||||
func ToExternalDashboards(dashboards []model.Dashboard) ([]*gqlmodel.Dashboard, error) {
|
||||
result := []*gqlmodel.Dashboard{}
|
||||
for _, dashboard := range dashboards {
|
||||
if converted, err := ToExternalDashboard(dashboard); err == nil {
|
||||
result = append(result, converted)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToExternalDashboard converts a dashboard.
|
||||
func ToExternalDashboard(dashboard model.Dashboard) (*gqlmodel.Dashboard, error) {
|
||||
entries, err := toExternalEntries(dashboard.Entries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ranges, err := toExternalDashboardsRanges(dashboard.Ranges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &gqlmodel.Dashboard{
|
||||
ID: dashboard.ID,
|
||||
Name: dashboard.Name,
|
||||
Items: entries,
|
||||
Ranges: ranges,
|
||||
}, nil
|
||||
}
|
||||
39
dashboard/convert/dashboardtag.go
Normal file
39
dashboard/convert/dashboardtag.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
func tagFiltersToExternal(tags []model.DashboardTagFilter, include bool) []*gqlmodel.TimeSpanTag {
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := []*gqlmodel.TimeSpanTag{}
|
||||
for _, tag := range tags {
|
||||
if tag.Include == include {
|
||||
result = append(result, &gqlmodel.TimeSpanTag{
|
||||
Key: tag.Key,
|
||||
Value: tag.StringValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TagFiltersToInternal(gqls []*gqlmodel.InputTimeSpanTag, include bool) []model.DashboardTagFilter {
|
||||
result := make([]model.DashboardTagFilter, 0)
|
||||
for _, tag := range gqls {
|
||||
result = append(result, tagFilterToInternal(*tag, include))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func tagFilterToInternal(gqls gqlmodel.InputTimeSpanTag, include bool) model.DashboardTagFilter {
|
||||
return model.DashboardTagFilter{
|
||||
Key: gqls.Key,
|
||||
StringValue: gqls.Value,
|
||||
Include: include,
|
||||
}
|
||||
}
|
||||
59
dashboard/convert/entry.go
Normal file
59
dashboard/convert/entry.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
func toExternalEntries(entries []model.DashboardEntry) ([]*gqlmodel.DashboardEntry, error) {
|
||||
result := []*gqlmodel.DashboardEntry{}
|
||||
for _, entry := range entries {
|
||||
converted, err := ToExternalEntry(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, converted)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToExternalEntry converts entries.
|
||||
func ToExternalEntry(entry model.DashboardEntry) (*gqlmodel.DashboardEntry, error) {
|
||||
mobilePosition, err := toExternalPosition(entry.MobilePosition)
|
||||
if err != nil {
|
||||
return &gqlmodel.DashboardEntry{}, err
|
||||
}
|
||||
desktopPos, err := toExternalPosition(entry.DesktopPosition)
|
||||
if err != nil {
|
||||
return &gqlmodel.DashboardEntry{}, err
|
||||
}
|
||||
pos := gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Mobile: enhancePos(mobilePosition, "mobile"),
|
||||
Desktop: enhancePos(desktopPos, "desktop"),
|
||||
}
|
||||
dateRange := &gqlmodel.RelativeOrStaticRange{
|
||||
From: entry.RangeFrom,
|
||||
To: entry.RangeTo,
|
||||
}
|
||||
stats := &gqlmodel.StatsSelection{
|
||||
Interval: ExternalInterval(entry.Interval),
|
||||
Tags: strings.Split(entry.Keys, ","),
|
||||
Range: dateRange,
|
||||
ExcludeTags: tagFiltersToExternal(entry.TagFilters, false),
|
||||
IncludeTags: tagFiltersToExternal(entry.TagFilters, true),
|
||||
}
|
||||
if entry.RangeID != model.NoRangeIDDefined {
|
||||
stats.RangeID = &entry.RangeID
|
||||
stats.Range = nil
|
||||
}
|
||||
return &gqlmodel.DashboardEntry{
|
||||
ID: entry.ID,
|
||||
Title: entry.Title,
|
||||
Total: entry.Total,
|
||||
Pos: &pos,
|
||||
StatsSelection: stats,
|
||||
EntryType: ExternalEntryType(entry.Type),
|
||||
}, nil
|
||||
}
|
||||
46
dashboard/convert/entrytype.go
Normal file
46
dashboard/convert/entrytype.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// InternalEntryType converts entry type.
|
||||
func InternalEntryType(entryType gqlmodel.EntryType) model.DashboardType {
|
||||
switch entryType {
|
||||
case gqlmodel.EntryTypeBarChart:
|
||||
return model.TypeBarChart
|
||||
case gqlmodel.EntryTypePieChart:
|
||||
return model.TypePieChart
|
||||
case gqlmodel.EntryTypeStackedBarChart:
|
||||
return model.TypeStackedBarChart
|
||||
case gqlmodel.EntryTypeLineChart:
|
||||
return model.TypeLineChart
|
||||
case gqlmodel.EntryTypeHorizontalTable:
|
||||
return model.HorizontalTable
|
||||
case gqlmodel.EntryTypeVerticalTable:
|
||||
return model.VerticalTable
|
||||
default:
|
||||
panic("unknown entry type " + entryType)
|
||||
}
|
||||
}
|
||||
|
||||
// ExternalEntryType converts entry type.
|
||||
func ExternalEntryType(entryType model.DashboardType) gqlmodel.EntryType {
|
||||
switch entryType {
|
||||
case model.TypeBarChart:
|
||||
return gqlmodel.EntryTypeBarChart
|
||||
case model.TypePieChart:
|
||||
return gqlmodel.EntryTypePieChart
|
||||
case model.TypeStackedBarChart:
|
||||
return gqlmodel.EntryTypeStackedBarChart
|
||||
case model.TypeLineChart:
|
||||
return gqlmodel.EntryTypeLineChart
|
||||
case model.HorizontalTable:
|
||||
return gqlmodel.EntryTypeHorizontalTable
|
||||
case model.VerticalTable:
|
||||
return gqlmodel.EntryTypeVerticalTable
|
||||
default:
|
||||
panic("unknown entry type " + entryType)
|
||||
}
|
||||
}
|
||||
46
dashboard/convert/interval.go
Normal file
46
dashboard/convert/interval.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// InternalInterval converts interval.
|
||||
func InternalInterval(interval gqlmodel.StatsInterval) model.Interval {
|
||||
switch interval {
|
||||
case gqlmodel.StatsIntervalHourly:
|
||||
return model.IntervalHourly
|
||||
case gqlmodel.StatsIntervalDaily:
|
||||
return model.IntervalDaily
|
||||
case gqlmodel.StatsIntervalWeekly:
|
||||
return model.IntervalWeekly
|
||||
case gqlmodel.StatsIntervalMonthly:
|
||||
return model.IntervalMonthly
|
||||
case gqlmodel.StatsIntervalYearly:
|
||||
return model.IntervalYearly
|
||||
case gqlmodel.StatsIntervalSingle:
|
||||
return model.IntervalSingle
|
||||
default:
|
||||
panic("unknown interval type " + interval)
|
||||
}
|
||||
}
|
||||
|
||||
// ExternalInterval converts interval.
|
||||
func ExternalInterval(interval model.Interval) gqlmodel.StatsInterval {
|
||||
switch interval {
|
||||
case model.IntervalHourly:
|
||||
return gqlmodel.StatsIntervalHourly
|
||||
case model.IntervalDaily:
|
||||
return gqlmodel.StatsIntervalDaily
|
||||
case model.IntervalWeekly:
|
||||
return gqlmodel.StatsIntervalWeekly
|
||||
case model.IntervalMonthly:
|
||||
return gqlmodel.StatsIntervalMonthly
|
||||
case model.IntervalYearly:
|
||||
return gqlmodel.StatsIntervalYearly
|
||||
case model.IntervalSingle:
|
||||
return gqlmodel.StatsIntervalSingle
|
||||
default:
|
||||
panic("unknown interval type " + interval)
|
||||
}
|
||||
}
|
||||
111
dashboard/convert/position.go
Normal file
111
dashboard/convert/position.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
func enhancePos(pos *gqlmodel.DashboardEntryPos, size string) *gqlmodel.DashboardEntryPos {
|
||||
switch size {
|
||||
case "mobile":
|
||||
pos.MinH = 2
|
||||
pos.MinW = 1
|
||||
case "desktop":
|
||||
pos.MinH = 3
|
||||
pos.MinW = 2
|
||||
default:
|
||||
panic("unknown size")
|
||||
}
|
||||
if pos.MinW > pos.W {
|
||||
pos.W = pos.MinW
|
||||
}
|
||||
if pos.MinH > pos.H {
|
||||
pos.H = pos.MinH
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
// EmptyPos returns a empty position.
|
||||
func EmptyPos() string {
|
||||
internal := internalDashboardPositionV1{
|
||||
Version: "v1",
|
||||
H: 0,
|
||||
W: 0,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
}
|
||||
|
||||
if bytes, err := json.Marshal(&internal); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return string(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func toInternalPosition(external gqlmodel.InputDashboardEntryPos) (string, error) {
|
||||
internal := internalDashboardPositionV1{Version: "v1"}
|
||||
if err := copier.Copy(&internal, &external); err != nil {
|
||||
return "", err
|
||||
}
|
||||
bytes, err := json.Marshal(&internal)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// ApplyPos copies the position to the external position.
|
||||
func ApplyPos(entry *model.DashboardEntry, pos *gqlmodel.InputResponsiveDashboardEntryPos) error {
|
||||
if pos == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if pos.Mobile != nil {
|
||||
lpos, err := toInternalPosition(*pos.Mobile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.MobilePosition = lpos
|
||||
}
|
||||
if pos.Desktop != nil {
|
||||
mpos, err := toInternalPosition(*pos.Desktop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.DesktopPosition = mpos
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toExternalPosition(pos string) (*gqlmodel.DashboardEntryPos, error) {
|
||||
if pos == "" {
|
||||
return nil, nil
|
||||
}
|
||||
internal := internalDashboardPositionV1{}
|
||||
if err := json.Unmarshal([]byte(pos), &internal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if internal.Version != "v1" {
|
||||
log.Error().Interface("pos", internal).Msg("Invalid position version")
|
||||
}
|
||||
external := gqlmodel.DashboardEntryPos{}
|
||||
|
||||
if err := copier.Copy(&external, &internal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &external, nil
|
||||
}
|
||||
|
||||
type internalDashboardPositionV1 struct {
|
||||
Version string
|
||||
W int `json:"w"`
|
||||
H int `json:"h"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
}
|
||||
46
dashboard/convert/range.go
Normal file
46
dashboard/convert/range.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
"github.com/traggo/server/time"
|
||||
)
|
||||
|
||||
func toExternalDashboardsRanges(ranges []model.DashboardRange) ([]*gqlmodel.NamedDateRange, error) {
|
||||
result := []*gqlmodel.NamedDateRange{}
|
||||
for _, xrange := range ranges {
|
||||
result = append(result, ToExternalDashboardRange(xrange))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToExternalDashboardRange converts a range.
|
||||
func ToExternalDashboardRange(xrange model.DashboardRange) *gqlmodel.NamedDateRange {
|
||||
return &gqlmodel.NamedDateRange{
|
||||
ID: xrange.ID,
|
||||
Name: xrange.Name,
|
||||
Editable: xrange.Editable,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: xrange.From,
|
||||
To: xrange.To,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ToInternalDashboardRange converts a range.
|
||||
func ToInternalDashboardRange(xrange gqlmodel.InputNamedDateRange) (model.DashboardRange, error) {
|
||||
if err := time.Validate(xrange.Range.From); err != nil {
|
||||
return model.DashboardRange{}, fmt.Errorf("range from (%s) invalid: %s", xrange.Range.From, err)
|
||||
}
|
||||
if err := time.Validate(xrange.Range.To); err != nil {
|
||||
return model.DashboardRange{}, fmt.Errorf("range to (%s) invalid: %s", xrange.Range.To, err)
|
||||
}
|
||||
return model.DashboardRange{
|
||||
Editable: xrange.Editable,
|
||||
From: xrange.Range.From,
|
||||
To: xrange.Range.To,
|
||||
Name: xrange.Name,
|
||||
}, nil
|
||||
}
|
||||
27
dashboard/create.go
Normal file
27
dashboard/create.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// CreateDashboard creates a dashboard.
|
||||
func (r *ResolverForDashboard) CreateDashboard(ctx context.Context, name string) (*gqlmodel.Dashboard, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
dashboard := model.Dashboard{
|
||||
UserID: userID,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
create := r.DB.Create(&dashboard)
|
||||
if create.Error != nil {
|
||||
return &gqlmodel.Dashboard{}, create.Error
|
||||
}
|
||||
|
||||
return convert.ToExternalDashboard(dashboard)
|
||||
}
|
||||
106
dashboard/dashboard_test.go
Normal file
106
dashboard/dashboard_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/test"
|
||||
"github.com/traggo/server/test/fake"
|
||||
)
|
||||
|
||||
func TestDashboard(t *testing.T) {
|
||||
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
db.User(1)
|
||||
db.User(2)
|
||||
|
||||
resolver := NewResolverForDashboard(db.DB)
|
||||
|
||||
// initial no dashboards
|
||||
user1 := fake.User(1)
|
||||
user2 := fake.User(2)
|
||||
dashboards, err := resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dashboards)
|
||||
|
||||
dashboard, err := resolver.CreateDashboard(user1, "cool dashboard")
|
||||
require.NoError(t, err)
|
||||
expectAdded := &gqlmodel.Dashboard{
|
||||
ID: 1,
|
||||
Name: "cool dashboard",
|
||||
Ranges: []*gqlmodel.NamedDateRange{},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
}
|
||||
require.Equal(t, expectAdded, dashboard)
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.Dashboard{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "cool dashboard",
|
||||
Ranges: []*gqlmodel.NamedDateRange{},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
},
|
||||
}, dashboards)
|
||||
dashboards, err = resolver.Dashboards(user2)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dashboards)
|
||||
|
||||
dashboard, err = resolver.UpdateDashboard(user2, 1, "kek")
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
require.Nil(t, dashboard)
|
||||
|
||||
dashboard, err = resolver.UpdateDashboard(user2, 55, "kek")
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
require.Nil(t, dashboard)
|
||||
|
||||
dashboard, err = resolver.UpdateDashboard(user1, 1, "kek")
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.Dashboard{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "kek",
|
||||
Ranges: []*gqlmodel.NamedDateRange{},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
},
|
||||
}, dashboards)
|
||||
|
||||
dashboard, err = resolver.RemoveDashboard(user2, 1)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
require.Nil(t, dashboard)
|
||||
|
||||
dashboard, err = resolver.RemoveDashboard(user2, 55)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
require.Nil(t, dashboard)
|
||||
|
||||
dashboard, err = resolver.RemoveDashboard(user1, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dashboards)
|
||||
}
|
||||
|
||||
func TestDeleteDashboard(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
dbOne := db.User(1).Dashboard("test")
|
||||
dbOne.Range("r")
|
||||
dbOne.Entry("e")
|
||||
dbTwo := db.User(2).Dashboard("test")
|
||||
dbTwo.Range("r")
|
||||
dbTwo.Entry("e")
|
||||
|
||||
resolver := NewResolverForDashboard(db.DB)
|
||||
_, err := resolver.RemoveDashboard(fake.User(dbOne.User.ID), dbOne.Dashboard.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
dbOne.AssertExists(false).AssertHasEntry("e", false).AssertHasRange("r", false)
|
||||
dbTwo.AssertExists(true).AssertHasEntry("e", true).AssertHasRange("r", true)
|
||||
}
|
||||
27
dashboard/dashboardresolver.go
Normal file
27
dashboard/dashboardresolver.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/traggo/server/dashboard/dbrange"
|
||||
"github.com/traggo/server/dashboard/entry"
|
||||
)
|
||||
|
||||
// ResolverForDashboard resolves dashboard specific things.
|
||||
type ResolverForDashboard struct {
|
||||
DB *gorm.DB
|
||||
entry.ResolverForEntry
|
||||
dbrange.ResolverForRange
|
||||
}
|
||||
|
||||
// NewResolverForDashboard creates a new resolver.
|
||||
func NewResolverForDashboard(db *gorm.DB) ResolverForDashboard {
|
||||
return ResolverForDashboard{
|
||||
DB: db,
|
||||
ResolverForEntry: entry.ResolverForEntry{
|
||||
DB: db,
|
||||
},
|
||||
ResolverForRange: dbrange.ResolverForRange{
|
||||
DB: db,
|
||||
},
|
||||
}
|
||||
}
|
||||
28
dashboard/dbrange/add.go
Normal file
28
dashboard/dbrange/add.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package dbrange
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
)
|
||||
|
||||
// AddDashboardRange adds a dashboard range.
|
||||
func (r *ResolverForRange) AddDashboardRange(ctx context.Context, dashboardID int, rangeArg gqlmodel.InputNamedDateRange) (*gqlmodel.NamedDateRange, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
if _, err := util.FindDashboard(r.DB, userID, dashboardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rangeToAdd, err := convert.ToInternalDashboardRange(rangeArg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rangeToAdd.DashboardID = dashboardID
|
||||
|
||||
save := r.DB.Create(&rangeToAdd)
|
||||
return convert.ToExternalDashboardRange(rangeToAdd), save.Error
|
||||
}
|
||||
8
dashboard/dbrange/dbrangeresolver.go
Normal file
8
dashboard/dbrange/dbrangeresolver.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package dbrange
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
// ResolverForRange resolves range specific things.
|
||||
type ResolverForRange struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
201
dashboard/dbrange/ranges_test.go
Normal file
201
dashboard/dbrange/ranges_test.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package dbrange_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/dashboard"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/test"
|
||||
"github.com/traggo/server/test/fake"
|
||||
)
|
||||
|
||||
func TestRanges(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
resolver := dashboard.NewResolverForDashboard(db.DB)
|
||||
|
||||
// initial no dashboards
|
||||
user1 := fake.User(1)
|
||||
user2 := fake.User(2)
|
||||
db.User(1)
|
||||
db.User(2)
|
||||
dashboards, err := resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dashboards)
|
||||
|
||||
dashboard, err := resolver.CreateDashboard(user1, "cool dashboard")
|
||||
require.NoError(t, err)
|
||||
expectAdded := &gqlmodel.Dashboard{
|
||||
ID: 1,
|
||||
Name: "cool dashboard",
|
||||
Ranges: []*gqlmodel.NamedDateRange{},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
}
|
||||
require.Equal(t, expectAdded, dashboard)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.Dashboard{expectAdded}, dashboards)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user2)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dashboards)
|
||||
|
||||
xrange, err := resolver.AddDashboardRange(user1, dashboard.ID, gqlmodel.InputNamedDateRange{
|
||||
Name: "new range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &gqlmodel.NamedDateRange{
|
||||
ID: 1,
|
||||
Name: "new range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d",
|
||||
},
|
||||
}, xrange)
|
||||
_, err = resolver.AddDashboardRange(user1, dashboard.ID, gqlmodel.InputNamedDateRange{
|
||||
Name: "new range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1",
|
||||
To: "now-2d",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, err, "range from (now-1) invalid: expected unit at the end but got nothing")
|
||||
_, err = resolver.AddDashboardRange(user1, dashboard.ID, gqlmodel.InputNamedDateRange{
|
||||
Name: "new range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, err, "range to (now-2) invalid: expected unit at the end but got nothing")
|
||||
_, err = resolver.AddDashboardRange(user1, 100, gqlmodel.InputNamedDateRange{
|
||||
Name: "new range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.Dashboard{{
|
||||
ID: 1,
|
||||
Name: "cool dashboard",
|
||||
Ranges: []*gqlmodel.NamedDateRange{{
|
||||
ID: 1,
|
||||
Name: "new range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d",
|
||||
},
|
||||
}},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
}}, dashboards)
|
||||
|
||||
_, err = resolver.UpdateDashboardRange(user2, 1, gqlmodel.InputNamedDateRange{
|
||||
Name: "my range",
|
||||
Editable: true,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d/w",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
xrange, err = resolver.UpdateDashboardRange(user1, 1, gqlmodel.InputNamedDateRange{
|
||||
Name: "my range",
|
||||
Editable: true,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d/w",
|
||||
},
|
||||
})
|
||||
require.Equal(t, &gqlmodel.NamedDateRange{
|
||||
ID: 1,
|
||||
Name: "my range",
|
||||
Editable: true,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d/w",
|
||||
},
|
||||
}, xrange)
|
||||
_, err = resolver.UpdateDashboardRange(user1, 1, gqlmodel.InputNamedDateRange{
|
||||
Name: "my range",
|
||||
Editable: true,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, err, "range to (now-2) invalid: expected unit at the end but got nothing")
|
||||
_, err = resolver.UpdateDashboardRange(user1, 44, gqlmodel.InputNamedDateRange{
|
||||
Name: "my range",
|
||||
Editable: true,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, err, "dashboard range does not exist")
|
||||
|
||||
entry, err := resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "other", true, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
RangeID: &xrange.ID,
|
||||
}, &gqlmodel.InputResponsiveDashboardEntryPos{Desktop: &gqlmodel.InputDashboardEntryPos{
|
||||
H: 1,
|
||||
W: 2,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
_, err = resolver.RemoveDashboardRange(user1, xrange.ID)
|
||||
require.EqualError(t, err, "range is used in entries: other")
|
||||
_, err = resolver.RemoveDashboardEntry(user1, entry.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.Dashboard{{
|
||||
ID: 1,
|
||||
Name: "cool dashboard",
|
||||
Ranges: []*gqlmodel.NamedDateRange{{
|
||||
ID: 1,
|
||||
Name: "my range",
|
||||
Editable: true,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d/w",
|
||||
},
|
||||
}},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
}}, dashboards)
|
||||
|
||||
_, err = resolver.RemoveDashboardRange(user2, 1)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
_, err = resolver.RemoveDashboardRange(user2, 55)
|
||||
require.EqualError(t, err, "dashboard range does not exist")
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, dashboards[0].Ranges, 1)
|
||||
|
||||
_, err = resolver.RemoveDashboardRange(user1, 1)
|
||||
require.NoError(t, err)
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, dashboards[0].Ranges, 0)
|
||||
|
||||
}
|
||||
43
dashboard/dbrange/remove.go
Normal file
43
dashboard/dbrange/remove.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package dbrange
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// RemoveDashboardRange removes a dashboard range.
|
||||
func (r *ResolverForRange) RemoveDashboardRange(ctx context.Context, rangeID int) (*gqlmodel.NamedDateRange, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
dashboardRange, err := util.FindDashboardRange(r.DB, rangeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := util.FindDashboard(r.DB, userID, dashboardRange.DashboardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries := []model.DashboardEntry{}
|
||||
if err := r.DB.Where(model.DashboardEntry{RangeID: rangeID}).Find(&entries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
for _, entry := range entries {
|
||||
names = append(names, entry.Title)
|
||||
}
|
||||
|
||||
if len(entries) != 0 {
|
||||
return nil, errors.New("range is used in entries: " + strings.Join(names, ","))
|
||||
}
|
||||
|
||||
remove := r.DB.Delete(&model.DashboardRange{}, rangeID)
|
||||
return convert.ToExternalDashboardRange(dashboardRange), remove.Error
|
||||
}
|
||||
35
dashboard/dbrange/update.go
Normal file
35
dashboard/dbrange/update.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package dbrange
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
)
|
||||
|
||||
// UpdateDashboardRange updates a dashboard range.
|
||||
func (r *ResolverForRange) UpdateDashboardRange(ctx context.Context, rangeID int, rangeArg gqlmodel.InputNamedDateRange) (*gqlmodel.NamedDateRange, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
dashboardRange, err := util.FindDashboardRange(r.DB, rangeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := util.FindDashboard(r.DB, userID, dashboardRange.DashboardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rangeToUpdate, err := convert.ToInternalDashboardRange(rangeArg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rangeToUpdate.DashboardID = dashboardRange.DashboardID
|
||||
rangeToUpdate.ID = dashboardRange.ID
|
||||
|
||||
save := r.DB.Save(rangeToUpdate)
|
||||
return convert.ToExternalDashboardRange(rangeToUpdate), save.Error
|
||||
}
|
||||
77
dashboard/entry/add.go
Normal file
77
dashboard/entry/add.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package entry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/traggo/server/time"
|
||||
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// AddDashboardEntry adds a dashboard entry.
|
||||
func (r *ResolverForEntry) AddDashboardEntry(ctx context.Context, dashboardID int, entryType gqlmodel.EntryType, title string, total bool, stats gqlmodel.InputStatsSelection, pos *gqlmodel.InputResponsiveDashboardEntryPos) (*gqlmodel.DashboardEntry, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
if _, err := util.FindDashboard(r.DB, userID, dashboardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tagFilters := convert.TagFiltersToInternal(stats.ExcludeTags, false)
|
||||
tagFilters = append(tagFilters, convert.TagFiltersToInternal(stats.IncludeTags, true)...)
|
||||
|
||||
if err := tagsDuplicates(tagFilters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entry := model.DashboardEntry{
|
||||
Keys: strings.Join(stats.Tags, ","),
|
||||
Type: convert.InternalEntryType(entryType),
|
||||
Title: title,
|
||||
Total: total,
|
||||
DashboardID: dashboardID,
|
||||
Interval: convert.InternalInterval(stats.Interval),
|
||||
MobilePosition: convert.EmptyPos(),
|
||||
DesktopPosition: convert.EmptyPos(),
|
||||
RangeID: -1,
|
||||
TagFilters: tagFilters,
|
||||
}
|
||||
|
||||
if len(stats.Tags) == 0 {
|
||||
return nil, errors.New("at least one tag is required")
|
||||
}
|
||||
|
||||
if err := convert.ApplyPos(&entry, pos); err != nil {
|
||||
return &gqlmodel.DashboardEntry{}, err
|
||||
}
|
||||
|
||||
if stats.RangeID != nil {
|
||||
if _, err := util.FindDashboardRange(r.DB, *stats.RangeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entry.RangeID = *stats.RangeID
|
||||
} else if stats.Range != nil {
|
||||
entry.RangeID = model.NoRangeIDDefined
|
||||
if err := time.Validate(stats.Range.From); err != nil {
|
||||
return nil, fmt.Errorf("range from (%s) invalid: %s", stats.Range.From, err)
|
||||
}
|
||||
if err := time.Validate(stats.Range.To); err != nil {
|
||||
return nil, fmt.Errorf("range to (%s) invalid: %s", stats.Range.To, err)
|
||||
}
|
||||
entry.RangeFrom = stats.Range.From
|
||||
entry.RangeTo = stats.Range.To
|
||||
}
|
||||
|
||||
if err := r.DB.Save(&entry).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return convert.ToExternalEntry(entry)
|
||||
}
|
||||
8
dashboard/entry/dbentryresolver.go
Normal file
8
dashboard/entry/dbentryresolver.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package entry
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
// ResolverForEntry resolves dashboard entry things.
|
||||
type ResolverForEntry struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
526
dashboard/entry/entries_test.go
Normal file
526
dashboard/entry/entries_test.go
Normal file
@@ -0,0 +1,526 @@
|
||||
package entry_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/dashboard"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/test"
|
||||
"github.com/traggo/server/test/fake"
|
||||
)
|
||||
|
||||
func TestEntries(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
var bVal bool
|
||||
|
||||
resolver := dashboard.NewResolverForDashboard(db.DB)
|
||||
|
||||
// initial no dashboards
|
||||
user1 := fake.User(1)
|
||||
user2 := fake.User(2)
|
||||
db.User(1)
|
||||
db.User(2)
|
||||
dashboards, err := resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dashboards)
|
||||
|
||||
dashboard, err := resolver.CreateDashboard(user1, "cool dashboard")
|
||||
require.NoError(t, err)
|
||||
expectAdded := &gqlmodel.Dashboard{
|
||||
ID: 1,
|
||||
Name: "cool dashboard",
|
||||
Ranges: []*gqlmodel.NamedDateRange{},
|
||||
Items: []*gqlmodel.DashboardEntry{},
|
||||
}
|
||||
require.Equal(t, expectAdded, dashboard)
|
||||
|
||||
_, err = resolver.AddDashboardEntry(user1, 5, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: "",
|
||||
Tags: []string{"hhol"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
_, err = resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalHourly,
|
||||
Tags: []string{"hhol"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(55),
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now-2d",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "dashboard range does not exist")
|
||||
_, err = resolver.AddDashboardEntry(user2, 1, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: "doubly",
|
||||
Tags: []string{"hhol"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "",
|
||||
To: "",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
_, err = resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"hhol"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-2",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "range to (now-2) invalid: expected unit at the end but got nothing")
|
||||
_, err = resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"hhol"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2",
|
||||
To: "now-2d",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "range from (now-2) invalid: expected unit at the end but got nothing")
|
||||
_, err = resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-2d",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "at least one tag is required")
|
||||
|
||||
entry, err := resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "test", false, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-5d",
|
||||
},
|
||||
}, &gqlmodel.InputResponsiveDashboardEntryPos{Desktop: &gqlmodel.InputDashboardEntryPos{
|
||||
H: 1,
|
||||
W: 2,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
expectedEntry := &gqlmodel.DashboardEntry{
|
||||
ID: 1,
|
||||
Title: "test",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-5d",
|
||||
},
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypeBarChart,
|
||||
}
|
||||
require.Equal(t, expectedEntry, entry)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dashboards[0].Items, []*gqlmodel.DashboardEntry{expectedEntry})
|
||||
xrange, err := resolver.AddDashboardRange(user1, 1, gqlmodel.InputNamedDateRange{
|
||||
Name: "range",
|
||||
Editable: false,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-1d",
|
||||
To: "now",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = resolver.AddDashboardEntry(user1, 1, gqlmodel.EntryTypeBarChart, "other", false, gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
RangeID: p(xrange.ID),
|
||||
}, &gqlmodel.InputResponsiveDashboardEntryPos{Desktop: &gqlmodel.InputDashboardEntryPos{
|
||||
H: 1,
|
||||
W: 2,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
_, err = resolver.RemoveDashboardRange(user1, xrange.ID)
|
||||
require.EqualError(t, err, "range is used in entries: other")
|
||||
|
||||
chart := gqlmodel.EntryTypePieChart
|
||||
_, err = resolver.UpdateDashboardEntry(user2, 1, &chart, nil, nil, nil, nil)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
_, err = resolver.UpdateDashboardEntry(user1, 3, &chart, nil, nil, nil, nil)
|
||||
require.EqualError(t, err, "entry does not exist")
|
||||
_, err = resolver.UpdateDashboardEntry(user1, 1, &chart, s("cool title"), &bVal, &gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2",
|
||||
To: "now-5d",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "range from (now-2) invalid: expected unit at the end but got nothing")
|
||||
_, err = resolver.UpdateDashboardEntry(user1, 1, &chart, s("cool title"), &bVal, &gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-5",
|
||||
},
|
||||
}, nil)
|
||||
require.EqualError(t, err, "range to (now-5) invalid: expected unit at the end but got nothing")
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.DashboardEntry{
|
||||
{
|
||||
ID: 1,
|
||||
Title: "test",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-5d",
|
||||
},
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypeBarChart,
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Title: "other",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(1),
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypeBarChart,
|
||||
},
|
||||
}, dashboards[0].Items)
|
||||
_, err = resolver.UpdateDashboardEntry(user1, 1, &chart, s("cool title"), &bVal, &gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.InputRelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-12d",
|
||||
},
|
||||
}, &gqlmodel.InputResponsiveDashboardEntryPos{Desktop: &gqlmodel.InputDashboardEntryPos{
|
||||
H: 1,
|
||||
W: 2,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.DashboardEntry{
|
||||
{
|
||||
ID: 1,
|
||||
Title: "cool title",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: nil,
|
||||
Range: &gqlmodel.RelativeOrStaticRange{
|
||||
From: "now-2d",
|
||||
To: "now-12d",
|
||||
},
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypePieChart,
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Title: "other",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(1),
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypeBarChart,
|
||||
},
|
||||
}, dashboards[0].Items)
|
||||
|
||||
_, err = resolver.UpdateDashboardEntry(user1, 1, &chart, s("cool title"), &bVal, &gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(5),
|
||||
}, &gqlmodel.InputResponsiveDashboardEntryPos{Desktop: &gqlmodel.InputDashboardEntryPos{
|
||||
H: 1,
|
||||
W: 2,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
}})
|
||||
require.EqualError(t, err, "dashboard range does not exist")
|
||||
|
||||
_, err = resolver.UpdateDashboardEntry(user1, 1, &chart, s("cool title"), &bVal, &gqlmodel.InputStatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(xrange.ID),
|
||||
}, &gqlmodel.InputResponsiveDashboardEntryPos{Desktop: &gqlmodel.InputDashboardEntryPos{
|
||||
H: 1,
|
||||
W: 2,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.DashboardEntry{
|
||||
{
|
||||
ID: 1,
|
||||
Title: "cool title",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"kek"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(xrange.ID),
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypePieChart,
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Title: "other",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(1),
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypeBarChart,
|
||||
},
|
||||
}, dashboards[0].Items)
|
||||
|
||||
_, err = resolver.RemoveDashboardEntry(user2, 1)
|
||||
require.EqualError(t, err, "dashboard does not exist")
|
||||
|
||||
_, err = resolver.RemoveDashboardEntry(user1, 55)
|
||||
require.EqualError(t, err, "entry does not exist")
|
||||
|
||||
_, err = resolver.RemoveDashboardEntry(user1, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboards, err = resolver.Dashboards(user1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*gqlmodel.DashboardEntry{
|
||||
{
|
||||
ID: 2,
|
||||
Title: "other",
|
||||
Total: false,
|
||||
StatsSelection: &gqlmodel.StatsSelection{
|
||||
Interval: gqlmodel.StatsIntervalDaily,
|
||||
Tags: []string{"abc"},
|
||||
ExcludeTags: nil,
|
||||
IncludeTags: nil,
|
||||
RangeID: p(1),
|
||||
},
|
||||
Pos: &gqlmodel.ResponsiveDashboardEntryPos{
|
||||
Desktop: &gqlmodel.DashboardEntryPos{
|
||||
W: 2,
|
||||
H: 3,
|
||||
X: 1,
|
||||
Y: 3,
|
||||
MinW: 2,
|
||||
MinH: 3,
|
||||
},
|
||||
Mobile: &gqlmodel.DashboardEntryPos{
|
||||
W: 1,
|
||||
H: 2,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
MinW: 1,
|
||||
MinH: 2,
|
||||
},
|
||||
},
|
||||
EntryType: gqlmodel.EntryTypeBarChart,
|
||||
},
|
||||
}, dashboards[0].Items)
|
||||
}
|
||||
|
||||
func p(i int) *int {
|
||||
return &i
|
||||
}
|
||||
func s(s string) *string {
|
||||
return &s
|
||||
}
|
||||
34
dashboard/entry/remove.go
Normal file
34
dashboard/entry/remove.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package entry
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// RemoveDashboardEntry removes a dashboard entry.
|
||||
func (r *ResolverForEntry) RemoveDashboardEntry(ctx context.Context, id int) (*gqlmodel.DashboardEntry, error) {
|
||||
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
entry, err := util.FindDashboardEntry(r.DB, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := util.FindDashboard(r.DB, userID, entry.DashboardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remove := r.DB.Delete(&model.DashboardEntry{}, id)
|
||||
if remove.Error != nil {
|
||||
return &gqlmodel.DashboardEntry{}, remove.Error
|
||||
}
|
||||
|
||||
return convert.ToExternalEntry(entry)
|
||||
}
|
||||
33
dashboard/entry/tagcheck.go
Normal file
33
dashboard/entry/tagcheck.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package entry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
func tagsDuplicates(tags []model.DashboardTagFilter) error {
|
||||
existingTags := make(map[model.DashboardTagFilter]struct{})
|
||||
|
||||
for _, tag := range tags {
|
||||
if _, ok := existingTags[tag]; ok {
|
||||
tagType := "exclude"
|
||||
if tag.Include {
|
||||
tagType = "include"
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s tags: tag '%s' is present multiple times", tagType, tag.Key+":"+tag.StringValue)
|
||||
} else {
|
||||
copyTag := tag
|
||||
copyTag.Include = !copyTag.Include
|
||||
|
||||
if _, ok := existingTags[copyTag]; ok {
|
||||
return fmt.Errorf("tag '%s' is present in both exclude tags and include tags", tag.Key+":"+tag.StringValue)
|
||||
}
|
||||
}
|
||||
|
||||
existingTags[tag] = struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
104
dashboard/entry/update.go
Normal file
104
dashboard/entry/update.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package entry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/traggo/server/time"
|
||||
|
||||
"github.com/traggo/server/model"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
)
|
||||
|
||||
// UpdateDashboardEntry updates a dashboard entry.
|
||||
func (r *ResolverForEntry) UpdateDashboardEntry(ctx context.Context, id int, entryType *gqlmodel.EntryType, title *string, total *bool, stats *gqlmodel.InputStatsSelection, pos *gqlmodel.InputResponsiveDashboardEntryPos) (*gqlmodel.DashboardEntry, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
entry, err := util.FindDashboardEntry(r.DB, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := util.FindDashboard(r.DB, userID, entry.DashboardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if title != nil {
|
||||
entry.Title = *title
|
||||
}
|
||||
|
||||
if total != nil {
|
||||
entry.Total = *total
|
||||
}
|
||||
|
||||
tx := r.DB.Begin()
|
||||
if err := tx.Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
panic(r)
|
||||
} else if tx != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
if stats != nil {
|
||||
if stats.RangeID != nil {
|
||||
if _, err := util.FindDashboardRange(tx, *stats.RangeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entry.RangeID = *stats.RangeID
|
||||
} else if stats.Range != nil {
|
||||
entry.RangeID = model.NoRangeIDDefined
|
||||
if err := time.Validate(stats.Range.From); err != nil {
|
||||
return nil, fmt.Errorf("range from (%s) invalid: %s", stats.Range.From, err)
|
||||
}
|
||||
if err := time.Validate(stats.Range.To); err != nil {
|
||||
return nil, fmt.Errorf("range to (%s) invalid: %s", stats.Range.To, err)
|
||||
}
|
||||
entry.RangeFrom = stats.Range.From
|
||||
entry.RangeTo = stats.Range.To
|
||||
}
|
||||
|
||||
tagFilters := convert.TagFiltersToInternal(stats.ExcludeTags, false)
|
||||
tagFilters = append(tagFilters, convert.TagFiltersToInternal(stats.IncludeTags, true)...)
|
||||
|
||||
if err := tagsDuplicates(tagFilters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Where("dashboard_entry_id = ?", id).Delete(new(model.DashboardTagFilter)).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to update tag filters: %s", err)
|
||||
}
|
||||
|
||||
entry.TagFilters = tagFilters
|
||||
entry.Keys = strings.Join(stats.Tags, ",")
|
||||
entry.Interval = convert.InternalInterval(stats.Interval)
|
||||
}
|
||||
|
||||
if entryType != nil {
|
||||
entry.Type = convert.InternalEntryType(*entryType)
|
||||
}
|
||||
|
||||
if err := convert.ApplyPos(&entry, pos); err != nil {
|
||||
return &gqlmodel.DashboardEntry{}, err
|
||||
}
|
||||
|
||||
if err := tx.Save(entry).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx = nil
|
||||
return convert.ToExternalEntry(entry)
|
||||
}
|
||||
31
dashboard/get.go
Normal file
31
dashboard/get.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// Dashboards returns all dashboards.
|
||||
func (r *ResolverForDashboard) Dashboards(ctx context.Context) ([]*gqlmodel.Dashboard, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
dashboards := []model.Dashboard{}
|
||||
|
||||
q := r.DB
|
||||
q = q.Preload("Entries")
|
||||
q = q.Preload("Entries.TagFilters")
|
||||
q = q.Preload("Ranges")
|
||||
|
||||
find := q.Where(&model.Dashboard{UserID: userID}).Find(&dashboards)
|
||||
|
||||
if find.Error != nil {
|
||||
return nil, find.Error
|
||||
}
|
||||
|
||||
return convert.ToExternalDashboards(dashboards)
|
||||
}
|
||||
28
dashboard/remove.go
Normal file
28
dashboard/remove.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// RemoveDashboard removes a dashboard.
|
||||
func (r *ResolverForDashboard) RemoveDashboard(ctx context.Context, id int) (*gqlmodel.Dashboard, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
dashboard, err := util.FindDashboard(r.DB, userID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.DB.Where(&model.Dashboard{UserID: userID, ID: id}).Delete(&model.Dashboard{}).Error; err != nil {
|
||||
return &gqlmodel.Dashboard{}, err
|
||||
}
|
||||
|
||||
return convert.ToExternalDashboard(dashboard)
|
||||
}
|
||||
45
dashboard/update.go
Normal file
45
dashboard/update.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/traggo/server/dashboard/convert"
|
||||
|
||||
"github.com/traggo/server/dashboard/util"
|
||||
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// UpdateDashboard updates a dashboard.
|
||||
func (r *ResolverForDashboard) UpdateDashboard(ctx context.Context, id int, name string) (*gqlmodel.Dashboard, error) {
|
||||
userID := auth.GetUser(ctx).ID
|
||||
|
||||
dashboard, err := util.FindDashboard(r.DB, userID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dashboard.Name = name
|
||||
|
||||
save := r.DB.Save(dashboard)
|
||||
|
||||
if save.Error != nil {
|
||||
return nil, save.Error
|
||||
}
|
||||
|
||||
var entries []model.DashboardEntry
|
||||
if err := r.DB.Where(&model.DashboardEntry{DashboardID: dashboard.ID}).Find(&entries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dashboard.Entries = entries
|
||||
|
||||
var ranges []model.DashboardRange
|
||||
if err := r.DB.Where(&model.DashboardRange{DashboardID: dashboard.ID}).Find(&ranges).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dashboard.Ranges = ranges
|
||||
|
||||
return convert.ToExternalDashboard(dashboard)
|
||||
}
|
||||
41
dashboard/util/dashboard.go
Normal file
41
dashboard/util/dashboard.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// FindDashboard finds a dashboard.
|
||||
func FindDashboard(db *gorm.DB, userID, dashboardID int) (model.Dashboard, error) {
|
||||
dashboard := model.Dashboard{}
|
||||
find := db.Where(&model.Dashboard{UserID: userID, ID: dashboardID}).Find(&dashboard)
|
||||
if find.RecordNotFound() {
|
||||
return dashboard, errors.New("dashboard does not exist")
|
||||
}
|
||||
|
||||
return dashboard, find.Error
|
||||
}
|
||||
|
||||
// FindDashboardRange finds a dashboard range.
|
||||
func FindDashboardRange(db *gorm.DB, rangeID int) (model.DashboardRange, error) {
|
||||
dashboardRange := model.DashboardRange{}
|
||||
find := db.Where(&model.DashboardRange{ID: rangeID}).Find(&dashboardRange)
|
||||
if find.RecordNotFound() {
|
||||
return dashboardRange, errors.New("dashboard range does not exist")
|
||||
}
|
||||
|
||||
return dashboardRange, find.Error
|
||||
}
|
||||
|
||||
// FindDashboardEntry finds a dashboard entry.
|
||||
func FindDashboardEntry(db *gorm.DB, entryID int) (model.DashboardEntry, error) {
|
||||
dashboardEntry := model.DashboardEntry{}
|
||||
find := db.Where(&model.DashboardEntry{ID: entryID}).Find(&dashboardEntry)
|
||||
if find.RecordNotFound() {
|
||||
return dashboardEntry, errors.New("entry does not exist")
|
||||
}
|
||||
|
||||
return dashboardEntry, find.Error
|
||||
}
|
||||
Reference in New Issue
Block a user