mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
LibraryPanels: removes feature toggle (#33839)
* WIP: intial structure * Refactor: adds create library element endpoint * Feature: adds delete library element * wip * Refactor: adds get api * Refactor: adds get all api * Refactor: adds patch api * Refactor: changes to library_element_connection * Refactor: add get connections api * wip: in the middle of refactor * wip * Refactor: consolidating both api:s * Refactor: points front end to library elements api * Tests: Fixes broken test * LibraryPanels: removes feature toggle * Fix: fixes delete library elements in folder and adds tests * Tests: fixes snapshot * Refactor: adds service interfaces so they can be easily mocked * Refactor: changes order of tabs in manage folder * Refactor: fixes so link does not cover whole card * Refactor: fixes index string name * Update pkg/services/libraryelements/libraryelements.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/libraryelements/libraryelements_permissions_test.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/libraryelements/database.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Chore: changes after PR comments * Update libraryelements.go * Update libraryelements.go * Chore: updates after PR comments * Chore: trying to fix build error * Refactor: fixed stupid mistake * Update libraryelements.go * Chore: tries to fix build errors * Refactor: trying to fix MySQL key length * Update libraryelements.go * Update pkg/services/libraryelements/libraryelements.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/librarypanels/librarypanels.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Refactor: changes after PR comments * Refactor: changes after PR comments * Tests: fixes tests * Refactor: renames connections to connectedDashboards Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
@@ -13,10 +13,6 @@ import (
|
||||
)
|
||||
|
||||
func (l *LibraryElementService) registerAPIEndpoints() {
|
||||
if !l.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
l.RouteRegister.Group("/api/library-elements", func(entities routing.RouteRegister) {
|
||||
entities.Post("/", middleware.ReqSignedIn, binding.Bind(CreateLibraryElementCommand{}), routing.Wrap(l.createHandler))
|
||||
entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(l.deleteHandler))
|
||||
|
||||
@@ -22,7 +22,7 @@ SELECT DISTINCT
|
||||
, u1.email AS created_by_email
|
||||
, u2.login AS updated_by_name
|
||||
, u2.email AS updated_by_email
|
||||
, (SELECT COUNT(connection_id) FROM ` + connectionTableName + ` WHERE library_element_id = le.id AND connection_kind=1) AS connections`
|
||||
, (SELECT COUNT(connection_id) FROM ` + connectionTableName + ` WHERE element_id = le.id AND kind=1) AS connected_dashboards`
|
||||
fromLibraryElementDTOWithMeta = `
|
||||
FROM library_element AS le
|
||||
LEFT JOIN user AS u1 ON le.created_by = u1.id
|
||||
@@ -134,9 +134,9 @@ func (l *LibraryElementService) createLibraryElement(c *models.ReqContext, cmd C
|
||||
Model: element.Model,
|
||||
Version: element.Version,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
Connections: 0,
|
||||
Created: element.Created,
|
||||
Updated: element.Updated,
|
||||
ConnectedDashboards: 0,
|
||||
Created: element.Created,
|
||||
Updated: element.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: element.CreatedBy,
|
||||
Name: c.SignedInUser.Login,
|
||||
@@ -166,7 +166,7 @@ func (l *LibraryElementService) deleteLibraryElement(c *models.ReqContext, uid s
|
||||
var connectionIDs []struct {
|
||||
ConnectionID int64 `xorm:"connection_id"`
|
||||
}
|
||||
sql := "SELECT connection_id FROM library_element_connection WHERE library_element_id=?"
|
||||
sql := "SELECT connection_id FROM library_element_connection WHERE element_id=?"
|
||||
if err := session.SQL(sql, element.ID).Find(&connectionIDs); err != nil {
|
||||
return err
|
||||
} else if len(connectionIDs) > 0 {
|
||||
@@ -239,11 +239,11 @@ func (l *LibraryElementService) getLibraryElement(c *models.ReqContext, uid stri
|
||||
Model: libraryElement.Model,
|
||||
Version: libraryElement.Version,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: libraryElement.FolderName,
|
||||
FolderUID: libraryElement.FolderUID,
|
||||
Connections: libraryElement.Connections,
|
||||
Created: libraryElement.Created,
|
||||
Updated: libraryElement.Updated,
|
||||
FolderName: libraryElement.FolderName,
|
||||
FolderUID: libraryElement.FolderUID,
|
||||
ConnectedDashboards: libraryElement.ConnectedDashboards,
|
||||
Created: libraryElement.Created,
|
||||
Updated: libraryElement.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: libraryElement.CreatedBy,
|
||||
Name: libraryElement.CreatedByName,
|
||||
@@ -332,11 +332,11 @@ func (l *LibraryElementService) getAllLibraryElements(c *models.ReqContext, quer
|
||||
Model: element.Model,
|
||||
Version: element.Version,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: element.FolderName,
|
||||
FolderUID: element.FolderUID,
|
||||
Connections: element.Connections,
|
||||
Created: element.Created,
|
||||
Updated: element.Updated,
|
||||
FolderName: element.FolderName,
|
||||
FolderUID: element.FolderUID,
|
||||
ConnectedDashboards: element.ConnectedDashboards,
|
||||
Created: element.Created,
|
||||
Updated: element.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: element.CreatedBy,
|
||||
Name: element.CreatedByName,
|
||||
@@ -467,9 +467,9 @@ func (l *LibraryElementService) patchLibraryElement(c *models.ReqContext, cmd pa
|
||||
Model: libraryElement.Model,
|
||||
Version: libraryElement.Version,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
Connections: elementInDB.Connections,
|
||||
Created: libraryElement.Created,
|
||||
Updated: libraryElement.Updated,
|
||||
ConnectedDashboards: elementInDB.ConnectedDashboards,
|
||||
Created: libraryElement.Created,
|
||||
Updated: libraryElement.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: elementInDB.CreatedBy,
|
||||
Name: elementInDB.CreatedByName,
|
||||
@@ -503,7 +503,7 @@ func (l *LibraryElementService) getConnections(c *models.ReqContext, uid string)
|
||||
builder.Write(" FROM " + connectionTableName + " AS lec")
|
||||
builder.Write(" LEFT JOIN user AS u1 ON lec.created_by = u1.id")
|
||||
builder.Write(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id")
|
||||
builder.Write(` WHERE lec.library_element_id=?`, element.ID)
|
||||
builder.Write(` WHERE lec.element_id=?`, element.ID)
|
||||
if c.SignedInUser.OrgRole != models.ROLE_ADMIN {
|
||||
builder.WriteDashboardPermissionFilter(c.SignedInUser, models.PERMISSION_VIEW)
|
||||
}
|
||||
@@ -514,8 +514,8 @@ func (l *LibraryElementService) getConnections(c *models.ReqContext, uid string)
|
||||
for _, connection := range libraryElementConnections {
|
||||
connections = append(connections, LibraryElementConnectionDTO{
|
||||
ID: connection.ID,
|
||||
Kind: connection.ConnectionKind,
|
||||
ElementID: connection.LibraryElementID,
|
||||
Kind: connection.Kind,
|
||||
ElementID: connection.ElementID,
|
||||
ConnectionID: connection.ConnectionID,
|
||||
Created: connection.Created,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
@@ -542,7 +542,7 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
|
||||
", coalesce(dashboard.uid, '') AS folder_uid" +
|
||||
fromLibraryElementDTOWithMeta +
|
||||
" LEFT JOIN dashboard AS dashboard ON dashboard.id = le.folder_id" +
|
||||
" INNER JOIN " + connectionTableName + " AS lce ON lce.library_element_id = le.id AND lce.connection_kind=1 AND lce.connection_id=?"
|
||||
" INNER JOIN " + connectionTableName + " AS lce ON lce.element_id = le.id AND lce.kind=1 AND lce.connection_id=?"
|
||||
sess := session.SQL(sql, dashboardID)
|
||||
err := sess.Find(&libraryElements)
|
||||
if err != nil {
|
||||
@@ -562,11 +562,11 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
|
||||
Model: element.Model,
|
||||
Version: element.Version,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: element.FolderName,
|
||||
FolderUID: element.FolderUID,
|
||||
Connections: element.Connections,
|
||||
Created: element.Created,
|
||||
Updated: element.Updated,
|
||||
FolderName: element.FolderName,
|
||||
FolderUID: element.FolderUID,
|
||||
ConnectedDashboards: element.ConnectedDashboards,
|
||||
Created: element.Created,
|
||||
Updated: element.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: element.CreatedBy,
|
||||
Name: element.CreatedByName,
|
||||
@@ -590,7 +590,7 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
|
||||
// connectElementsToDashboardID adds connections for all elements Library Elements in a Dashboard.
|
||||
func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContext, elementUIDs []string, dashboardID int64) error {
|
||||
err := l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE connection_kind=1 AND connection_id=?", dashboardID)
|
||||
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -604,11 +604,11 @@ func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContex
|
||||
}
|
||||
|
||||
connection := libraryElementConnection{
|
||||
LibraryElementID: element.ID,
|
||||
ConnectionKind: 1,
|
||||
ConnectionID: dashboardID,
|
||||
Created: time.Now(),
|
||||
CreatedBy: c.SignedInUser.UserId,
|
||||
ElementID: element.ID,
|
||||
Kind: 1,
|
||||
ConnectionID: dashboardID,
|
||||
Created: time.Now(),
|
||||
CreatedBy: c.SignedInUser.UserId,
|
||||
}
|
||||
if _, err := session.Insert(&connection); err != nil {
|
||||
if l.SQLStore.Dialect.IsUniqueConstraintViolation(err) {
|
||||
@@ -626,7 +626,7 @@ func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContex
|
||||
// disconnectElementsFromDashboardID deletes connections for all Library Elements in a Dashboard.
|
||||
func (l *LibraryElementService) disconnectElementsFromDashboardID(c *models.ReqContext, dashboardID int64) error {
|
||||
return l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE connection_kind=1 AND connection_id=?", dashboardID)
|
||||
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -656,7 +656,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo
|
||||
ConnectionID int64 `xorm:"connection_id"`
|
||||
}
|
||||
sql := "SELECT lec.connection_id FROM library_element AS le"
|
||||
sql += " INNER JOIN " + connectionTableName + " AS lec on le.id = lec.library_element_id"
|
||||
sql += " INNER JOIN " + connectionTableName + " AS lec on le.id = lec.element_id"
|
||||
sql += " WHERE le.folder_id=? AND le.org_id=?"
|
||||
err = session.SQL(sql, folderID, c.SignedInUser.OrgId).Find(&connectionIDs)
|
||||
if err != nil {
|
||||
@@ -674,7 +674,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo
|
||||
return err
|
||||
}
|
||||
for _, elementID := range elementIDs {
|
||||
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE library_element_id=?", elementID.ID)
|
||||
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE element_id=?", elementID.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,15 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// Service is a service for operating on library elements.
|
||||
type Service interface {
|
||||
CreateElement(c *models.ReqContext, cmd CreateLibraryElementCommand) (LibraryElementDTO, error)
|
||||
GetElementsForDashboard(c *models.ReqContext, dashboardID int64) (map[string]LibraryElementDTO, error)
|
||||
ConnectElementsToDashboard(c *models.ReqContext, elementUIDs []string, dashboardID int64) error
|
||||
DisconnectElementsFromDashboard(c *models.ReqContext, dashboardID int64) error
|
||||
DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error
|
||||
}
|
||||
|
||||
// LibraryElementService is the service for the Library Element feature.
|
||||
type LibraryElementService struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
@@ -33,66 +42,34 @@ func (l *LibraryElementService) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the Panel Library feature is enabled for this instance.
|
||||
func (l *LibraryElementService) IsEnabled() bool {
|
||||
if l.Cfg == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return l.Cfg.IsPanelLibraryEnabled()
|
||||
}
|
||||
|
||||
// CreateElement creates a Library Element.
|
||||
func (l *LibraryElementService) CreateElement(c *models.ReqContext, cmd CreateLibraryElementCommand) (LibraryElementDTO, error) {
|
||||
if !l.IsEnabled() {
|
||||
return LibraryElementDTO{}, nil
|
||||
}
|
||||
|
||||
return l.createLibraryElement(c, cmd)
|
||||
}
|
||||
|
||||
// GetElementsForDashboard gets all connected elements for a specific dashboard.
|
||||
func (l *LibraryElementService) GetElementsForDashboard(c *models.ReqContext, dashboardID int64) (map[string]LibraryElementDTO, error) {
|
||||
if !l.IsEnabled() {
|
||||
return map[string]LibraryElementDTO{}, nil
|
||||
}
|
||||
|
||||
return l.getElementsForDashboardID(c, dashboardID)
|
||||
}
|
||||
|
||||
// ConnectElementsToDashboard connects elements to a specific dashboard.
|
||||
func (l *LibraryElementService) ConnectElementsToDashboard(c *models.ReqContext, elementUIDs []string, dashboardID int64) error {
|
||||
if !l.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return l.connectElementsToDashboardID(c, elementUIDs, dashboardID)
|
||||
}
|
||||
|
||||
// DisconnectElementsFromDashboard disconnects elements from a specific dashboard.
|
||||
func (l *LibraryElementService) DisconnectElementsFromDashboard(c *models.ReqContext, dashboardID int64) error {
|
||||
if !l.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return l.disconnectElementsFromDashboardID(c, dashboardID)
|
||||
}
|
||||
|
||||
// DeleteLibraryElementsInFolder deletes all elements for a specific folder.
|
||||
func (l *LibraryElementService) DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error {
|
||||
if !l.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
return l.deleteLibraryElementsInFolderUID(c, folderUID)
|
||||
}
|
||||
|
||||
// AddMigration defines database migrations.
|
||||
// If Panel Library is not enabled does nothing.
|
||||
func (l *LibraryElementService) AddMigration(mg *migrator.Migrator) {
|
||||
if !l.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
libraryElementsV1 := migrator.Table{
|
||||
Name: "library_element",
|
||||
Columns: []*migrator.Column{
|
||||
@@ -100,7 +77,7 @@ func (l *LibraryElementService) AddMigration(mg *migrator.Migrator) {
|
||||
{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "folder_id", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
|
||||
{Name: "name", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "name", Type: migrator.DB_NVarchar, Length: 150, Nullable: false},
|
||||
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "type", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
|
||||
{Name: "description", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
|
||||
@@ -117,23 +94,23 @@ func (l *LibraryElementService) AddMigration(mg *migrator.Migrator) {
|
||||
}
|
||||
|
||||
mg.AddMigration("create library_element table v1", migrator.NewAddTableMigration(libraryElementsV1))
|
||||
mg.AddMigration("add index library_element", migrator.NewAddIndexMigration(libraryElementsV1, libraryElementsV1.Indices[0]))
|
||||
mg.AddMigration("add index library_element org_id-folder_id-name-kind", migrator.NewAddIndexMigration(libraryElementsV1, libraryElementsV1.Indices[0]))
|
||||
|
||||
libraryElementConnectionV1 := migrator.Table{
|
||||
Name: connectionTableName,
|
||||
Columns: []*migrator.Column{
|
||||
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "library_element_id", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "connection_kind", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "element_id", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "connection_id", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
|
||||
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
|
||||
},
|
||||
Indices: []*migrator.Index{
|
||||
{Cols: []string{"library_element_id", "connection_kind", "connection_id"}, Type: migrator.UniqueIndex},
|
||||
{Cols: []string{"element_id", "kind", "connection_id"}, Type: migrator.UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create "+connectionTableName+" table v1", migrator.NewAddTableMigration(libraryElementConnectionV1))
|
||||
mg.AddMigration("add index "+connectionTableName, migrator.NewAddIndexMigration(libraryElementConnectionV1, libraryElementConnectionV1.Indices[0]))
|
||||
mg.AddMigration("add index "+connectionTableName+" element_id-kind-connection_id", migrator.NewAddIndexMigration(libraryElementConnectionV1, libraryElementConnectionV1.Indices[0]))
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
Connections: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: sc.initialResult.Result.Meta.Updated,
|
||||
ConnectedDashboards: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: sc.initialResult.Result.Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
@@ -81,9 +81,9 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
Connections: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
|
||||
@@ -74,11 +74,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -138,11 +138,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -199,11 +199,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -234,11 +234,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -298,11 +298,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -333,11 +333,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -417,11 +417,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -452,11 +452,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -554,11 +554,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "NewFolder",
|
||||
FolderUID: newFolder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "NewFolder",
|
||||
FolderUID: newFolder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -649,11 +649,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -684,11 +684,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -748,11 +748,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -812,11 +812,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -877,11 +877,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -951,11 +951,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -1023,11 +1023,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -1058,11 +1058,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[1].Meta.Created,
|
||||
Updated: result.Result.Elements[1].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
@@ -1124,11 +1124,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
|
||||
@@ -3,6 +3,8 @@ package libraryelements
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -41,11 +43,92 @@ func TestGetLibraryElement(t *testing.T) {
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
Connections: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
AvatarURL: userInDbAvatar,
|
||||
},
|
||||
UpdatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
AvatarURL: userInDbAvatar,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithPanel(t, "When an admin tries to get a connected library panel, it should succeed and return correct connected dashboards",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashJSON := map[string]interface{}{
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": int64(1),
|
||||
"gridPos": map[string]interface{}{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": int64(2),
|
||||
"gridPos": map[string]interface{}{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
},
|
||||
"libraryPanel": map[string]interface{}{
|
||||
"uid": sc.initialResult.Result.UID,
|
||||
"name": sc.initialResult.Result.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
dash := models.Dashboard{
|
||||
Title: "Testing getHandler",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryElementResult{
|
||||
Result: libraryElement{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(Panel),
|
||||
Type: "text",
|
||||
Description: "A description",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"description": "A description",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: sc.folder.Uid,
|
||||
ConnectedDashboards: 1,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
|
||||
@@ -57,9 +57,9 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
},
|
||||
Version: 2,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
Connections: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
ConnectedDashboards: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryElementDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: userInDbName,
|
||||
|
||||
@@ -121,27 +121,6 @@ type libraryElementsSearchResult struct {
|
||||
PerPage int `json:"perPage"`
|
||||
}
|
||||
|
||||
func overrideLibraryElementServiceInRegistry(cfg *setting.Cfg) LibraryElementService {
|
||||
l := LibraryElementService{
|
||||
SQLStore: nil,
|
||||
Cfg: cfg,
|
||||
}
|
||||
|
||||
overrideServiceFunc := func(d registry.Descriptor) (*registry.Descriptor, bool) {
|
||||
descriptor := registry.Descriptor{
|
||||
Name: "LibraryElementService",
|
||||
Instance: &l,
|
||||
InitPriority: 0,
|
||||
}
|
||||
|
||||
return &descriptor, true
|
||||
}
|
||||
|
||||
registry.RegisterOverride(overrideServiceFunc)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func getCreatePanelCommand(folderID int64, name string) CreateLibraryElementCommand {
|
||||
command := getCreateCommandWithModel(folderID, name, Panel, []byte(`
|
||||
{
|
||||
@@ -294,17 +273,11 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
}
|
||||
orgID := int64(1)
|
||||
role := models.ROLE_ADMIN
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
// Everything in this service is behind the feature toggle "panelLibrary"
|
||||
cfg.FeatureToggles = map[string]bool{"panelLibrary": true}
|
||||
// Because the LibraryElementService is behind a feature toggle, we need to override the service in the registry
|
||||
// with a Cfg that contains the feature toggle so migrations are run properly
|
||||
service := overrideLibraryElementServiceInRegistry(cfg)
|
||||
|
||||
// We need to assign SQLStore after the override and migrations are done
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
service.SQLStore = sqlStore
|
||||
service := LibraryElementService{
|
||||
Cfg: setting.NewCfg(),
|
||||
SQLStore: sqlStore,
|
||||
}
|
||||
|
||||
user := models.SignedInUser{
|
||||
UserId: 1,
|
||||
|
||||
@@ -55,15 +55,15 @@ type LibraryElementWithMeta struct {
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
|
||||
FolderName string
|
||||
FolderUID string `xorm:"folder_uid"`
|
||||
Connections int64
|
||||
CreatedBy int64
|
||||
UpdatedBy int64
|
||||
CreatedByName string
|
||||
CreatedByEmail string
|
||||
UpdatedByName string
|
||||
UpdatedByEmail string
|
||||
FolderName string
|
||||
FolderUID string `xorm:"folder_uid"`
|
||||
ConnectedDashboards int64
|
||||
CreatedBy int64
|
||||
UpdatedBy int64
|
||||
CreatedByName string
|
||||
CreatedByEmail string
|
||||
UpdatedByName string
|
||||
UpdatedByEmail string
|
||||
}
|
||||
|
||||
// LibraryElementDTO is the frontend DTO for entities.
|
||||
@@ -91,9 +91,9 @@ type LibraryElementSearchResult struct {
|
||||
|
||||
// LibraryElementDTOMeta is the meta information for LibraryElementDTO.
|
||||
type LibraryElementDTOMeta struct {
|
||||
FolderName string `json:"folderName"`
|
||||
FolderUID string `json:"folderUid"`
|
||||
Connections int64 `json:"connections"`
|
||||
FolderName string `json:"folderName"`
|
||||
FolderUID string `json:"folderUid"`
|
||||
ConnectedDashboards int64 `json:"connectedDashboards"`
|
||||
|
||||
Created time.Time `json:"created"`
|
||||
Updated time.Time `json:"updated"`
|
||||
@@ -111,24 +111,24 @@ type LibraryElementDTOMetaUser struct {
|
||||
|
||||
// libraryElementConnection is the model for library element connections.
|
||||
type libraryElementConnection struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
LibraryElementID int64 `xorm:"library_element_id"`
|
||||
ConnectionKind int64 `xorm:"connection_kind"`
|
||||
ConnectionID int64 `xorm:"connection_id"`
|
||||
Created time.Time
|
||||
CreatedBy int64
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
ElementID int64 `xorm:"element_id"`
|
||||
Kind int64 `xorm:"kind"`
|
||||
ConnectionID int64 `xorm:"connection_id"`
|
||||
Created time.Time
|
||||
CreatedBy int64
|
||||
}
|
||||
|
||||
// libraryElementConnectionWithMeta is the model for library element connections with meta.
|
||||
type libraryElementConnectionWithMeta struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
LibraryElementID int64 `xorm:"library_element_id"`
|
||||
ConnectionKind int64 `xorm:"connection_kind"`
|
||||
ConnectionID int64 `xorm:"connection_id"`
|
||||
Created time.Time
|
||||
CreatedBy int64
|
||||
CreatedByName string
|
||||
CreatedByEmail string
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
ElementID int64 `xorm:"element_id"`
|
||||
Kind int64 `xorm:"kind"`
|
||||
ConnectionID int64 `xorm:"connection_id"`
|
||||
Created time.Time
|
||||
CreatedBy int64
|
||||
CreatedByName string
|
||||
CreatedByEmail string
|
||||
}
|
||||
|
||||
// LibraryElementConnectionDTO is the frontend DTO for element connections.
|
||||
|
||||
Reference in New Issue
Block a user