2018-01-29 07:51:01 -05:00
package api
import (
2020-07-23 02:15:47 -04:00
"errors"
2021-11-29 04:18:01 -05:00
"net/http"
2022-01-14 11:55:57 -05:00
"strconv"
2026-04-10 13:44:30 -04:00
"strings"
2024-09-10 05:22:08 -04:00
2021-08-25 09:11:22 -04:00
"github.com/grafana/grafana/pkg/api/apierrors"
2018-01-29 07:51:01 -05:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 08:43:20 -05:00
"github.com/grafana/grafana/pkg/api/response"
2024-09-25 02:56:15 -04:00
"github.com/grafana/grafana/pkg/api/routing"
2024-01-24 06:39:11 -05:00
"github.com/grafana/grafana/pkg/infra/metrics"
2023-01-30 09:19:42 -05:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
2023-01-27 02:50:36 -05:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2023-11-22 08:20:22 -05:00
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
2022-11-10 03:42:32 -05:00
"github.com/grafana/grafana/pkg/services/folder"
2023-02-01 11:32:05 -05:00
"github.com/grafana/grafana/pkg/services/libraryelements/model"
2023-08-24 12:10:58 -04:00
"github.com/grafana/grafana/pkg/util"
2021-10-11 08:30:59 -04:00
"github.com/grafana/grafana/pkg/web"
2018-01-29 07:51:01 -05:00
)
2023-09-08 03:43:41 -04:00
const REDACTED = "redacted"
2024-09-25 02:56:15 -04:00
func ( hs * HTTPServer ) registerFolderAPI ( apiRoute routing . RouteRegister , authorize func ( accesscontrol . Evaluator ) web . Handler ) {
// #TODO add back auth part
apiRoute . Group ( "/folders" , func ( folderRoute routing . RouteRegister ) {
2026-04-07 14:00:45 -04:00
idScope := folder . ScopeFoldersProvider . GetResourceScope ( accesscontrol . Parameter ( ":id" ) )
uidScope := folder . ScopeFoldersProvider . GetResourceScopeUID ( accesscontrol . Parameter ( ":uid" ) )
folderRoute . Get ( "/id/:id" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersRead , idScope ) ) , routing . Wrap ( hs . GetFolderByID ) )
2024-10-04 05:26:36 -04:00
folderRoute . Group ( "/:uid" , func ( folderUidRoute routing . RouteRegister ) {
folderUidRoute . Group ( "/permissions" , func ( folderPermissionRoute routing . RouteRegister ) {
2026-04-07 14:00:45 -04:00
folderPermissionRoute . Get ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersPermissionsRead , uidScope ) ) , routing . Wrap ( hs . GetFolderPermissionList ) )
folderPermissionRoute . Post ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersPermissionsWrite , uidScope ) ) , routing . Wrap ( hs . UpdateFolderPermissions ) )
2024-10-04 05:26:36 -04:00
} )
} )
2025-01-23 09:25:03 -05:00
2026-04-07 14:00:45 -04:00
folderRoute . Post ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersCreate ) ) , routing . Wrap ( hs . CreateFolder ) )
folderRoute . Get ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersRead ) ) , routing . Wrap ( hs . GetFolders ) )
2025-01-23 09:25:03 -05:00
folderRoute . Group ( "/:uid" , func ( folderUidRoute routing . RouteRegister ) {
2026-04-07 14:00:45 -04:00
folderUidRoute . Put ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersWrite , uidScope ) ) , routing . Wrap ( hs . UpdateFolder ) )
folderUidRoute . Delete ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersDelete , uidScope ) ) , routing . Wrap ( hs . DeleteFolder ) )
folderUidRoute . Get ( "/" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersRead , uidScope ) ) , routing . Wrap ( hs . GetFolderByUID ) )
folderUidRoute . Get ( "/counts" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersRead , uidScope ) ) , routing . Wrap ( hs . GetFolderDescendantCounts ) )
folderUidRoute . Post ( "/move" , authorize ( accesscontrol . EvalPermission ( folder . ActionFoldersWrite , uidScope ) ) , routing . Wrap ( hs . MoveFolder ) )
2025-01-23 09:25:03 -05:00
} )
2024-09-25 02:56:15 -04:00
} )
}
2022-07-27 09:54:37 -04:00
// swagger:route GET /folders folders getFolders
//
// Get all folders.
//
2024-03-07 05:07:35 -05:00
// It returns all folders that the authenticated user has permission to view.
2023-01-23 07:09:09 -05:00
// If nested folders are enabled, it expects an additional query parameter with the parent folder UID
2023-04-27 10:24:47 -04:00
// and returns the immediate subfolders that the authenticated user has permission to view.
// If the parameter is not supplied then it returns immediate subfolders under the root
// that the authenticated user has permission to view.
2022-07-27 09:54:37 -04:00
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders
//
// Deprecated: true
//
2022-07-27 09:54:37 -04:00
// Responses:
// 200: getFoldersResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 02:50:36 -05:00
func ( hs * HTTPServer ) GetFolders ( c * contextmodel . ReqContext ) response . Response {
2024-03-15 08:05:27 -04:00
permission := dashboardaccess . PERMISSION_VIEW
2026-04-10 13:44:30 -04:00
if strings . EqualFold ( c . Query ( "permission" ) , "edit" ) {
2024-03-15 08:05:27 -04:00
permission = dashboardaccess . PERMISSION_EDIT
}
2025-08-06 03:07:23 -04:00
q := & folder . GetChildrenQuery {
OrgID : c . GetOrgID ( ) ,
Limit : c . QueryInt64 ( "limit" ) ,
Page : c . QueryInt64 ( "page" ) ,
UID : c . Query ( "parentUid" ) ,
Permission : permission ,
SignedInUser : c . SignedInUser ,
2023-01-23 07:09:09 -05:00
}
2018-01-29 07:51:01 -05:00
2025-08-06 03:07:23 -04:00
folders , err := hs . folderService . GetChildren ( c . Req . Context ( ) , q )
2018-01-29 07:51:01 -05:00
if err != nil {
2021-08-25 09:11:22 -04:00
return apierrors . ToFolderErrorResponse ( err )
2018-01-29 07:51:01 -05:00
}
2025-08-06 03:07:23 -04:00
hits := make ( [ ] dtos . FolderSearchHit , 0 )
for _ , f := range folders {
hits = append ( hits , dtos . FolderSearchHit {
ID : f . ID , // nolint:staticcheck
UID : f . UID ,
Title : f . Title ,
ParentUID : f . ParentUID ,
ManagedBy : f . ManagedBy ,
} )
metrics . MFolderIDsAPICount . WithLabelValues ( metrics . GetFolders ) . Inc ( )
}
2024-01-16 06:35:10 -05:00
return response . JSON ( http . StatusOK , hits )
2018-01-29 07:51:01 -05:00
}
2022-07-27 09:54:37 -04:00
// swagger:route GET /folders/{folder_uid} folders getFolderByUID
//
// Get folder by uid.
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders/{folder_uid}
//
// Deprecated: true
//
2022-07-27 09:54:37 -04:00
// Responses:
// 200: folderResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 02:50:36 -05:00
func ( hs * HTTPServer ) GetFolderByUID ( c * contextmodel . ReqContext ) response . Response {
2022-11-11 08:28:24 -05:00
uid := web . Params ( c . Req ) [ ":uid" ]
2025-04-10 08:42:23 -04:00
folder , err := hs . folderService . Get ( c . Req . Context ( ) , & folder . GetFolderQuery { OrgID : c . GetOrgID ( ) , UID : & uid , SignedInUser : c . SignedInUser } )
2018-02-20 07:57:32 -05:00
if err != nil {
2021-08-25 09:11:22 -04:00
return apierrors . ToFolderErrorResponse ( err )
2018-01-29 07:51:01 -05:00
}
2023-09-08 03:43:41 -04:00
folderDTO , err := hs . newToFolderDto ( c , folder )
2022-12-15 09:34:17 -05:00
if err != nil {
return response . Err ( err )
}
2023-09-08 03:43:41 -04:00
return response . JSON ( http . StatusOK , folderDTO )
2018-02-20 07:57:32 -05:00
}
2018-01-29 07:51:01 -05:00
2022-07-27 09:54:37 -04:00
// Get folder by id.
//
2023-04-25 03:33:47 -04:00
// Returns the folder identified by id. This is deprecated.
// Please refer to [updated API](#/folders/getFolderByUID) instead
//
// Deprecated: true
2023-01-27 02:50:36 -05:00
func ( hs * HTTPServer ) GetFolderByID ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 11:55:57 -05:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2024-01-25 06:14:18 -05:00
metrics . MFolderIDsAPICount . WithLabelValues ( metrics . GetFolderByID ) . Inc ( )
2023-11-15 10:30:00 -05:00
// nolint:staticcheck
2023-10-06 05:34:36 -04:00
folder , err := hs . folderService . Get ( c . Req . Context ( ) , & folder . GetFolderQuery { ID : & id , OrgID : c . SignedInUser . GetOrgID ( ) , SignedInUser : c . SignedInUser } )
2018-01-29 07:51:01 -05:00
if err != nil {
2021-08-25 09:11:22 -04:00
return apierrors . ToFolderErrorResponse ( err )
2018-01-29 07:51:01 -05:00
}
2023-09-08 03:43:41 -04:00
folderDTO , err := hs . newToFolderDto ( c , folder )
2022-12-15 09:34:17 -05:00
if err != nil {
return response . Err ( err )
}
2023-09-08 03:43:41 -04:00
return response . JSON ( http . StatusOK , folderDTO )
2018-02-20 07:57:32 -05:00
}
2018-01-29 07:51:01 -05:00
2022-07-27 09:54:37 -04:00
// swagger:route POST /folders folders createFolder
//
// Create folder.
//
2022-11-10 04:41:03 -05:00
// If nested folders are enabled then it additionally expects the parent folder UID.
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders/{folder_uid}
//
// Deprecated: true
//
2022-07-27 09:54:37 -04:00
// Responses:
// 200: folderResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 409: conflictError
// 500: internalServerError
2023-01-27 02:50:36 -05:00
func ( hs * HTTPServer ) CreateFolder ( c * contextmodel . ReqContext ) response . Response {
2022-11-10 04:41:03 -05:00
cmd := folder . CreateFolderCommand { }
2021-11-29 04:18:01 -05:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2025-04-10 08:42:23 -04:00
cmd . OrgID = c . GetOrgID ( )
2022-11-23 04:13:47 -05:00
cmd . SignedInUser = c . SignedInUser
2022-11-10 04:41:03 -05:00
folder , err := hs . folderService . Create ( c . Req . Context ( ) , & cmd )
2018-01-29 07:51:01 -05:00
if err != nil {
2021-08-25 09:11:22 -04:00
return apierrors . ToFolderErrorResponse ( err )
2018-01-29 07:51:01 -05:00
}
2023-09-08 03:43:41 -04:00
folderDTO , err := hs . newToFolderDto ( c , folder )
2022-12-15 09:34:17 -05:00
if err != nil {
return response . Err ( err )
}
2022-11-10 04:41:03 -05:00
// TODO set ParentUID if nested folders are enabled
2023-09-08 03:43:41 -04:00
return response . JSON ( http . StatusOK , folderDTO )
2018-01-29 07:51:01 -05:00
}
2023-03-30 04:46:11 -04:00
// swagger:route POST /folders/{folder_uid}/move folders moveFolder
//
// Move folder.
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders/{folder_uid},
// Changing the parent folder annotation
//
// Deprecated: true
//
2023-03-30 04:46:11 -04:00
// Responses:
// 200: folderResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 02:50:36 -05:00
func ( hs * HTTPServer ) MoveFolder ( c * contextmodel . ReqContext ) response . Response {
2025-08-06 03:07:23 -04:00
cmd := folder . MoveFolderCommand { }
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
var err error
2023-03-20 07:04:22 -04:00
2025-08-06 03:07:23 -04:00
cmd . OrgID = c . GetOrgID ( )
cmd . UID = web . Params ( c . Req ) [ ":uid" ]
cmd . SignedInUser = c . SignedInUser
theFolder , err := hs . folderService . Move ( c . Req . Context ( ) , & cmd )
if err != nil {
2025-12-19 15:17:39 -05:00
return apierrors . ToFolderErrorResponse ( err )
2025-08-06 03:07:23 -04:00
}
2023-03-30 04:46:11 -04:00
2025-08-06 03:07:23 -04:00
folderDTO , err := hs . newToFolderDto ( c , theFolder )
if err != nil {
return response . Err ( err )
2022-11-10 09:06:52 -05:00
}
2025-08-06 03:07:23 -04:00
return response . JSON ( http . StatusOK , folderDTO )
2022-11-10 09:06:52 -05:00
}
2022-07-27 09:54:37 -04:00
// swagger:route PUT /folders/{folder_uid} folders updateFolder
//
// Update folder.
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders/{folder_uid}
//
// Deprecated: true
//
2022-07-27 09:54:37 -04:00
// Responses:
// 200: folderResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 409: conflictError
// 500: internalServerError
2023-01-27 02:50:36 -05:00
func ( hs * HTTPServer ) UpdateFolder ( c * contextmodel . ReqContext ) response . Response {
2022-12-20 08:00:33 -05:00
cmd := folder . UpdateFolderCommand { }
2021-11-29 04:18:01 -05:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-12-20 08:00:33 -05:00
2025-04-10 08:42:23 -04:00
cmd . OrgID = c . GetOrgID ( )
2022-12-20 08:00:33 -05:00
cmd . UID = web . Params ( c . Req ) [ ":uid" ]
cmd . SignedInUser = c . SignedInUser
result , err := hs . folderService . Update ( c . Req . Context ( ) , & cmd )
2018-01-29 07:51:01 -05:00
if err != nil {
2021-08-25 09:11:22 -04:00
return apierrors . ToFolderErrorResponse ( err )
2018-01-29 07:51:01 -05:00
}
2023-09-08 03:43:41 -04:00
folderDTO , err := hs . newToFolderDto ( c , result )
2022-12-15 09:34:17 -05:00
if err != nil {
return response . Err ( err )
}
2023-09-08 03:43:41 -04:00
return response . JSON ( http . StatusOK , folderDTO )
2018-01-29 07:51:01 -05:00
}
2022-07-27 09:54:37 -04:00
// swagger:route DELETE /folders/{folder_uid} folders deleteFolder
//
// Delete folder.
//
// Deletes an existing folder identified by UID along with all dashboards (and their alerts) stored in the folder. This operation cannot be reverted.
2022-11-10 04:41:03 -05:00
// If nested folders are enabled then it also deletes all the subfolders.
2022-07-27 09:54:37 -04:00
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders/{folder_uid}
//
// Deprecated: true
//
2022-07-27 09:54:37 -04:00
// Responses:
// 200: deleteFolderResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2025-11-06 07:56:32 -05:00
func ( hs * HTTPServer ) DeleteFolder ( c * contextmodel . ReqContext ) response . Response {
uid := web . Params ( c . Req ) [ ":uid" ]
err := hs . folderService . Delete ( c . Req . Context ( ) , & folder . DeleteFolderCommand { UID : uid , OrgID : c . GetOrgID ( ) , ForceDeleteRules : c . QueryBool ( "forceDeleteRules" ) , SignedInUser : c . SignedInUser } )
2021-05-12 02:48:17 -04:00
if err != nil {
2023-02-01 11:32:05 -05:00
if errors . Is ( err , model . ErrFolderHasConnectedLibraryElements ) {
2024-02-27 11:39:51 -05:00
return response . Error ( http . StatusForbidden , "Folder could not be deleted because it contains library elements in use" , err )
2021-03-02 04:34:01 -05:00
}
2021-08-25 09:11:22 -04:00
return apierrors . ToFolderErrorResponse ( err )
2021-03-02 04:34:01 -05:00
}
2018-01-29 07:51:01 -05:00
2023-08-24 12:10:58 -04:00
return response . JSON ( http . StatusOK , util . DynMap {
"message" : "Folder deleted" ,
} )
2018-01-29 07:51:01 -05:00
}
2023-04-27 11:00:09 -04:00
// swagger:route GET /folders/{folder_uid}/counts folders getFolderDescendantCounts
2023-04-24 09:57:28 -04:00
//
// Gets the count of each descendant of a folder by kind. The folder is identified by UID.
//
2026-03-26 14:34:20 -04:00
// Use: /apis/folder.grafana.app/v1/namespaces/{ns}/folders/{folder_uid}
//
// Deprecated: true
//
2023-04-24 09:57:28 -04:00
// Responses:
2023-04-27 11:00:09 -04:00
// 200: getFolderDescendantCountsResponse
2023-04-24 09:57:28 -04:00
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-04-27 11:00:09 -04:00
func ( hs * HTTPServer ) GetFolderDescendantCounts ( c * contextmodel . ReqContext ) response . Response {
2023-04-24 09:57:28 -04:00
uid := web . Params ( c . Req ) [ ":uid" ]
2025-04-10 08:42:23 -04:00
counts , err := hs . folderService . GetDescendantCounts ( c . Req . Context ( ) , & folder . GetDescendantCountsQuery { OrgID : c . GetOrgID ( ) , UID : & uid , SignedInUser : c . SignedInUser } )
2023-04-24 09:57:28 -04:00
if err != nil {
return apierrors . ToFolderErrorResponse ( err )
}
return response . JSON ( http . StatusOK , counts )
}
2023-09-08 03:43:41 -04:00
func ( hs * HTTPServer ) newToFolderDto ( c * contextmodel . ReqContext , f * folder . Folder ) ( dtos . Folder , error ) {
2023-04-25 04:22:20 -04:00
ctx := c . Req . Context ( )
2023-09-08 03:43:41 -04:00
toDTO := func ( f * folder . Folder , checkCanView bool ) ( dtos . Folder , error ) {
2026-04-07 14:00:45 -04:00
canEditEvaluator := accesscontrol . EvalPermission ( folder . ActionFoldersWrite , folder . ScopeFoldersProvider . GetResourceScopeUID ( f . UID ) )
2025-05-16 17:25:07 -04:00
canEdit , _ := hs . AccessControl . Evaluate ( ctx , c . SignedInUser , canEditEvaluator )
canSave := canEdit
canAdminEvaluator := accesscontrol . EvalAll (
2026-04-07 14:00:45 -04:00
accesscontrol . EvalPermission ( folder . ActionFoldersPermissionsRead , folder . ScopeFoldersProvider . GetResourceScopeUID ( f . UID ) ) ,
accesscontrol . EvalPermission ( folder . ActionFoldersPermissionsWrite , folder . ScopeFoldersProvider . GetResourceScopeUID ( f . UID ) ) ,
2025-05-16 17:25:07 -04:00
)
canAdmin , _ := hs . AccessControl . Evaluate ( ctx , c . SignedInUser , canAdminEvaluator )
2026-04-07 14:00:45 -04:00
canDeleteEvaluator := accesscontrol . EvalPermission ( folder . ActionFoldersDelete , folder . ScopeFoldersProvider . GetResourceScopeUID ( f . UID ) )
2025-05-16 17:25:07 -04:00
canDelete , _ := hs . AccessControl . Evaluate ( ctx , c . SignedInUser , canDeleteEvaluator )
2023-04-25 04:22:20 -04:00
// Finding creator and last updater of the folder
updater , creator := anonString , anonString
if f . CreatedBy > 0 {
2025-01-10 04:06:59 -05:00
creator = hs . getIdentityName ( ctx , f . OrgID , f . CreatedBy )
2023-04-25 04:22:20 -04:00
}
if f . UpdatedBy > 0 {
2025-01-10 04:06:59 -05:00
updater = hs . getIdentityName ( ctx , f . OrgID , f . UpdatedBy )
2023-04-25 04:22:20 -04:00
}
acMetadata , _ := hs . getFolderACMetadata ( c , f )
2023-09-08 03:43:41 -04:00
if checkCanView {
2026-04-07 14:00:45 -04:00
canViewEvaluator := accesscontrol . EvalPermission ( folder . ActionFoldersRead , folder . ScopeFoldersProvider . GetResourceScopeUID ( f . UID ) )
2025-05-16 17:25:07 -04:00
canView , _ := hs . AccessControl . Evaluate ( ctx , c . SignedInUser , canViewEvaluator )
2023-09-08 03:43:41 -04:00
if ! canView {
return dtos . Folder {
2023-12-07 07:15:58 -05:00
UID : REDACTED ,
2023-09-08 03:43:41 -04:00
Title : REDACTED ,
} , nil
}
}
2024-01-25 06:14:18 -05:00
metrics . MFolderIDsAPICount . WithLabelValues ( metrics . NewToFolderDTO ) . Inc ( )
2023-04-25 04:22:20 -04:00
return dtos . Folder {
2023-12-07 07:15:58 -05:00
ID : f . ID , // nolint:staticcheck
UID : f . UID ,
2025-01-28 21:13:26 -05:00
OrgID : f . OrgID ,
2023-04-25 04:22:20 -04:00
Title : f . Title ,
2023-12-07 07:15:58 -05:00
URL : f . URL ,
2023-04-25 04:22:20 -04:00
HasACL : f . HasACL ,
CanSave : canSave ,
CanEdit : canEdit ,
CanAdmin : canAdmin ,
CanDelete : canDelete ,
CreatedBy : creator ,
Created : f . Created ,
UpdatedBy : updater ,
Updated : f . Updated ,
Version : f . Version ,
AccessControl : acMetadata ,
ParentUID : f . ParentUID ,
2025-03-05 01:54:20 -05:00
ManagedBy : f . ManagedBy ,
2023-09-08 03:43:41 -04:00
} , nil
2023-04-25 04:22:20 -04:00
}
2023-04-24 09:57:28 -04:00
2023-09-08 03:43:41 -04:00
// no need to check view permission for the starting folder since it's already checked by the callers
folderDTO , err := toDTO ( f , false )
if err != nil {
return dtos . Folder { } , err
}
2022-11-10 04:41:03 -05:00
2023-04-25 04:22:20 -04:00
parents , err := hs . folderService . GetParents ( ctx , folder . GetParentsQuery { UID : f . UID , OrgID : f . OrgID } )
if err != nil {
// log the error instead of failing
hs . log . Error ( "failed to fetch folder parents" , "folder" , f . UID , "org" , f . OrgID , "error" , err )
2022-11-15 05:58:12 -05:00
}
2022-11-10 04:41:03 -05:00
2023-04-25 04:22:20 -04:00
folderDTO . Parents = make ( [ ] dtos . Folder , 0 , len ( parents ) )
for _ , f := range parents {
2023-09-08 03:43:41 -04:00
DTO , err := toDTO ( f , true )
if err != nil {
hs . log . Error ( "failed to convert folder to DTO" , "folder" , f . UID , "org" , f . OrgID , "error" , err )
continue
}
folderDTO . Parents = append ( folderDTO . Parents , DTO )
2022-11-10 04:41:03 -05:00
}
2023-04-25 04:22:20 -04:00
2023-09-08 03:43:41 -04:00
return folderDTO , nil
2022-11-10 04:41:03 -05:00
}
2023-04-21 10:05:11 -04:00
func ( hs * HTTPServer ) getFolderACMetadata ( c * contextmodel . ReqContext , f * folder . Folder ) ( accesscontrol . Metadata , error ) {
2023-07-10 08:14:21 -04:00
if ! c . QueryBool ( "accesscontrol" ) {
2023-04-21 10:05:11 -04:00
return nil , nil
}
2025-04-10 08:42:23 -04:00
parents , err := hs . folderService . GetParents ( c . Req . Context ( ) , folder . GetParentsQuery { UID : f . UID , OrgID : c . GetOrgID ( ) } )
2023-04-21 10:05:11 -04:00
if err != nil {
return nil , err
}
folderIDs := map [ string ] bool { f . UID : true }
for _ , p := range parents {
folderIDs [ p . UID ] = true
}
2026-04-07 14:00:45 -04:00
allMetadata := getMultiAccessControlMetadata ( c , folder . ScopeFoldersPrefix , folderIDs )
2023-11-28 04:28:47 -05:00
metadata := map [ string ] bool { }
2023-04-21 10:05:11 -04:00
// Flatten metadata - if any parent has a permission, the child folder inherits it
for _ , md := range allMetadata {
for action := range md {
metadata [ action ] = true
}
}
return metadata , nil
}
2022-07-27 09:54:37 -04:00
// swagger:parameters getFolders
type GetFoldersParams struct {
// Limit the maximum number of folders to return
// in:query
// required:false
// default:1000
Limit int64 ` json:"limit" `
// Page index for starting fetching folders
// in:query
// required:false
// default:1
Page int64 ` json:"page" `
2022-12-19 03:52:04 -05:00
// The parent folder UID
// in:query
// required:false
2023-01-24 03:20:28 -05:00
ParentUID string ` json:"parentUid" `
2024-03-15 08:05:27 -04:00
// Set to `Edit` to return folders that the user can edit
// in:query
// required: false
// default:View
// Enum: Edit,View
Permission string ` json:"permission" `
2022-07-27 09:54:37 -04:00
}
// swagger:parameters getFolderByUID
type GetFolderByUIDParams struct {
// in:path
// required:true
FolderUID string ` json:"folder_uid" `
}
// swagger:parameters updateFolder
type UpdateFolderParams struct {
// in:path
// required:true
FolderUID string ` json:"folder_uid" `
// To change the unique identifier (uid), provide another one.
// To overwrite an existing folder with newer version, set `overwrite` to `true`.
// Provide the current version to safelly update the folder: if the provided version differs from the stored one the request will fail, unless `overwrite` is `true`.
//
// in:body
// required:true
2022-12-20 08:00:33 -05:00
Body folder . UpdateFolderCommand ` json:"body" `
2022-07-27 09:54:37 -04:00
}
// swagger:parameters getFolderByID
type GetFolderByIDParams struct {
// in:path
// required:true
2023-11-16 09:57:04 -05:00
//
// Deprecated: use FolderUID instead
2022-07-27 09:54:37 -04:00
FolderID int64 ` json:"folder_id" `
}
// swagger:parameters createFolder
type CreateFolderParams struct {
// in:body
// required:true
2022-11-10 04:41:03 -05:00
Body folder . CreateFolderCommand ` json:"body" `
2022-07-27 09:54:37 -04:00
}
2023-03-30 04:46:11 -04:00
// swagger:parameters moveFolder
type MoveFolderParams struct {
// in:path
// required:true
FolderUID string ` json:"folder_uid" `
// in:body
// required:true
Body folder . MoveFolderCommand ` json:"body" `
}
2022-07-27 09:54:37 -04:00
// swagger:parameters deleteFolder
type DeleteFolderParams struct {
// in:path
// required:true
FolderUID string ` json:"folder_uid" `
// If `true` any Grafana 8 Alerts under this folder will be deleted.
// Set to `false` so that the request will fail if the folder contains any Grafana 8 Alerts.
// in:query
// required:false
// default:false
ForceDeleteRules bool ` json:"forceDeleteRules" `
}
// swagger:response getFoldersResponse
type GetFoldersResponse struct {
// The response message
// in: body
Body [ ] dtos . FolderSearchHit ` json:"body" `
}
// swagger:response folderResponse
type FolderResponse struct {
// The response message
// in: body
Body dtos . Folder ` json:"body" `
}
// swagger:response deleteFolderResponse
type DeleteFolderResponse struct {
// The response message
// in: body
Body struct {
// ID Identifier of the deleted folder.
// required: true
// example: 65
ID int64 ` json:"id" `
// Title of the deleted folder.
// required: true
// example: My Folder
Title string ` json:"title" `
// Message Message of the deleted folder.
// required: true
// example: Folder My Folder deleted
Message string ` json:"message" `
} ` json:"body" `
}
2023-04-24 09:57:28 -04:00
2023-04-27 11:00:09 -04:00
// swagger:parameters getFolderDescendantCounts
type GetFolderDescendantCountsParams struct {
2023-04-24 09:57:28 -04:00
// in:path
// required:true
FolderUID string ` json:"folder_uid" `
}
2023-04-27 11:00:09 -04:00
// swagger:response getFolderDescendantCountsResponse
type GetFolderDescendantCountsResponse struct {
2023-04-24 09:57:28 -04:00
// The response message
// in: body
2023-04-27 11:00:09 -04:00
Body folder . DescendantCounts ` json:"body" `
2023-04-24 09:57:28 -04:00
}