2021-05-11 00:10:19 -05:00
package libraryelements
import (
2021-09-14 09:08:04 -05:00
"context"
2021-05-11 00:10:19 -05:00
"encoding/json"
2021-09-10 04:22:13 -05:00
"errors"
2021-05-11 00:10:19 -05:00
"fmt"
"strings"
"time"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/models"
2022-06-30 08:31:54 -05:00
"github.com/grafana/grafana/pkg/services/dashboards"
2021-05-18 07:52:19 -05:00
"github.com/grafana/grafana/pkg/services/search"
2021-05-11 00:10:19 -05:00
"github.com/grafana/grafana/pkg/services/sqlstore"
2021-05-18 07:52:19 -05:00
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
2021-05-11 00:10:19 -05:00
"github.com/grafana/grafana/pkg/util"
)
const (
selectLibraryElementDTOWithMeta = `
SELECT DISTINCT
le . name , le . id , le . org_id , le . folder_id , le . uid , le . kind , le . type , le . description , le . model , le . created , le . created_by , le . updated , le . updated_by , le . version
, u1 . login AS created_by_name
, u1 . email AS created_by_email
, u2 . login AS updated_by_name
, u2 . email AS updated_by_email
2021-05-23 23:11:01 -05:00
, ( SELECT COUNT ( connection_id ) FROM ` + models.LibraryElementConnectionTableName + ` WHERE element_id = le . id AND kind = 1 ) AS connected_dashboards `
2021-05-18 07:52:19 -05:00
)
2022-06-29 07:56:40 -05:00
const deleteInvalidConnections = "DELETE FROM library_element_connection WHERE connection_id IN (SELECT connection_id as id FROM library_element_connection WHERE element_id=? EXCEPT SELECT id from dashboard)"
2021-05-18 07:52:19 -05:00
func getFromLibraryElementDTOWithMeta ( dialect migrator . Dialect ) string {
user := dialect . Quote ( "user" )
userJoin := `
2021-05-11 00:10:19 -05:00
FROM library_element AS le
2021-05-18 07:52:19 -05:00
LEFT JOIN ` + user + ` AS u1 ON le . created_by = u1 . id
LEFT JOIN ` + user + ` AS u2 ON le . updated_by = u2 . id
2021-05-11 00:10:19 -05:00
`
2021-05-18 07:52:19 -05:00
return userJoin
}
2021-05-11 00:10:19 -05:00
func syncFieldsWithModel ( libraryElement * LibraryElement ) error {
var model map [ string ] interface { }
if err := json . Unmarshal ( libraryElement . Model , & model ) ; err != nil {
return err
}
2021-09-01 06:27:43 -05:00
if models . LibraryElementKind ( libraryElement . Kind ) == models . VariableElement {
2021-05-11 00:10:19 -05:00
model [ "name" ] = libraryElement . Name
}
if model [ "type" ] != nil {
libraryElement . Type = model [ "type" ] . ( string )
} else {
model [ "type" ] = libraryElement . Type
}
if model [ "description" ] != nil {
libraryElement . Description = model [ "description" ] . ( string )
} else {
model [ "description" ] = libraryElement . Description
}
syncedModel , err := json . Marshal ( & model )
if err != nil {
return err
}
libraryElement . Model = syncedModel
return nil
}
2021-05-18 07:52:19 -05:00
func getLibraryElement ( dialect migrator . Dialect , session * sqlstore . DBSession , uid string , orgID int64 ) ( LibraryElementWithMeta , error ) {
2021-05-11 00:10:19 -05:00
elements := make ( [ ] LibraryElementWithMeta , 0 )
sql := selectLibraryElementDTOWithMeta +
", coalesce(dashboard.title, 'General') AS folder_name" +
", coalesce(dashboard.uid, '') AS folder_uid" +
2021-05-18 07:52:19 -05:00
getFromLibraryElementDTOWithMeta ( dialect ) +
2021-05-11 00:10:19 -05:00
" LEFT JOIN dashboard AS dashboard ON dashboard.id = le.folder_id" +
" WHERE le.uid=? AND le.org_id=?"
sess := session . SQL ( sql , uid , orgID )
err := sess . Find ( & elements )
if err != nil {
return LibraryElementWithMeta { } , err
}
if len ( elements ) == 0 {
2021-09-20 03:58:24 -05:00
return LibraryElementWithMeta { } , ErrLibraryElementNotFound
2021-05-11 00:10:19 -05:00
}
if len ( elements ) > 1 {
return LibraryElementWithMeta { } , fmt . Errorf ( "found %d elements, while expecting at most one" , len ( elements ) )
}
return elements [ 0 ] , nil
}
// createLibraryElement adds a library element.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) createLibraryElement ( c context . Context , signedInUser * models . SignedInUser , cmd CreateLibraryElementCommand ) ( LibraryElementDTO , error ) {
2021-05-11 00:10:19 -05:00
if err := l . requireSupportedElementKind ( cmd . Kind ) ; err != nil {
return LibraryElementDTO { } , err
}
2021-09-10 04:22:13 -05:00
createUID := cmd . UID
if len ( createUID ) == 0 {
createUID = util . GenerateShortUID ( )
} else {
if ! util . IsValidShortUID ( createUID ) {
return LibraryElementDTO { } , errLibraryElementInvalidUID
} else if util . IsShortUIDTooLong ( createUID ) {
return LibraryElementDTO { } , errLibraryElementUIDTooLong
}
}
2021-05-11 00:10:19 -05:00
element := LibraryElement {
2021-09-27 02:04:36 -05:00
OrgID : signedInUser . OrgId ,
2021-05-11 00:10:19 -05:00
FolderID : cmd . FolderID ,
2021-09-10 04:22:13 -05:00
UID : createUID ,
2021-05-11 00:10:19 -05:00
Name : cmd . Name ,
Model : cmd . Model ,
Version : 1 ,
Kind : cmd . Kind ,
Created : time . Now ( ) ,
Updated : time . Now ( ) ,
2021-09-27 02:04:36 -05:00
CreatedBy : signedInUser . UserId ,
UpdatedBy : signedInUser . UserId ,
2021-05-11 00:10:19 -05:00
}
if err := syncFieldsWithModel ( & element ) ; err != nil {
return LibraryElementDTO { } , err
}
2021-09-27 02:04:36 -05:00
err := l . SQLStore . WithTransactionalDbSession ( c , func ( session * sqlstore . DBSession ) error {
2022-06-13 08:26:17 -05:00
if err := l . requireEditPermissionsOnFolder ( c , signedInUser , cmd . FolderID ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
if _ , err := session . Insert ( & element ) ; err != nil {
if l . SQLStore . Dialect . IsUniqueConstraintViolation ( err ) {
return errLibraryElementAlreadyExists
}
return err
}
return nil
} )
dto := LibraryElementDTO {
ID : element . ID ,
OrgID : element . OrgID ,
FolderID : element . FolderID ,
UID : element . UID ,
Name : element . Name ,
Kind : element . Kind ,
Type : element . Type ,
Description : element . Description ,
Model : element . Model ,
Version : element . Version ,
Meta : LibraryElementDTOMeta {
2021-05-12 01:48:17 -05:00
ConnectedDashboards : 0 ,
Created : element . Created ,
Updated : element . Updated ,
2021-05-11 00:10:19 -05:00
CreatedBy : LibraryElementDTOMetaUser {
ID : element . CreatedBy ,
2021-09-27 02:04:36 -05:00
Name : signedInUser . Login ,
AvatarURL : dtos . GetGravatarUrl ( signedInUser . Email ) ,
2021-05-11 00:10:19 -05:00
} ,
UpdatedBy : LibraryElementDTOMetaUser {
ID : element . UpdatedBy ,
2021-09-27 02:04:36 -05:00
Name : signedInUser . Login ,
AvatarURL : dtos . GetGravatarUrl ( signedInUser . Email ) ,
2021-05-11 00:10:19 -05:00
} ,
} ,
}
return dto , err
}
// deleteLibraryElement deletes a library element.
2021-11-05 09:06:14 -05:00
func ( l * LibraryElementService ) deleteLibraryElement ( c context . Context , signedInUser * models . SignedInUser , uid string ) ( int64 , error ) {
var elementID int64
err := l . SQLStore . WithTransactionalDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-09-27 02:04:36 -05:00
element , err := getLibraryElement ( l . SQLStore . Dialect , session , uid , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
2022-06-13 08:26:17 -05:00
if err := l . requireEditPermissionsOnFolder ( c , signedInUser , element . FolderID ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
2022-06-29 07:56:40 -05:00
// Delete any hanging/invalid connections
if _ , err = session . Exec ( deleteInvalidConnections , element . ID ) ; err != nil {
return err
}
2021-05-11 00:10:19 -05:00
var connectionIDs [ ] struct {
ConnectionID int64 ` xorm:"connection_id" `
}
2021-05-12 01:48:17 -05:00
sql := "SELECT connection_id FROM library_element_connection WHERE element_id=?"
2021-05-11 00:10:19 -05:00
if err := session . SQL ( sql , element . ID ) . Find ( & connectionIDs ) ; err != nil {
return err
} else if len ( connectionIDs ) > 0 {
return errLibraryElementHasConnections
}
result , err := session . Exec ( "DELETE FROM library_element WHERE id=?" , element . ID )
if err != nil {
return err
}
if rowsAffected , err := result . RowsAffected ( ) ; err != nil {
return err
} else if rowsAffected != 1 {
2021-09-20 03:58:24 -05:00
return ErrLibraryElementNotFound
2021-05-11 00:10:19 -05:00
}
2021-11-05 09:06:14 -05:00
elementID = element . ID
2021-05-11 00:10:19 -05:00
return nil
} )
2021-11-05 09:06:14 -05:00
return elementID , err
2021-05-11 00:10:19 -05:00
}
2021-09-20 03:58:24 -05:00
// getLibraryElements gets a Library Element where param == value
2021-09-27 02:04:36 -05:00
func getLibraryElements ( c context . Context , store * sqlstore . SQLStore , signedInUser * models . SignedInUser , params [ ] Pair ) ( [ ] LibraryElementDTO , error ) {
2021-05-14 09:03:37 -05:00
libraryElements := make ( [ ] LibraryElementWithMeta , 0 )
2021-09-27 02:04:36 -05:00
err := store . WithDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-05-11 00:10:19 -05:00
builder := sqlstore . SQLBuilder { }
builder . Write ( selectLibraryElementDTOWithMeta )
builder . Write ( ", 'General' as folder_name " )
builder . Write ( ", '' as folder_uid " )
2021-09-20 03:58:24 -05:00
builder . Write ( getFromLibraryElementDTOWithMeta ( store . Dialect ) )
2021-05-14 09:03:37 -05:00
writeParamSelectorSQL ( & builder , append ( params , Pair { "folder_id" , 0 } ) ... )
2021-05-11 00:10:19 -05:00
builder . Write ( " UNION " )
builder . Write ( selectLibraryElementDTOWithMeta )
builder . Write ( ", dashboard.title as folder_name " )
builder . Write ( ", dashboard.uid as folder_uid " )
2021-09-20 03:58:24 -05:00
builder . Write ( getFromLibraryElementDTOWithMeta ( store . Dialect ) )
2021-05-11 00:10:19 -05:00
builder . Write ( " INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id AND le.folder_id <> 0" )
2021-05-14 09:03:37 -05:00
writeParamSelectorSQL ( & builder , params ... )
2021-09-27 02:04:36 -05:00
if signedInUser . OrgRole != models . ROLE_ADMIN {
builder . WriteDashboardPermissionFilter ( signedInUser , models . PERMISSION_VIEW )
2021-05-11 00:10:19 -05:00
}
builder . Write ( ` OR dashboard.id=0 ` )
if err := session . SQL ( builder . GetSQLString ( ) , builder . GetParams ( ) ... ) . Find ( & libraryElements ) ; err != nil {
return err
}
if len ( libraryElements ) == 0 {
2021-09-20 03:58:24 -05:00
return ErrLibraryElementNotFound
2021-05-11 00:10:19 -05:00
}
return nil
} )
if err != nil {
2021-05-14 09:03:37 -05:00
return [ ] LibraryElementDTO { } , err
2021-05-11 00:10:19 -05:00
}
2021-05-14 09:03:37 -05:00
leDtos := make ( [ ] LibraryElementDTO , len ( libraryElements ) )
for i , libraryElement := range libraryElements {
leDtos [ i ] = LibraryElementDTO {
ID : libraryElement . ID ,
OrgID : libraryElement . OrgID ,
FolderID : libraryElement . FolderID ,
2022-05-05 03:04:54 -05:00
FolderUID : libraryElement . FolderUID ,
2021-05-14 09:03:37 -05:00
UID : libraryElement . UID ,
Name : libraryElement . Name ,
Kind : libraryElement . Kind ,
Type : libraryElement . Type ,
Description : libraryElement . Description ,
Model : libraryElement . Model ,
Version : libraryElement . Version ,
Meta : LibraryElementDTOMeta {
FolderName : libraryElement . FolderName ,
FolderUID : libraryElement . FolderUID ,
ConnectedDashboards : libraryElement . ConnectedDashboards ,
Created : libraryElement . Created ,
Updated : libraryElement . Updated ,
CreatedBy : LibraryElementDTOMetaUser {
ID : libraryElement . CreatedBy ,
Name : libraryElement . CreatedByName ,
AvatarURL : dtos . GetGravatarUrl ( libraryElement . CreatedByEmail ) ,
} ,
UpdatedBy : LibraryElementDTOMetaUser {
ID : libraryElement . UpdatedBy ,
Name : libraryElement . UpdatedByName ,
AvatarURL : dtos . GetGravatarUrl ( libraryElement . UpdatedByEmail ) ,
} ,
2021-05-11 00:10:19 -05:00
} ,
2021-05-14 09:03:37 -05:00
}
2021-05-11 00:10:19 -05:00
}
2021-05-14 09:03:37 -05:00
return leDtos , nil
}
// getLibraryElementByUid gets a Library Element by uid.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) getLibraryElementByUid ( c context . Context , signedInUser * models . SignedInUser , UID string ) ( LibraryElementDTO , error ) {
libraryElements , err := getLibraryElements ( c , l . SQLStore , signedInUser , [ ] Pair { { key : "org_id" , value : signedInUser . OrgId } , { key : "uid" , value : UID } } )
2021-05-14 09:03:37 -05:00
if err != nil {
return LibraryElementDTO { } , err
}
if len ( libraryElements ) > 1 {
return LibraryElementDTO { } , fmt . Errorf ( "found %d elements, while expecting at most one" , len ( libraryElements ) )
}
return libraryElements [ 0 ] , nil
}
// getLibraryElementByName gets a Library Element by name.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) getLibraryElementsByName ( c context . Context , signedInUser * models . SignedInUser , name string ) ( [ ] LibraryElementDTO , error ) {
return getLibraryElements ( c , l . SQLStore , signedInUser , [ ] Pair { { "org_id" , signedInUser . OrgId } , { "name" , name } } )
2021-05-11 00:10:19 -05:00
}
// getAllLibraryElements gets all Library Elements.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) getAllLibraryElements ( c context . Context , signedInUser * models . SignedInUser , query searchLibraryElementsQuery ) ( LibraryElementSearchResult , error ) {
2021-05-11 00:10:19 -05:00
elements := make ( [ ] LibraryElementWithMeta , 0 )
result := LibraryElementSearchResult { }
if query . perPage <= 0 {
query . perPage = 100
}
if query . page <= 0 {
query . page = 1
}
var typeFilter [ ] string
if len ( strings . TrimSpace ( query . typeFilter ) ) > 0 {
typeFilter = strings . Split ( query . typeFilter , "," )
}
folderFilter := parseFolderFilter ( query )
if folderFilter . parseError != nil {
return LibraryElementSearchResult { } , folderFilter . parseError
}
2021-09-27 02:04:36 -05:00
err := l . SQLStore . WithDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-05-11 00:10:19 -05:00
builder := sqlstore . SQLBuilder { }
if folderFilter . includeGeneralFolder {
builder . Write ( selectLibraryElementDTOWithMeta )
builder . Write ( ", 'General' as folder_name " )
builder . Write ( ", '' as folder_uid " )
2021-05-18 07:52:19 -05:00
builder . Write ( getFromLibraryElementDTOWithMeta ( l . SQLStore . Dialect ) )
2021-09-27 02:04:36 -05:00
builder . Write ( ` WHERE le.org_id=? AND le.folder_id=0 ` , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
writeKindSQL ( query , & builder )
writeSearchStringSQL ( query , l . SQLStore , & builder )
writeExcludeSQL ( query , & builder )
writeTypeFilterSQL ( typeFilter , & builder )
builder . Write ( " UNION " )
}
builder . Write ( selectLibraryElementDTOWithMeta )
builder . Write ( ", dashboard.title as folder_name " )
builder . Write ( ", dashboard.uid as folder_uid " )
2021-05-18 07:52:19 -05:00
builder . Write ( getFromLibraryElementDTOWithMeta ( l . SQLStore . Dialect ) )
2021-05-11 00:10:19 -05:00
builder . Write ( " INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id AND le.folder_id<>0" )
2021-09-27 02:04:36 -05:00
builder . Write ( ` WHERE le.org_id=? ` , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
writeKindSQL ( query , & builder )
writeSearchStringSQL ( query , l . SQLStore , & builder )
writeExcludeSQL ( query , & builder )
writeTypeFilterSQL ( typeFilter , & builder )
if err := folderFilter . writeFolderFilterSQL ( false , & builder ) ; err != nil {
return err
}
2021-09-27 02:04:36 -05:00
if signedInUser . OrgRole != models . ROLE_ADMIN {
builder . WriteDashboardPermissionFilter ( signedInUser , models . PERMISSION_VIEW )
2021-05-11 00:10:19 -05:00
}
if query . sortDirection == search . SortAlphaDesc . Name {
builder . Write ( " ORDER BY 1 DESC" )
} else {
builder . Write ( " ORDER BY 1 ASC" )
}
writePerPageSQL ( query , l . SQLStore , & builder )
if err := session . SQL ( builder . GetSQLString ( ) , builder . GetParams ( ) ... ) . Find ( & elements ) ; err != nil {
return err
}
retDTOs := make ( [ ] LibraryElementDTO , 0 )
for _ , element := range elements {
retDTOs = append ( retDTOs , LibraryElementDTO {
ID : element . ID ,
OrgID : element . OrgID ,
FolderID : element . FolderID ,
2022-05-05 03:04:54 -05:00
FolderUID : element . FolderUID ,
2021-05-11 00:10:19 -05:00
UID : element . UID ,
Name : element . Name ,
Kind : element . Kind ,
Type : element . Type ,
Description : element . Description ,
Model : element . Model ,
Version : element . Version ,
Meta : LibraryElementDTOMeta {
2021-05-12 01:48:17 -05:00
FolderName : element . FolderName ,
FolderUID : element . FolderUID ,
ConnectedDashboards : element . ConnectedDashboards ,
Created : element . Created ,
Updated : element . Updated ,
2021-05-11 00:10:19 -05:00
CreatedBy : LibraryElementDTOMetaUser {
ID : element . CreatedBy ,
Name : element . CreatedByName ,
AvatarURL : dtos . GetGravatarUrl ( element . CreatedByEmail ) ,
} ,
UpdatedBy : LibraryElementDTOMetaUser {
ID : element . UpdatedBy ,
Name : element . UpdatedByName ,
AvatarURL : dtos . GetGravatarUrl ( element . UpdatedByEmail ) ,
} ,
} ,
} )
}
var libraryElements [ ] LibraryElement
countBuilder := sqlstore . SQLBuilder { }
countBuilder . Write ( "SELECT * FROM library_element AS le" )
2021-09-27 02:04:36 -05:00
countBuilder . Write ( ` WHERE le.org_id=? ` , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
writeKindSQL ( query , & countBuilder )
writeSearchStringSQL ( query , l . SQLStore , & countBuilder )
writeExcludeSQL ( query , & countBuilder )
writeTypeFilterSQL ( typeFilter , & countBuilder )
if err := folderFilter . writeFolderFilterSQL ( true , & countBuilder ) ; err != nil {
return err
}
if err := session . SQL ( countBuilder . GetSQLString ( ) , countBuilder . GetParams ( ) ... ) . Find ( & libraryElements ) ; err != nil {
return err
}
result = LibraryElementSearchResult {
TotalCount : int64 ( len ( libraryElements ) ) ,
Elements : retDTOs ,
Page : query . page ,
PerPage : query . perPage ,
}
return nil
} )
return result , err
}
2021-09-14 09:08:04 -05:00
func ( l * LibraryElementService ) handleFolderIDPatches ( ctx context . Context , elementToPatch * LibraryElement , fromFolderID int64 , toFolderID int64 , user * models . SignedInUser ) error {
2021-05-11 00:10:19 -05:00
// FolderID was not provided in the PATCH request
if toFolderID == - 1 {
toFolderID = fromFolderID
}
// FolderID was provided in the PATCH request
if toFolderID != - 1 && toFolderID != fromFolderID {
2022-06-13 08:26:17 -05:00
if err := l . requireEditPermissionsOnFolder ( ctx , user , toFolderID ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
}
// Always check permissions for the folder where library element resides
2022-06-13 08:26:17 -05:00
if err := l . requireEditPermissionsOnFolder ( ctx , user , fromFolderID ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
elementToPatch . FolderID = toFolderID
return nil
}
// patchLibraryElement updates a Library Element.
2022-02-08 06:38:43 -06:00
func ( l * LibraryElementService ) patchLibraryElement ( c context . Context , signedInUser * models . SignedInUser , cmd PatchLibraryElementCommand , uid string ) ( LibraryElementDTO , error ) {
2021-05-11 00:10:19 -05:00
var dto LibraryElementDTO
if err := l . requireSupportedElementKind ( cmd . Kind ) ; err != nil {
return LibraryElementDTO { } , err
}
2021-09-27 02:04:36 -05:00
err := l . SQLStore . WithTransactionalDbSession ( c , func ( session * sqlstore . DBSession ) error {
elementInDB , err := getLibraryElement ( l . SQLStore . Dialect , session , uid , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
if elementInDB . Version != cmd . Version {
return errLibraryElementVersionMismatch
}
2021-09-10 04:22:13 -05:00
updateUID := cmd . UID
if len ( updateUID ) == 0 {
updateUID = uid
} else if updateUID != uid {
if ! util . IsValidShortUID ( updateUID ) {
return errLibraryElementInvalidUID
} else if util . IsShortUIDTooLong ( updateUID ) {
return errLibraryElementUIDTooLong
}
2021-09-27 02:04:36 -05:00
_ , err := getLibraryElement ( l . SQLStore . Dialect , session , updateUID , signedInUser . OrgId )
2021-09-20 03:58:24 -05:00
if ! errors . Is ( err , ErrLibraryElementNotFound ) {
2021-09-10 04:22:13 -05:00
return errLibraryElementAlreadyExists
}
}
2021-05-11 00:10:19 -05:00
var libraryElement = LibraryElement {
ID : elementInDB . ID ,
2021-09-27 02:04:36 -05:00
OrgID : signedInUser . OrgId ,
2021-05-11 00:10:19 -05:00
FolderID : cmd . FolderID ,
2021-09-10 04:22:13 -05:00
UID : updateUID ,
2021-05-11 00:10:19 -05:00
Name : cmd . Name ,
Kind : elementInDB . Kind ,
Type : elementInDB . Type ,
Description : elementInDB . Description ,
Model : cmd . Model ,
Version : elementInDB . Version + 1 ,
Created : elementInDB . Created ,
CreatedBy : elementInDB . CreatedBy ,
Updated : time . Now ( ) ,
2021-09-27 02:04:36 -05:00
UpdatedBy : signedInUser . UserId ,
2021-05-11 00:10:19 -05:00
}
if cmd . Name == "" {
libraryElement . Name = elementInDB . Name
}
if cmd . Model == nil {
libraryElement . Model = elementInDB . Model
}
2021-09-27 02:04:36 -05:00
if err := l . handleFolderIDPatches ( c , & libraryElement , elementInDB . FolderID , cmd . FolderID , signedInUser ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
if err := syncFieldsWithModel ( & libraryElement ) ; err != nil {
return err
}
if rowsAffected , err := session . ID ( elementInDB . ID ) . Update ( & libraryElement ) ; err != nil {
if l . SQLStore . Dialect . IsUniqueConstraintViolation ( err ) {
return errLibraryElementAlreadyExists
}
return err
} else if rowsAffected != 1 {
2021-09-20 03:58:24 -05:00
return ErrLibraryElementNotFound
2021-05-11 00:10:19 -05:00
}
dto = LibraryElementDTO {
ID : libraryElement . ID ,
OrgID : libraryElement . OrgID ,
FolderID : libraryElement . FolderID ,
UID : libraryElement . UID ,
Name : libraryElement . Name ,
Kind : libraryElement . Kind ,
Type : libraryElement . Type ,
Description : libraryElement . Description ,
Model : libraryElement . Model ,
Version : libraryElement . Version ,
Meta : LibraryElementDTOMeta {
2021-05-12 01:48:17 -05:00
ConnectedDashboards : elementInDB . ConnectedDashboards ,
Created : libraryElement . Created ,
Updated : libraryElement . Updated ,
2021-05-11 00:10:19 -05:00
CreatedBy : LibraryElementDTOMetaUser {
ID : elementInDB . CreatedBy ,
Name : elementInDB . CreatedByName ,
AvatarURL : dtos . GetGravatarUrl ( elementInDB . CreatedByEmail ) ,
} ,
UpdatedBy : LibraryElementDTOMetaUser {
ID : libraryElement . UpdatedBy ,
2021-09-27 02:04:36 -05:00
Name : signedInUser . Login ,
AvatarURL : dtos . GetGravatarUrl ( signedInUser . Email ) ,
2021-05-11 00:10:19 -05:00
} ,
} ,
}
return nil
} )
return dto , err
}
// getConnections gets all connections for a Library Element.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) getConnections ( c context . Context , signedInUser * models . SignedInUser , uid string ) ( [ ] LibraryElementConnectionDTO , error ) {
2021-05-11 00:10:19 -05:00
connections := make ( [ ] LibraryElementConnectionDTO , 0 )
2021-09-27 02:04:36 -05:00
err := l . SQLStore . WithDbSession ( c , func ( session * sqlstore . DBSession ) error {
element , err := getLibraryElement ( l . SQLStore . Dialect , session , uid , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
var libraryElementConnections [ ] libraryElementConnectionWithMeta
builder := sqlstore . SQLBuilder { }
builder . Write ( "SELECT lec.*, u1.login AS created_by_name, u1.email AS created_by_email" )
2021-05-23 23:11:01 -05:00
builder . Write ( " FROM " + models . LibraryElementConnectionTableName + " AS lec" )
2021-05-18 07:52:19 -05:00
builder . Write ( " LEFT JOIN " + l . SQLStore . Dialect . Quote ( "user" ) + " AS u1 ON lec.created_by = u1.id" )
2021-05-11 00:10:19 -05:00
builder . Write ( " INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id" )
2021-05-12 01:48:17 -05:00
builder . Write ( ` WHERE lec.element_id=? ` , element . ID )
2021-09-27 02:04:36 -05:00
if signedInUser . OrgRole != models . ROLE_ADMIN {
builder . WriteDashboardPermissionFilter ( signedInUser , models . PERMISSION_VIEW )
2021-05-11 00:10:19 -05:00
}
if err := session . SQL ( builder . GetSQLString ( ) , builder . GetParams ( ) ... ) . Find ( & libraryElementConnections ) ; err != nil {
return err
}
for _ , connection := range libraryElementConnections {
connections = append ( connections , LibraryElementConnectionDTO {
ID : connection . ID ,
2021-05-12 01:48:17 -05:00
Kind : connection . Kind ,
ElementID : connection . ElementID ,
2021-05-11 00:10:19 -05:00
ConnectionID : connection . ConnectionID ,
Created : connection . Created ,
CreatedBy : LibraryElementDTOMetaUser {
ID : connection . CreatedBy ,
Name : connection . CreatedByName ,
AvatarURL : dtos . GetGravatarUrl ( connection . CreatedByEmail ) ,
} ,
} )
}
return nil
} )
return connections , err
}
//getElementsForDashboardID gets all elements for a specific dashboard
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) getElementsForDashboardID ( c context . Context , dashboardID int64 ) ( map [ string ] LibraryElementDTO , error ) {
2021-05-11 00:10:19 -05:00
libraryElementMap := make ( map [ string ] LibraryElementDTO )
2021-09-27 02:04:36 -05:00
err := l . SQLStore . WithDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-05-11 00:10:19 -05:00
var libraryElements [ ] LibraryElementWithMeta
sql := selectLibraryElementDTOWithMeta +
", coalesce(dashboard.title, 'General') AS folder_name" +
", coalesce(dashboard.uid, '') AS folder_uid" +
2021-05-18 07:52:19 -05:00
getFromLibraryElementDTOWithMeta ( l . SQLStore . Dialect ) +
2021-05-11 00:10:19 -05:00
" LEFT JOIN dashboard AS dashboard ON dashboard.id = le.folder_id" +
2021-05-23 23:11:01 -05:00
" INNER JOIN " + models . LibraryElementConnectionTableName + " AS lce ON lce.element_id = le.id AND lce.kind=1 AND lce.connection_id=?"
2021-05-11 00:10:19 -05:00
sess := session . SQL ( sql , dashboardID )
err := sess . Find ( & libraryElements )
if err != nil {
return err
}
for _ , element := range libraryElements {
libraryElementMap [ element . UID ] = LibraryElementDTO {
ID : element . ID ,
OrgID : element . OrgID ,
FolderID : element . FolderID ,
UID : element . UID ,
Name : element . Name ,
Kind : element . Kind ,
Type : element . Type ,
Description : element . Description ,
Model : element . Model ,
Version : element . Version ,
Meta : LibraryElementDTOMeta {
2021-05-12 01:48:17 -05:00
FolderName : element . FolderName ,
FolderUID : element . FolderUID ,
ConnectedDashboards : element . ConnectedDashboards ,
Created : element . Created ,
Updated : element . Updated ,
2021-05-11 00:10:19 -05:00
CreatedBy : LibraryElementDTOMetaUser {
ID : element . CreatedBy ,
Name : element . CreatedByName ,
AvatarURL : dtos . GetGravatarUrl ( element . CreatedByEmail ) ,
} ,
UpdatedBy : LibraryElementDTOMetaUser {
ID : element . UpdatedBy ,
Name : element . UpdatedByName ,
AvatarURL : dtos . GetGravatarUrl ( element . UpdatedByEmail ) ,
} ,
} ,
}
}
return nil
} )
return libraryElementMap , err
}
// connectElementsToDashboardID adds connections for all elements Library Elements in a Dashboard.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) connectElementsToDashboardID ( c context . Context , signedInUser * models . SignedInUser , elementUIDs [ ] string , dashboardID int64 ) error {
2022-06-13 08:26:17 -05:00
if err := l . requireEditPermissionsOnDashboard ( c , signedInUser , dashboardID ) ; err != nil {
return err
}
2021-09-27 02:04:36 -05:00
err := l . SQLStore . WithTransactionalDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-05-23 23:11:01 -05:00
_ , err := session . Exec ( "DELETE FROM " + models . LibraryElementConnectionTableName + " WHERE kind=1 AND connection_id=?" , dashboardID )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
for _ , elementUID := range elementUIDs {
2021-09-27 02:04:36 -05:00
element , err := getLibraryElement ( l . SQLStore . Dialect , session , elementUID , signedInUser . OrgId )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
2022-06-13 08:26:17 -05:00
if err := l . requireViewPermissionsOnFolder ( c , signedInUser , element . FolderID ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
connection := libraryElementConnection {
2021-05-12 01:48:17 -05:00
ElementID : element . ID ,
Kind : 1 ,
ConnectionID : dashboardID ,
Created : time . Now ( ) ,
2021-09-27 02:04:36 -05:00
CreatedBy : signedInUser . UserId ,
2021-05-11 00:10:19 -05:00
}
if _ , err := session . Insert ( & connection ) ; err != nil {
if l . SQLStore . Dialect . IsUniqueConstraintViolation ( err ) {
return nil
}
return err
}
}
return nil
} )
return err
}
// disconnectElementsFromDashboardID deletes connections for all Library Elements in a Dashboard.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) disconnectElementsFromDashboardID ( c context . Context , dashboardID int64 ) error {
return l . SQLStore . WithTransactionalDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-05-23 23:11:01 -05:00
_ , err := session . Exec ( "DELETE FROM " + models . LibraryElementConnectionTableName + " WHERE kind=1 AND connection_id=?" , dashboardID )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
return nil
} )
}
// deleteLibraryElementsInFolderUID deletes all Library Elements in a folder.
2021-09-27 02:04:36 -05:00
func ( l * LibraryElementService ) deleteLibraryElementsInFolderUID ( c context . Context , signedInUser * models . SignedInUser , folderUID string ) error {
return l . SQLStore . WithTransactionalDbSession ( c , func ( session * sqlstore . DBSession ) error {
2021-05-11 00:10:19 -05:00
var folderUIDs [ ] struct {
ID int64 ` xorm:"id" `
}
2021-09-27 02:04:36 -05:00
err := session . SQL ( "SELECT id from dashboard WHERE uid=? AND org_id=? AND is_folder=?" , folderUID , signedInUser . OrgId , l . SQLStore . Dialect . BooleanStr ( true ) ) . Find ( & folderUIDs )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
2021-11-30 07:07:04 -06:00
if len ( folderUIDs ) == 0 {
2022-06-30 08:31:54 -05:00
return dashboards . ErrFolderNotFound
2021-11-30 07:07:04 -06:00
}
2021-05-11 00:10:19 -05:00
if len ( folderUIDs ) != 1 {
return fmt . Errorf ( "found %d folders, while expecting at most one" , len ( folderUIDs ) )
}
2021-11-30 07:07:04 -06:00
2021-05-11 00:10:19 -05:00
folderID := folderUIDs [ 0 ] . ID
2022-06-13 08:26:17 -05:00
if err := l . requireEditPermissionsOnFolder ( c , signedInUser , folderID ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
var connectionIDs [ ] struct {
ConnectionID int64 ` xorm:"connection_id" `
}
sql := "SELECT lec.connection_id FROM library_element AS le"
2021-05-23 23:11:01 -05:00
sql += " INNER JOIN " + models . LibraryElementConnectionTableName + " AS lec on le.id = lec.element_id"
2021-05-11 00:10:19 -05:00
sql += " WHERE le.folder_id=? AND le.org_id=?"
2021-09-27 02:04:36 -05:00
err = session . SQL ( sql , folderID , signedInUser . OrgId ) . Find ( & connectionIDs )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
if len ( connectionIDs ) > 0 {
return ErrFolderHasConnectedLibraryElements
}
var elementIDs [ ] struct {
ID int64 ` xorm:"id" `
}
2021-09-27 02:04:36 -05:00
err = session . SQL ( "SELECT id from library_element WHERE folder_id=? AND org_id=?" , folderID , signedInUser . OrgId ) . Find ( & elementIDs )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
for _ , elementID := range elementIDs {
2021-05-23 23:11:01 -05:00
_ , err := session . Exec ( "DELETE FROM " + models . LibraryElementConnectionTableName + " WHERE element_id=?" , elementID . ID )
2021-05-11 00:10:19 -05:00
if err != nil {
return err
}
}
2021-09-27 02:04:36 -05:00
if _ , err := session . Exec ( "DELETE FROM library_element WHERE folder_id=? AND org_id=?" , folderID , signedInUser . OrgId ) ; err != nil {
2021-05-11 00:10:19 -05:00
return err
}
return nil
} )
}