From 4a867fb87c994a77dcc25538d2c115e57008839f Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 5 Dec 2023 09:16:27 -0600 Subject: [PATCH 1/8] init commit --- constants/table.go | 3 + database/dashboard.go | 162 ++++++++++++++++++++++++ library/dashboard.go | 252 ++++++++++++++++++++++++++++++++++++++ library/dashboard_repo.go | 133 ++++++++++++++++++++ 4 files changed, 550 insertions(+) create mode 100644 database/dashboard.go create mode 100644 library/dashboard.go create mode 100644 library/dashboard_repo.go diff --git a/constants/table.go b/constants/table.go index 2a73e596..907d7f42 100644 --- a/constants/table.go +++ b/constants/table.go @@ -10,6 +10,9 @@ const ( // TableBuildExecutable defines the table type for the database build_executables table. TableBuildExecutable = "build_executables" + // TableDashboard defines the table type for the database dashboards table. + TableDashboard = "dashboards" + // TableHook defines the table type for the database hooks table. TableHook = "hooks" diff --git a/database/dashboard.go b/database/dashboard.go new file mode 100644 index 00000000..f2d9481f --- /dev/null +++ b/database/dashboard.go @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 + +package database + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "errors" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/library" + "github.com/lib/pq" +) + +var ( + // ErrEmptyDashName defines the error type when a + // User type has an empty Name field provided. + ErrEmptyDashName = errors.New("empty dashboard name provided") + + // ErrExceededAdminLimit defines the error type when a + // User type has Admins field provided that exceeds the database limit. + ErrExceededAdminLimit = errors.New("exceeded admins limit") +) + +// Dashboard is the database representation of a user. +type Dashboard struct { + ID sql.NullInt64 `sql:"id"` + Name sql.NullString `sql:"name"` + CreatedAt sql.NullInt64 `sql:"created_at"` + CreatedBy sql.NullString `sql:"created_by"` + UpdatedAt sql.NullInt64 `sql:"updated_at"` + UpdatedBy sql.NullString `sql:"updated_by"` + Admins pq.StringArray `sql:"admins" gorm:"type:varchar(5000)"` + Repos DashReposJSON +} + +type DashReposJSON []*library.DashboardRepo + +// Value - Implementation of valuer for database/sql for DashReposJSON. +func (r DashReposJSON) Value() (driver.Value, error) { + valueString, err := json.Marshal(r) + return string(valueString), err +} + +// Scan - Implement the database/sql scanner interface for DashReposJSON. +func (r *DashReposJSON) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &r) +} + +// Nullify ensures the valid flag for +// the sql.Null types are properly set. +// +// When a field within the Dashboard type is the zero +// value for the field, the valid flag is set to +// false causing it to be NULL in the database. +func (d *Dashboard) Nullify() *Dashboard { + if d == nil { + return nil + } + + // check if the ID field should be false + if d.ID.Int64 == 0 { + d.ID.Valid = false + } + + // check if the Name field should be false + if len(d.Name.String) == 0 { + d.Name.Valid = false + } + + // check if the CreatedAt field should be false + if d.CreatedAt.Int64 == 0 { + d.CreatedAt.Valid = false + } + + // check if the CreatedBy field should be false + if len(d.CreatedBy.String) == 0 { + d.CreatedBy.Valid = false + } + + // check if the UpdatedAt field should be false + if d.UpdatedAt.Int64 == 0 { + d.UpdatedAt.Valid = false + } + + // check if the UpdatedBy field should be false + if len(d.UpdatedBy.String) == 0 { + d.UpdatedBy.Valid = false + } + + return d +} + +// ToLibrary converts the Dashboard type +// to a library Dashboard type. +func (d *Dashboard) ToLibrary() *library.Dashboard { + dashboard := new(library.Dashboard) + + dashboard.SetID(d.ID.Int64) + dashboard.SetName(d.Name.String) + dashboard.SetAdmins(d.Admins) + dashboard.SetCreatedAt(d.CreatedAt.Int64) + dashboard.SetCreatedBy(d.CreatedBy.String) + dashboard.SetUpdatedAt(d.UpdatedAt.Int64) + dashboard.SetUpdatedBy(d.UpdatedBy.String) + dashboard.SetRepos(d.Repos) + + return dashboard +} + +// Validate verifies the necessary fields for +// the Dashboard type are populated correctly. +func (d *Dashboard) Validate() error { + // verify the Name field is populated + if len(d.Name.String) == 0 { + return ErrEmptyDashName + } + + // calculate total size of favorites + total := 0 + for _, f := range d.Admins { + total += len(f) + } + + // verify the Favorites field is within the database constraints + // len is to factor in number of comma separators included in the database field, + // removing 1 due to the last item not having an appended comma + if (total + len(d.Admins) - 1) > constants.FavoritesMaxSize { + return ErrExceededAdminLimit + } + + // ensure that all Dashboard string fields + // that can be returned as JSON are sanitized + // to avoid unsafe HTML content + d.Name = sql.NullString{String: sanitize(d.Name.String), Valid: d.Name.Valid} + + // ensure that all Favorites are sanitized + // to avoid unsafe HTML content + for i, v := range d.Admins { + d.Admins[i] = sanitize(v) + } + + return nil +} + +// DashboardFromLibrary converts the library Dashboard type +// to a database Dashboard type. +func DashboardFromLibrary(d *library.Dashboard) *Dashboard { + user := &Dashboard{ + ID: sql.NullInt64{Int64: d.GetID(), Valid: true}, + Name: sql.NullString{String: d.GetName(), Valid: true}, + CreatedAt: sql.NullInt64{Int64: d.GetCreatedAt(), Valid: true}, + CreatedBy: sql.NullString{String: d.GetCreatedBy(), Valid: true}, + UpdatedAt: sql.NullInt64{Int64: d.GetUpdatedAt(), Valid: true}, + UpdatedBy: sql.NullString{String: d.GetUpdatedBy(), Valid: true}, + Admins: pq.StringArray(d.GetAdmins()), + Repos: d.GetRepos(), + } + + return user.Nullify() +} diff --git a/library/dashboard.go b/library/dashboard.go new file mode 100644 index 00000000..702241af --- /dev/null +++ b/library/dashboard.go @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: Apache-2.0 + +package library + +import ( + "fmt" +) + +// Dashboard is the library representation of a dashboard. +// +// swagger:model Dashboard +type Dashboard struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + CreatedAt *int64 `json:"created_at,omitempty"` + CreatedBy *string `json:"created_by,omitempty"` + UpdatedAt *int64 `json:"updated_at,omitempty"` + UpdatedBy *string `json:"updated_by,omitempty"` + Admins *[]string `json:"admins,omitempty"` + Repos []*DashboardRepo `json:"repos,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetID() int64 { + // return zero value if Dashboard type or ID field is nil + if d == nil || d.ID == nil { + return 0 + } + + return *d.ID +} + +// GetName returns the Name field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetName() string { + // return zero value if Dashboard type or Name field is nil + if d == nil || d.Name == nil { + return "" + } + + return *d.Name +} + +// GetCreatedAt returns the CreatedAt field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetCreatedAt() int64 { + // return zero value if Dashboard type or CreatedAt field is nil + if d == nil || d.CreatedAt == nil { + return 0 + } + + return *d.CreatedAt +} + +// GetCreatedBy returns the CreatedBy field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetCreatedBy() string { + // return zero value if Dashboard type or CreatedBy field is nil + if d == nil || d.CreatedBy == nil { + return "" + } + + return *d.CreatedBy +} + +// GetUpdatedAt returns the UpdatedAt field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetUpdatedAt() int64 { + // return zero value if Dashboard type or UpdatedAt field is nil + if d == nil || d.UpdatedAt == nil { + return 0 + } + + return *d.UpdatedAt +} + +// GetUpdatedBy returns the UpdatedBy field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetUpdatedBy() string { + // return zero value if Dashboard type or UpdatedBy field is nil + if d == nil || d.UpdatedBy == nil { + return "" + } + + return *d.UpdatedBy +} + +// GetAdmins returns the Admins field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetAdmins() []string { + // return zero value if Dashboard type or Admins field is nil + if d == nil || d.Admins == nil { + return []string{} + } + + return *d.Admins +} + +// GetRepos returns the Repos field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Dashboard) GetRepos() []*DashboardRepo { + // return zero value if Dashboard type or Repos field is nil + if d == nil || d.Repos == nil { + return []*DashboardRepo{} + } + + return d.Repos +} + +// SetID sets the ID field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetID(v int64) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.ID = &v +} + +// SetName sets the Name field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetName(v string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.Name = &v +} + +// SetCreatedAt sets the CreatedAt field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetCreatedAt(v int64) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.CreatedAt = &v +} + +// SetCreatedBy sets the CreatedBy field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetCreatedBy(v string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.CreatedBy = &v +} + +// SetUpdatedAt sets the UpdatedAt field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetUpdatedAt(v int64) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.UpdatedAt = &v +} + +// SetUpdatedBy sets the UpdatedBy field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetUpdatedBy(v string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.UpdatedBy = &v +} + +// SetAdmins sets the Admins field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetAdmins(v []string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.Admins = &v +} + +// SetRepos sets the Repos field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *Dashboard) SetRepos(v []*DashboardRepo) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.Repos = v +} + +// String implements the Stringer interface for the Dashboard type. +func (d *Dashboard) String() string { + return fmt.Sprintf(`{ + Name: %s, + ID: %d, + Admins: %v, + CreatedAt: %d, + CreatedBy: %s, + UpdatedAt: %d, + UpdatedBy: %s, + Repos: %v, +}`, + d.GetName(), + d.GetID(), + d.GetAdmins(), + d.GetCreatedAt(), + d.GetCreatedBy(), + d.GetUpdatedAt(), + d.GetUpdatedBy(), + d.GetRepos(), + ) +} diff --git a/library/dashboard_repo.go b/library/dashboard_repo.go new file mode 100644 index 00000000..88aec3e3 --- /dev/null +++ b/library/dashboard_repo.go @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: Apache-2.0 + +package library + +import ( + "fmt" +) + +type DashboardRepo struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Branches *[]string `json:"branches,omitempty"` + Events *[]string `json:"events,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *DashboardRepo) GetID() int64 { + // return zero value if Dashboard type or ID field is nil + if d == nil { + return 0 + } + + return *d.ID +} + +// GetName returns the Name field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *DashboardRepo) GetName() string { + // return zero value if Dashboard type or ID field is nil + if d == nil { + return "" + } + + return *d.Name +} + +// GetBranches returns the Branches field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *DashboardRepo) GetBranches() []string { + // return zero value if Dashboard type or Branches field is nil + if d == nil || d.Branches == nil { + return []string{} + } + + return *d.Branches +} + +// GetEvents returns the Events field. +// +// When the provided Dashboard type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *DashboardRepo) GetEvents() []string { + // return zero value if Dashboard type or Events field is nil + if d == nil || d.Events == nil { + return []string{} + } + + return *d.Events +} + +// SetID sets the ID field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *DashboardRepo) SetID(v int64) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.ID = &v +} + +// SetName sets the Name field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *DashboardRepo) SetName(v string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.Name = &v +} + +// SetBranches sets the Branches field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *DashboardRepo) SetBranches(v []string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.Branches = &v +} + +// SetEvents sets the Events field. +// +// When the provided Dashboard type is nil, it +// will set nothing and immediately return. +func (d *DashboardRepo) SetEvents(v []string) { + // return if Dashboard type is nil + if d == nil { + return + } + + d.Events = &v +} + +// String implements the Stringer interface for the Dashboard type. +func (d *DashboardRepo) String() string { + return fmt.Sprintf(`{ + Name: %s, + ID: %d, + Branches: %v, + Events: %v, +}`, + d.GetName(), + d.GetID(), + d.GetBranches(), + d.GetEvents(), + ) +} From 4b9563c186db658b885b8a398b1d9714a371046f Mon Sep 17 00:00:00 2001 From: claire1618 <55173466+claire1618@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:05:36 -0600 Subject: [PATCH 2/8] feat: adding dashboard field to user data type (#335) * feat: adding dashboard field to user data type * fix: fixing tests * fix: fixing parsing int --------- Co-authored-by: Claire.Nicholas --- database/user.go | 3 +++ database/user_test.go | 4 ++++ library/user.go | 51 +++++++++++++++++++++++++++++++++++++++++++ library/user_test.go | 2 ++ 4 files changed, 60 insertions(+) diff --git a/database/user.go b/database/user.go index 1e312755..6e3f9e46 100644 --- a/database/user.go +++ b/database/user.go @@ -49,6 +49,7 @@ type User struct { Favorites pq.StringArray `sql:"favorites" gorm:"type:varchar(5000)"` Active sql.NullBool `sql:"active"` Admin sql.NullBool `sql:"admin"` + Dashboards pq.StringArray `sql:"dashboards" gorm:"type:varchar(5000)"` } // Decrypt will manipulate the existing user tokens by @@ -210,6 +211,7 @@ func (u *User) ToLibrary() *library.User { user.SetActive(u.Active.Bool) user.SetAdmin(u.Admin.Bool) user.SetFavorites(u.Favorites) + user.SetDashboards(u.Dashboards) return user } @@ -271,6 +273,7 @@ func UserFromLibrary(u *library.User) *User { Active: sql.NullBool{Bool: u.GetActive(), Valid: true}, Admin: sql.NullBool{Bool: u.GetAdmin(), Valid: true}, Favorites: pq.StringArray(u.GetFavorites()), + Dashboards: pq.StringArray(u.GetDashboards()), } return user.Nullify() diff --git a/database/user_test.go b/database/user_test.go index fa3ae5d5..de91c4ec 100644 --- a/database/user_test.go +++ b/database/user_test.go @@ -114,6 +114,7 @@ func TestDatabase_User_Nullify(t *testing.T) { Hash: sql.NullString{String: "", Valid: false}, Active: sql.NullBool{Bool: false, Valid: false}, Admin: sql.NullBool{Bool: false, Valid: false}, + Dashboards: nil, } // setup tests @@ -157,6 +158,7 @@ func TestDatabase_User_ToLibrary(t *testing.T) { want.SetFavorites([]string{"github/octocat"}) want.SetActive(true) want.SetAdmin(false) + want.SetDashboards(nil) // run test got := testUser().ToLibrary() @@ -244,6 +246,7 @@ func TestDatabase_UserFromLibrary(t *testing.T) { u.SetFavorites([]string{"github/octocat"}) u.SetActive(true) u.SetAdmin(false) + u.SetDashboards(nil) want := testUser() @@ -267,6 +270,7 @@ func testUser() *User { Favorites: []string{"github/octocat"}, Active: sql.NullBool{Bool: true, Valid: true}, Admin: sql.NullBool{Bool: false, Valid: true}, + Dashboards: nil, } } diff --git a/library/user.go b/library/user.go index 3e2deb8e..2c95c4ca 100644 --- a/library/user.go +++ b/library/user.go @@ -4,6 +4,7 @@ package library import ( "fmt" + "strconv" "github.com/go-vela/types/constants" ) @@ -20,6 +21,7 @@ type User struct { Favorites *[]string `json:"favorites,omitempty"` Active *bool `json:"active,omitempty"` Admin *bool `json:"admin,omitempty"` + Dashboards *[]string `json:"dashboards,omitempty"` } // Sanitize creates a duplicate of the User without the token values. @@ -38,6 +40,7 @@ func (u *User) Sanitize() *User { Favorites: u.Favorites, Active: u.Active, Admin: u.Admin, + Dashboards: u.Dashboards, } } @@ -156,6 +159,19 @@ func (u *User) GetFavorites() []string { return *u.Favorites } +// GetDashboards returns the Dashboards field. +// +// When the provided User type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (u *User) GetDashboards() []string { + // return zero value if User type or Favorites field is nil + if u == nil || u.Dashboards == nil { + return []string{} + } + + return *u.Dashboards +} + // SetID sets the ID field. // // When the provided User type is nil, it @@ -260,11 +276,45 @@ func (u *User) SetFavorites(v []string) { u.Favorites = &v } +// SetDashboard sets the Dashboard field. +// +// When the provided User type is nil, it +// will set nothing and immediately return. +func (u *User) SetDashboards(v []string) { + // return if User type is nil + if u == nil { + return + } + + u.Dashboards = &v +} + +// SetDefaultDashboard sets the default Dashboard. +// +// When the provided User type is nil, it +// will set nothing and immediately return. +func (u *User) SetDefaultDashboard(d Dashboard) { + dashboards := *u.Dashboards + dID := d.GetID() + + for a, dashboard := range u.GetDashboards() { + oldID, _ := strconv.ParseInt(dashboard, 10, 64) + if oldID == dID { + hold := dashboards[0] + dashboards[0] = dashboard + dashboards[a] = hold + } + } + + u.Dashboards = &dashboards +} + // String implements the Stringer interface for the User type. func (u *User) String() string { return fmt.Sprintf(`{ Active: %t, Admin: %t, + Dashboards: %s, Favorites: %s, ID: %d, Name: %s, @@ -272,6 +322,7 @@ func (u *User) String() string { }`, u.GetActive(), u.GetAdmin(), + u.GetDashboards(), u.GetFavorites(), u.GetID(), u.GetName(), diff --git a/library/user_test.go b/library/user_test.go index 76657373..8d0f11d9 100644 --- a/library/user_test.go +++ b/library/user_test.go @@ -167,6 +167,7 @@ func TestLibrary_User_String(t *testing.T) { want := fmt.Sprintf(`{ Active: %t, Admin: %t, + Dashboards: %s, Favorites: %s, ID: %d, Name: %s, @@ -174,6 +175,7 @@ func TestLibrary_User_String(t *testing.T) { }`, u.GetActive(), u.GetAdmin(), + u.GetDashboards(), u.GetFavorites(), u.GetID(), u.GetName(), From f1853a2d7dcb968c0d72e586b9bb38029554f130 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 5 Dec 2023 21:50:09 -0600 Subject: [PATCH 3/8] uuid for dashboard id --- database/dashboard.go | 11 +++-------- go.mod | 1 + go.sum | 2 ++ library/dashboard.go | 8 ++++---- library/user.go | 21 --------------------- 5 files changed, 10 insertions(+), 33 deletions(-) diff --git a/database/dashboard.go b/database/dashboard.go index f2d9481f..3754651c 100644 --- a/database/dashboard.go +++ b/database/dashboard.go @@ -10,6 +10,7 @@ import ( "github.com/go-vela/types/constants" "github.com/go-vela/types/library" + "github.com/google/uuid" "github.com/lib/pq" ) @@ -25,7 +26,7 @@ var ( // Dashboard is the database representation of a user. type Dashboard struct { - ID sql.NullInt64 `sql:"id"` + ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"` Name sql.NullString `sql:"name"` CreatedAt sql.NullInt64 `sql:"created_at"` CreatedBy sql.NullString `sql:"created_by"` @@ -59,11 +60,6 @@ func (d *Dashboard) Nullify() *Dashboard { return nil } - // check if the ID field should be false - if d.ID.Int64 == 0 { - d.ID.Valid = false - } - // check if the Name field should be false if len(d.Name.String) == 0 { d.Name.Valid = false @@ -97,7 +93,7 @@ func (d *Dashboard) Nullify() *Dashboard { func (d *Dashboard) ToLibrary() *library.Dashboard { dashboard := new(library.Dashboard) - dashboard.SetID(d.ID.Int64) + dashboard.SetID(d.ID.String()) dashboard.SetName(d.Name.String) dashboard.SetAdmins(d.Admins) dashboard.SetCreatedAt(d.CreatedAt.Int64) @@ -148,7 +144,6 @@ func (d *Dashboard) Validate() error { // to a database Dashboard type. func DashboardFromLibrary(d *library.Dashboard) *Dashboard { user := &Dashboard{ - ID: sql.NullInt64{Int64: d.GetID(), Valid: true}, Name: sql.NullString{String: d.GetName(), Valid: true}, CreatedAt: sql.NullInt64{Int64: d.GetCreatedAt(), Valid: true}, CreatedBy: sql.NullString{String: d.GetCreatedBy(), Valid: true}, diff --git a/go.mod b/go.mod index 764f3775..40af65c5 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( require ( github.com/aymerick/douceur v0.2.0 // indirect + github.com/google/uuid v1.4.0 github.com/gorilla/css v1.0.0 // indirect github.com/kr/pretty v0.2.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum index 30a8143f..b5ea905f 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= diff --git a/library/dashboard.go b/library/dashboard.go index 702241af..3136d100 100644 --- a/library/dashboard.go +++ b/library/dashboard.go @@ -10,7 +10,7 @@ import ( // // swagger:model Dashboard type Dashboard struct { - ID *int64 `json:"id,omitempty"` + ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` CreatedAt *int64 `json:"created_at,omitempty"` CreatedBy *string `json:"created_by,omitempty"` @@ -24,10 +24,10 @@ type Dashboard struct { // // When the provided Dashboard type is nil, or the field within // the type is nil, it returns the zero value for the field. -func (d *Dashboard) GetID() int64 { +func (d *Dashboard) GetID() string { // return zero value if Dashboard type or ID field is nil if d == nil || d.ID == nil { - return 0 + return "" } return *d.ID @@ -128,7 +128,7 @@ func (d *Dashboard) GetRepos() []*DashboardRepo { // // When the provided Dashboard type is nil, it // will set nothing and immediately return. -func (d *Dashboard) SetID(v int64) { +func (d *Dashboard) SetID(v string) { // return if Dashboard type is nil if d == nil { return diff --git a/library/user.go b/library/user.go index 2c95c4ca..83de2d28 100644 --- a/library/user.go +++ b/library/user.go @@ -4,7 +4,6 @@ package library import ( "fmt" - "strconv" "github.com/go-vela/types/constants" ) @@ -289,26 +288,6 @@ func (u *User) SetDashboards(v []string) { u.Dashboards = &v } -// SetDefaultDashboard sets the default Dashboard. -// -// When the provided User type is nil, it -// will set nothing and immediately return. -func (u *User) SetDefaultDashboard(d Dashboard) { - dashboards := *u.Dashboards - dID := d.GetID() - - for a, dashboard := range u.GetDashboards() { - oldID, _ := strconv.ParseInt(dashboard, 10, 64) - if oldID == dID { - hold := dashboards[0] - dashboards[0] = dashboard - dashboards[a] = hold - } - } - - u.Dashboards = &dashboards -} - // String implements the Stringer interface for the User type. func (u *User) String() string { return fmt.Sprintf(`{ From 22641990d9088ddc31a33f0f034a1fc749278993 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 6 Dec 2023 09:24:42 -0600 Subject: [PATCH 4/8] uuid handling --- database/dashboard.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/database/dashboard.go b/database/dashboard.go index 3754651c..34bcba5d 100644 --- a/database/dashboard.go +++ b/database/dashboard.go @@ -143,7 +143,22 @@ func (d *Dashboard) Validate() error { // DashboardFromLibrary converts the library Dashboard type // to a database Dashboard type. func DashboardFromLibrary(d *library.Dashboard) *Dashboard { + var ( + id uuid.UUID + err error + ) + + if d.GetID() == "" { + id = uuid.New() + } else { + id, err = uuid.Parse(d.GetID()) + if err != nil { + return nil + } + } + user := &Dashboard{ + ID: id, Name: sql.NullString{String: d.GetName(), Valid: true}, CreatedAt: sql.NullInt64{Int64: d.GetCreatedAt(), Valid: true}, CreatedBy: sql.NullString{String: d.GetCreatedBy(), Valid: true}, From 43d85113aff4e210f990408cc5700b91591410bf Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 6 Dec 2023 11:58:41 -0600 Subject: [PATCH 5/8] add testing --- constants/limit.go | 3 + database/dashboard.go | 2 +- database/dashboard_test.go | 235 +++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 3 +- library/dashboard.go | 2 +- library/dashboard_repo.go | 4 +- library/dashboard_repo_test.go | 126 ++++++++++++++++++ library/dashboard_test.go | 174 ++++++++++++++++++++++++ 9 files changed, 546 insertions(+), 5 deletions(-) create mode 100644 database/dashboard_test.go create mode 100644 library/dashboard_repo_test.go create mode 100644 library/dashboard_test.go diff --git a/constants/limit.go b/constants/limit.go index c905ac04..cfa4d5a2 100644 --- a/constants/limit.go +++ b/constants/limit.go @@ -25,6 +25,9 @@ const ( // FavoritesMaxSize defines the maximum size in characters for user favorites. FavoritesMaxSize = 5000 + // DashboardAdminMaxSize defines the maximum size in characters for dashboard admins. + DashboardAdminMaxSize = 5000 + // RunningBuildIDsMaxSize defines the maximum size in characters for worker RunningBuildIDs. RunningBuildIDsMaxSize = 500 diff --git a/database/dashboard.go b/database/dashboard.go index 34bcba5d..2716f21d 100644 --- a/database/dashboard.go +++ b/database/dashboard.go @@ -122,7 +122,7 @@ func (d *Dashboard) Validate() error { // verify the Favorites field is within the database constraints // len is to factor in number of comma separators included in the database field, // removing 1 due to the last item not having an appended comma - if (total + len(d.Admins) - 1) > constants.FavoritesMaxSize { + if (total + len(d.Admins) - 1) > constants.DashboardAdminMaxSize { return ErrExceededAdminLimit } diff --git a/database/dashboard_test.go b/database/dashboard_test.go new file mode 100644 index 00000000..ac608530 --- /dev/null +++ b/database/dashboard_test.go @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: Apache-2.0 + +package database + +import ( + "database/sql" + "reflect" + "strconv" + "testing" + "time" + + "github.com/go-vela/types/library" + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" +) + +func TestDatabase_Dashboard_Nullify(t *testing.T) { + // setup types + var h *Dashboard + + want := &Dashboard{ + Name: sql.NullString{String: "", Valid: false}, + CreatedAt: sql.NullInt64{Int64: 0, Valid: false}, + CreatedBy: sql.NullString{String: "", Valid: false}, + UpdatedAt: sql.NullInt64{Int64: 0, Valid: false}, + UpdatedBy: sql.NullString{String: "", Valid: false}, + } + + // setup tests + tests := []struct { + dashboard *Dashboard + want *Dashboard + }{ + { + dashboard: testDashboard(), + want: testDashboard(), + }, + { + dashboard: h, + want: nil, + }, + { + dashboard: new(Dashboard), + want: want, + }, + } + + // run tests + for _, test := range tests { + got := test.dashboard.Nullify() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("Nullify is %v, want %v", got, test.want) + } + } +} + +func TestDatabase_Dashboard_ToLibrary(t *testing.T) { + // setup types + want := new(library.Dashboard) + want.SetID("c8da1302-07d6-11ea-882f-4893bca275b8") + want.SetName("vela") + want.SetCreatedAt(1) + want.SetCreatedBy("octocat") + want.SetUpdatedAt(2) + want.SetUpdatedBy("octokitty") + want.SetAdmins([]string{"octocat", "octokitty"}) + want.SetRepos(testDashReposJSON()) + + uuid, _ := uuid.Parse("c8da1302-07d6-11ea-882f-4893bca275b8") + h := &Dashboard{ + ID: uuid, + Name: sql.NullString{String: "vela", Valid: true}, + CreatedAt: sql.NullInt64{Int64: 1, Valid: true}, + CreatedBy: sql.NullString{String: "octocat", Valid: true}, + UpdatedAt: sql.NullInt64{Int64: 2, Valid: true}, + UpdatedBy: sql.NullString{String: "octokitty", Valid: true}, + Admins: []string{"octocat", "octokitty"}, + Repos: testDashReposJSON(), + } + + // run test + got := h.ToLibrary() + + if !reflect.DeepEqual(got, want) { + t.Errorf("ToLibrary is %v, want %v", got, want) + } +} + +func TestDatabase_Dashboard_Validate(t *testing.T) { + uuid, _ := uuid.Parse("c8da1302-07d6-11ea-882f-4893bca275b8") + + // setup tests + tests := []struct { + failure bool + dashboard *Dashboard + }{ + { + failure: false, + dashboard: testDashboard(), + }, + { // no name set for dashboard + failure: true, + dashboard: &Dashboard{ + ID: uuid, + }, + }, + { // invalid admin set for dashboard + failure: true, + dashboard: &Dashboard{ + ID: uuid, + Name: sql.NullString{String: "vela", Valid: true}, + Admins: exceedAdmins(), + }, + }, + } + + // run tests + for _, test := range tests { + err := test.dashboard.Validate() + + if test.failure { + if err == nil { + t.Errorf("Validate should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("Validate returned err: %v", err) + } + } +} + +func TestDatabase_DashboardFromLibrary(t *testing.T) { + uuid, err := uuid.Parse("c8da1302-07d6-11ea-882f-4893bca275b8") + if err != nil { + t.Errorf("error parsing uuid: %v", err) + } + + // setup types + want := &Dashboard{ + ID: uuid, + Name: sql.NullString{String: "vela", Valid: true}, + CreatedAt: sql.NullInt64{Int64: 1, Valid: true}, + CreatedBy: sql.NullString{String: "octocat", Valid: true}, + UpdatedAt: sql.NullInt64{Int64: 2, Valid: true}, + UpdatedBy: sql.NullString{String: "octokitty", Valid: true}, + Admins: []string{"octocat", "octokitty"}, + Repos: testDashReposJSON(), + } + + d := new(library.Dashboard) + d.SetID("c8da1302-07d6-11ea-882f-4893bca275b8") + d.SetName("vela") + d.SetCreatedAt(1) + d.SetCreatedBy("octocat") + d.SetUpdatedAt(2) + d.SetUpdatedBy("octokitty") + d.SetAdmins([]string{"octocat", "octokitty"}) + d.SetRepos(testDashReposJSON()) + + // run test + got := DashboardFromLibrary(d) + + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("DashboardFromLibrary() mismatch (-want +got):\n%s", diff) + } + + d.SetID("") + + //nolint:staticcheck // linter is lying + got = DashboardFromLibrary(d) + + if len(got.ID) != 16 { + t.Errorf("Length is %d", len(got.ID)) + } + + d.SetID("123-abc") + + got = DashboardFromLibrary(d) + + if got != nil { + t.Errorf("DashboardFromLibrary should have returned nil") + } +} + +// testDashboard is a test helper function to create a Dashboard +// type with all fields set to a fake value. +func testDashboard() *Dashboard { + uuid, _ := uuid.Parse("c8da1302-07d6-11ea-882f-4893bca275b8") + dRepos := testDashReposJSON() + + return &Dashboard{ + ID: uuid, + Name: sql.NullString{String: "vela", Valid: true}, + CreatedAt: sql.NullInt64{Int64: time.Now().UTC().Unix(), Valid: true}, + CreatedBy: sql.NullString{String: "octocat", Valid: true}, + UpdatedAt: sql.NullInt64{Int64: time.Now().UTC().Unix(), Valid: true}, + UpdatedBy: sql.NullString{String: "octokitty", Valid: true}, + Admins: []string{"octocat", "octokitty"}, + Repos: dRepos, + } +} + +// testDashReposJSON is a test helper function to create a DashReposJSON +// type with all fields set to a fake value. +func testDashReposJSON() DashReposJSON { + d := new(library.DashboardRepo) + + d.SetName("go-vela/server") + d.SetID(1) + d.SetBranches([]string{"main"}) + d.SetEvents([]string{"push", "tag"}) + + return DashReposJSON{d} +} + +// exceedAdmins returns a list of valid admins that exceed the maximum size. +func exceedAdmins() []string { + // initialize empty favorites + admins := []string{} + + // add enough favorites to exceed the character limit + for i := 0; i < 500; i++ { + // construct favorite + // use i to adhere to unique favorites + admin := "github/octocat-" + strconv.Itoa(i) + + admins = append(admins, admin) + } + + return admins +} diff --git a/go.mod b/go.mod index 40af65c5..24633aad 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ require ( github.com/microcosm-cc/bluemonday v1.0.26 ) +require github.com/google/go-cmp v0.5.9 + require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/google/uuid v1.4.0 diff --git a/go.sum b/go.sum index b5ea905f..1a50b664 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,9 @@ github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= diff --git a/library/dashboard.go b/library/dashboard.go index 3136d100..b67536d5 100644 --- a/library/dashboard.go +++ b/library/dashboard.go @@ -232,7 +232,7 @@ func (d *Dashboard) SetRepos(v []*DashboardRepo) { func (d *Dashboard) String() string { return fmt.Sprintf(`{ Name: %s, - ID: %d, + ID: %s, Admins: %v, CreatedAt: %d, CreatedBy: %s, diff --git a/library/dashboard_repo.go b/library/dashboard_repo.go index 88aec3e3..fa27b17e 100644 --- a/library/dashboard_repo.go +++ b/library/dashboard_repo.go @@ -19,7 +19,7 @@ type DashboardRepo struct { // the type is nil, it returns the zero value for the field. func (d *DashboardRepo) GetID() int64 { // return zero value if Dashboard type or ID field is nil - if d == nil { + if d == nil || d.ID == nil { return 0 } @@ -32,7 +32,7 @@ func (d *DashboardRepo) GetID() int64 { // the type is nil, it returns the zero value for the field. func (d *DashboardRepo) GetName() string { // return zero value if Dashboard type or ID field is nil - if d == nil { + if d == nil || d.Name == nil { return "" } diff --git a/library/dashboard_repo_test.go b/library/dashboard_repo_test.go new file mode 100644 index 00000000..29ab6246 --- /dev/null +++ b/library/dashboard_repo_test.go @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 + +package library + +import ( + "fmt" + "reflect" + "testing" +) + +func TestLibrary_DashboardRepo_Getters(t *testing.T) { + // setup tests + tests := []struct { + dashboardRepo *DashboardRepo + want *DashboardRepo + }{ + { + dashboardRepo: testDashboardRepo(), + want: testDashboardRepo(), + }, + { + dashboardRepo: new(DashboardRepo), + want: new(DashboardRepo), + }, + } + + // run tests + for _, test := range tests { + if test.dashboardRepo.GetID() != test.want.GetID() { + t.Errorf("GetID is %v, want %v", test.dashboardRepo.GetID(), test.want.GetID()) + } + + if test.dashboardRepo.GetName() != test.want.GetName() { + t.Errorf("GetName is %v, want %v", test.dashboardRepo.GetName(), test.want.GetName()) + } + + if !reflect.DeepEqual(test.dashboardRepo.GetBranches(), test.want.GetBranches()) { + t.Errorf("GetBranches is %v, want %v", test.dashboardRepo.GetBranches(), test.want.GetBranches()) + } + + if !reflect.DeepEqual(test.dashboardRepo.GetEvents(), test.want.GetEvents()) { + t.Errorf("GetEvents is %v, want %v", test.dashboardRepo.GetEvents(), test.want.GetEvents()) + } + } +} + +func TestLibrary_DashboardRepo_Setters(t *testing.T) { + // setup types + var d *DashboardRepo + + // setup tests + tests := []struct { + dashboardRepo *DashboardRepo + want *DashboardRepo + }{ + { + dashboardRepo: testDashboardRepo(), + want: testDashboardRepo(), + }, + { + dashboardRepo: d, + want: new(DashboardRepo), + }, + } + + // run tests + for _, test := range tests { + test.dashboardRepo.SetID(test.want.GetID()) + test.dashboardRepo.SetName(test.want.GetName()) + test.dashboardRepo.SetBranches(test.want.GetBranches()) + test.dashboardRepo.SetEvents(test.want.GetEvents()) + + if test.dashboardRepo.GetID() != test.want.GetID() { + t.Errorf("SetID is %v, want %v", test.dashboardRepo.GetID(), test.want.GetID()) + } + + if test.dashboardRepo.GetName() != test.want.GetName() { + t.Errorf("SetName is %v, want %v", test.dashboardRepo.GetName(), test.want.GetName()) + } + + if !reflect.DeepEqual(test.dashboardRepo.GetBranches(), test.want.GetBranches()) { + t.Errorf("SetBranches is %v, want %v", test.dashboardRepo.GetBranches(), test.want.GetBranches()) + } + + if !reflect.DeepEqual(test.dashboardRepo.GetEvents(), test.want.GetEvents()) { + t.Errorf("SetEvents is %v, want %v", test.dashboardRepo.GetEvents(), test.want.GetEvents()) + } + } +} + +func TestLibrary_DashboardRepo_String(t *testing.T) { + // setup types + d := testDashboardRepo() + + want := fmt.Sprintf(`{ + Name: %s, + ID: %d, + Branches: %v, + Events: %v, +}`, + d.GetName(), + d.GetID(), + d.GetBranches(), + d.GetEvents(), + ) + + // run test + got := d.String() + + if !reflect.DeepEqual(got, want) { + t.Errorf("String is %v, want %v", got, want) + } +} + +// testDashboardRepo is a test helper function to create a DashboardRepo +// type with all fields set to a fake value. +func testDashboardRepo() *DashboardRepo { + d := new(DashboardRepo) + + d.SetName("go-vela/server") + d.SetID(1) + d.SetBranches([]string{"main"}) + d.SetEvents([]string{"push", "tag"}) + + return d +} diff --git a/library/dashboard_test.go b/library/dashboard_test.go new file mode 100644 index 00000000..8746ab01 --- /dev/null +++ b/library/dashboard_test.go @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: Apache-2.0 + +package library + +import ( + "fmt" + "reflect" + "testing" +) + +func TestLibrary_Dashboard_Getters(t *testing.T) { + // setup tests + tests := []struct { + dashboard *Dashboard + want *Dashboard + }{ + { + dashboard: testDashboard(), + want: testDashboard(), + }, + { + dashboard: new(Dashboard), + want: new(Dashboard), + }, + } + + // run tests + for _, test := range tests { + if test.dashboard.GetID() != test.want.GetID() { + t.Errorf("GetID is %v, want %v", test.dashboard.GetID(), test.want.GetID()) + } + + if test.dashboard.GetName() != test.want.GetName() { + t.Errorf("GetName is %v, want %v", test.dashboard.GetName(), test.want.GetName()) + } + + if !reflect.DeepEqual(test.dashboard.GetAdmins(), test.want.GetAdmins()) { + t.Errorf("GetAdmins is %v, want %v", test.dashboard.GetAdmins(), test.want.GetAdmins()) + } + + if test.dashboard.GetCreatedAt() != test.want.GetCreatedAt() { + t.Errorf("GetCreatedAt is %v, want %v", test.dashboard.GetCreatedAt(), test.want.GetCreatedAt()) + } + + if test.dashboard.GetCreatedBy() != test.want.GetCreatedBy() { + t.Errorf("GetCreatedBy is %v, want %v", test.dashboard.GetCreatedBy(), test.want.GetCreatedBy()) + } + + if test.dashboard.GetUpdatedAt() != test.want.GetUpdatedAt() { + t.Errorf("GetUpdatedAt is %v, want %v", test.dashboard.GetUpdatedAt(), test.want.GetUpdatedAt()) + } + + if test.dashboard.GetUpdatedBy() != test.want.GetUpdatedBy() { + t.Errorf("GetUpdatedBy is %v, want %v", test.dashboard.GetUpdatedBy(), test.want.GetUpdatedBy()) + } + + if !reflect.DeepEqual(test.dashboard.GetRepos(), test.want.GetRepos()) { + t.Errorf("GetRepos is %v, want %v", test.dashboard.GetRepos(), test.want.GetRepos()) + } + } +} + +func TestLibrary_Dashboard_Setters(t *testing.T) { + // setup types + var d *Dashboard + + // setup tests + tests := []struct { + dashboard *Dashboard + want *Dashboard + }{ + { + dashboard: testDashboard(), + want: testDashboard(), + }, + { + dashboard: d, + want: new(Dashboard), + }, + } + + // run tests + for _, test := range tests { + test.dashboard.SetID(test.want.GetID()) + test.dashboard.SetName(test.want.GetName()) + test.dashboard.SetAdmins(test.want.GetAdmins()) + test.dashboard.SetCreatedAt(test.want.GetCreatedAt()) + test.dashboard.SetCreatedBy(test.want.GetCreatedBy()) + test.dashboard.SetUpdatedAt(test.want.GetUpdatedAt()) + test.dashboard.SetUpdatedBy(test.want.GetUpdatedBy()) + test.dashboard.SetRepos(test.want.GetRepos()) + + if test.dashboard.GetID() != test.want.GetID() { + t.Errorf("SetID is %v, want %v", test.dashboard.GetID(), test.want.GetID()) + } + + if test.dashboard.GetName() != test.want.GetName() { + t.Errorf("SetName is %v, want %v", test.dashboard.GetName(), test.want.GetName()) + } + + if !reflect.DeepEqual(test.dashboard.GetAdmins(), test.want.GetAdmins()) { + t.Errorf("SetAdmins is %v, want %v", test.dashboard.GetAdmins(), test.want.GetAdmins()) + } + + if test.dashboard.GetCreatedAt() != test.want.GetCreatedAt() { + t.Errorf("SetCreatedAt is %v, want %v", test.dashboard.GetCreatedAt(), test.want.GetCreatedAt()) + } + + if test.dashboard.GetCreatedBy() != test.want.GetCreatedBy() { + t.Errorf("SetCreatedBy is %v, want %v", test.dashboard.GetCreatedBy(), test.want.GetCreatedBy()) + } + + if test.dashboard.GetUpdatedAt() != test.want.GetUpdatedAt() { + t.Errorf("SetUpdatedAt is %v, want %v", test.dashboard.GetUpdatedAt(), test.want.GetUpdatedAt()) + } + + if test.dashboard.GetUpdatedBy() != test.want.GetUpdatedBy() { + t.Errorf("SetUpdatedBy is %v, want %v", test.dashboard.GetUpdatedBy(), test.want.GetUpdatedBy()) + } + + if !reflect.DeepEqual(test.dashboard.GetRepos(), test.want.GetRepos()) { + t.Errorf("SetRepos is %v, want %v", test.dashboard.GetRepos(), test.want.GetRepos()) + } + } +} + +func TestLibrary_Dashboard_String(t *testing.T) { + // setup types + d := testDashboard() + + want := fmt.Sprintf(`{ + Name: %s, + ID: %s, + Admins: %v, + CreatedAt: %d, + CreatedBy: %s, + UpdatedAt: %d, + UpdatedBy: %s, + Repos: %v, +}`, + d.GetName(), + d.GetID(), + d.GetAdmins(), + d.GetCreatedAt(), + d.GetCreatedBy(), + d.GetUpdatedAt(), + d.GetUpdatedBy(), + d.GetRepos(), + ) + + // run test + got := d.String() + + if !reflect.DeepEqual(got, want) { + t.Errorf("String is %v, want %v", got, want) + } +} + +// testDashboard is a test helper function to create a Dashboard +// type with all fields set to a fake value. +func testDashboard() *Dashboard { + d := new(Dashboard) + + d.SetID("123-abc") + d.SetName("vela") + d.SetAdmins([]string{"octocat", "octokitty"}) + d.SetCreatedAt(1) + d.SetCreatedBy("octocat") + d.SetUpdatedAt(2) + d.SetUpdatedBy("octokitty") + d.SetRepos([]*DashboardRepo{testDashboardRepo()}) + + return d +} From 9ab070dbcc668e9f3eeb502c52e6a329591c59ee Mon Sep 17 00:00:00 2001 From: ecrupper Date: Thu, 7 Dec 2023 16:00:56 -0600 Subject: [PATCH 6/8] use type switch for sqlite v postgres --- database/dashboard.go | 10 +++++++++- database/dashboard_test.go | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/database/dashboard.go b/database/dashboard.go index 2716f21d..221545ab 100644 --- a/database/dashboard.go +++ b/database/dashboard.go @@ -7,6 +7,7 @@ import ( "database/sql/driver" "encoding/json" "errors" + "fmt" "github.com/go-vela/types/constants" "github.com/go-vela/types/library" @@ -46,7 +47,14 @@ func (r DashReposJSON) Value() (driver.Value, error) { // Scan - Implement the database/sql scanner interface for DashReposJSON. func (r *DashReposJSON) Scan(value interface{}) error { - return json.Unmarshal(value.([]byte), &r) + switch v := value.(type) { + case []byte: + return json.Unmarshal(v, &r) + case string: + return json.Unmarshal([]byte(v), &r) + default: + return fmt.Errorf("wrong type for repos: %T", v) + } } // Nullify ensures the valid flag for diff --git a/database/dashboard_test.go b/database/dashboard_test.go index ac608530..2f1601c7 100644 --- a/database/dashboard_test.go +++ b/database/dashboard_test.go @@ -168,6 +168,7 @@ func TestDatabase_DashboardFromLibrary(t *testing.T) { t.Errorf("DashboardFromLibrary() mismatch (-want +got):\n%s", diff) } + // test empty uuid results in generated uuid d.SetID("") //nolint:staticcheck // linter is lying @@ -177,6 +178,7 @@ func TestDatabase_DashboardFromLibrary(t *testing.T) { t.Errorf("Length is %d", len(got.ID)) } + // test poorly formed uuid results in nil dashboard d.SetID("123-abc") got = DashboardFromLibrary(d) From 9810d3995fca9ac23065a12d7a9e9aefab5836e0 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 19 Dec 2023 15:54:08 -0600 Subject: [PATCH 7/8] adjusting admins to be user ids --- library/dashboard_repo.go | 3 +++ library/dashboard_test.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/library/dashboard_repo.go b/library/dashboard_repo.go index fa27b17e..275b290e 100644 --- a/library/dashboard_repo.go +++ b/library/dashboard_repo.go @@ -6,6 +6,9 @@ import ( "fmt" ) +// DashboardRepo is the library representation of a repo belonging to a Dashboard. +// +// swagger:model DashboardRepo type DashboardRepo struct { ID *int64 `json:"id,omitempty"` Name *string `json:"name,omitempty"` diff --git a/library/dashboard_test.go b/library/dashboard_test.go index 8746ab01..ac6fbbd4 100644 --- a/library/dashboard_test.go +++ b/library/dashboard_test.go @@ -163,7 +163,7 @@ func testDashboard() *Dashboard { d.SetID("123-abc") d.SetName("vela") - d.SetAdmins([]string{"octocat", "octokitty"}) + d.SetAdmins([]string{"1", "42"}) d.SetCreatedAt(1) d.SetCreatedBy("octocat") d.SetUpdatedAt(2) From 0f8b5565936c5886ed4bba04db9d7fd61167ef87 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 20 Dec 2023 08:57:55 -0600 Subject: [PATCH 8/8] fix weird go mod splitting --- go.mod | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 24633aad..fe996eb7 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,11 @@ require ( github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/drone/envsubst v1.0.3 github.com/ghodss/yaml v1.0.0 + github.com/google/go-cmp v0.5.9 github.com/lib/pq v1.10.9 github.com/microcosm-cc/bluemonday v1.0.26 ) -require github.com/google/go-cmp v0.5.9 - require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/google/uuid v1.4.0