mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			598 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			598 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
package api
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"net/http"
 | 
						|
	"strconv"
 | 
						|
 | 
						|
	"github.com/grafana/grafana/pkg/api/apierrors"
 | 
						|
	"github.com/grafana/grafana/pkg/api/dtos"
 | 
						|
	"github.com/grafana/grafana/pkg/api/response"
 | 
						|
	"github.com/grafana/grafana/pkg/services/accesscontrol"
 | 
						|
	"github.com/grafana/grafana/pkg/services/auth/identity"
 | 
						|
	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
 | 
						|
	"github.com/grafana/grafana/pkg/services/dashboards"
 | 
						|
	"github.com/grafana/grafana/pkg/services/featuremgmt"
 | 
						|
	"github.com/grafana/grafana/pkg/services/folder"
 | 
						|
	"github.com/grafana/grafana/pkg/services/guardian"
 | 
						|
	"github.com/grafana/grafana/pkg/services/libraryelements/model"
 | 
						|
	"github.com/grafana/grafana/pkg/services/org"
 | 
						|
	"github.com/grafana/grafana/pkg/services/search"
 | 
						|
	"github.com/grafana/grafana/pkg/util"
 | 
						|
	"github.com/grafana/grafana/pkg/web"
 | 
						|
)
 | 
						|
 | 
						|
const REDACTED = "redacted"
 | 
						|
 | 
						|
// swagger:route GET /folders folders getFolders
 | 
						|
//
 | 
						|
// Get all folders.
 | 
						|
//
 | 
						|
// Returns all folders that the authenticated user has permission to view.
 | 
						|
// If nested folders are enabled, it expects an additional query parameter with the parent folder UID
 | 
						|
// 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.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: getFoldersResponse
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) GetFolders(c *contextmodel.ReqContext) response.Response {
 | 
						|
	var folders []*folder.Folder
 | 
						|
	var err error
 | 
						|
	if hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
 | 
						|
		folders, err = hs.folderService.GetChildren(c.Req.Context(), &folder.GetChildrenQuery{
 | 
						|
			OrgID:        c.SignedInUser.GetOrgID(),
 | 
						|
			Limit:        c.QueryInt64("limit"),
 | 
						|
			Page:         c.QueryInt64("page"),
 | 
						|
			UID:          c.Query("parentUid"),
 | 
						|
			SignedInUser: c.SignedInUser,
 | 
						|
		})
 | 
						|
	} else {
 | 
						|
		folders, err = hs.searchFolders(c)
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
 | 
						|
	result := make([]dtos.FolderSearchHit, 0)
 | 
						|
	for _, f := range folders {
 | 
						|
		result = append(result, dtos.FolderSearchHit{
 | 
						|
			Id:        f.ID,
 | 
						|
			Uid:       f.UID,
 | 
						|
			Title:     f.Title,
 | 
						|
			ParentUID: f.ParentUID,
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return response.JSON(http.StatusOK, result)
 | 
						|
}
 | 
						|
 | 
						|
// swagger:route GET /folders/{folder_uid} folders getFolderByUID
 | 
						|
//
 | 
						|
// Get folder by uid.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: folderResponse
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 404: notFoundError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) GetFolderByUID(c *contextmodel.ReqContext) response.Response {
 | 
						|
	uid := web.Params(c.Req)[":uid"]
 | 
						|
	folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.SignedInUser.GetOrgID(), UID: &uid, SignedInUser: c.SignedInUser})
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
 | 
						|
	folderDTO, err := hs.newToFolderDto(c, folder)
 | 
						|
	if err != nil {
 | 
						|
		return response.Err(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return response.JSON(http.StatusOK, folderDTO)
 | 
						|
}
 | 
						|
 | 
						|
// swagger:route GET /folders/id/{folder_id} folders getFolderByID
 | 
						|
//
 | 
						|
// Get folder by id.
 | 
						|
//
 | 
						|
// Returns the folder identified by id. This is deprecated.
 | 
						|
// Please refer to [updated API](#/folders/getFolderByUID) instead
 | 
						|
//
 | 
						|
// Deprecated: true
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: folderResponse
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 404: notFoundError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) GetFolderByID(c *contextmodel.ReqContext) response.Response {
 | 
						|
	id, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64)
 | 
						|
	if err != nil {
 | 
						|
		return response.Error(http.StatusBadRequest, "id is invalid", err)
 | 
						|
	}
 | 
						|
	folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{ID: &id, OrgID: c.SignedInUser.GetOrgID(), SignedInUser: c.SignedInUser})
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
 | 
						|
	folderDTO, err := hs.newToFolderDto(c, folder)
 | 
						|
	if err != nil {
 | 
						|
		return response.Err(err)
 | 
						|
	}
 | 
						|
	return response.JSON(http.StatusOK, folderDTO)
 | 
						|
}
 | 
						|
 | 
						|
// swagger:route POST /folders folders createFolder
 | 
						|
//
 | 
						|
// Create folder.
 | 
						|
//
 | 
						|
// If nested folders are enabled then it additionally expects the parent folder UID.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: folderResponse
 | 
						|
// 400: badRequestError
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 409: conflictError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response {
 | 
						|
	cmd := folder.CreateFolderCommand{}
 | 
						|
	if err := web.Bind(c.Req, &cmd); err != nil {
 | 
						|
		return response.Error(http.StatusBadRequest, "bad request data", err)
 | 
						|
	}
 | 
						|
	cmd.OrgID = c.SignedInUser.GetOrgID()
 | 
						|
	cmd.SignedInUser = c.SignedInUser
 | 
						|
 | 
						|
	folder, err := hs.folderService.Create(c.Req.Context(), &cmd)
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil {
 | 
						|
		hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call
 | 
						|
	// Required for cases when caller wants to immediately interact with the newly created object
 | 
						|
	hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
 | 
						|
 | 
						|
	folderDTO, err := hs.newToFolderDto(c, folder)
 | 
						|
	if err != nil {
 | 
						|
		return response.Err(err)
 | 
						|
	}
 | 
						|
 | 
						|
	// TODO set ParentUID if nested folders are enabled
 | 
						|
	return response.JSON(http.StatusOK, folderDTO)
 | 
						|
}
 | 
						|
 | 
						|
func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error {
 | 
						|
	var permissions []accesscontrol.SetResourcePermissionCommand
 | 
						|
	var userID int64
 | 
						|
 | 
						|
	namespace, id := user.GetNamespacedID()
 | 
						|
	if namespace == identity.NamespaceUser {
 | 
						|
		var errID error
 | 
						|
		userID, errID = identity.IntIdentifier(namespace, id)
 | 
						|
		if errID != nil {
 | 
						|
			return errID
 | 
						|
		}
 | 
						|
 | 
						|
		permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
 | 
						|
			UserID: userID, Permission: dashboards.PERMISSION_ADMIN.String(),
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	isNested := folder.ParentUID != ""
 | 
						|
	if !isNested || !hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
 | 
						|
		permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
 | 
						|
			{BuiltinRole: string(org.RoleEditor), Permission: dashboards.PERMISSION_EDIT.String()},
 | 
						|
			{BuiltinRole: string(org.RoleViewer), Permission: dashboards.PERMISSION_VIEW.String()},
 | 
						|
		}...)
 | 
						|
	}
 | 
						|
 | 
						|
	_, err := hs.folderPermissionsService.SetPermissions(ctx, orgID, folder.UID, permissions...)
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// swagger:route POST /folders/{folder_uid}/move folders moveFolder
 | 
						|
//
 | 
						|
// Move folder.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: folderResponse
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 404: notFoundError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) MoveFolder(c *contextmodel.ReqContext) response.Response {
 | 
						|
	if hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
 | 
						|
		cmd := folder.MoveFolderCommand{}
 | 
						|
		if err := web.Bind(c.Req, &cmd); err != nil {
 | 
						|
			return response.Error(http.StatusBadRequest, "bad request data", err)
 | 
						|
		}
 | 
						|
		var err error
 | 
						|
 | 
						|
		cmd.OrgID = c.SignedInUser.GetOrgID()
 | 
						|
		cmd.UID = web.Params(c.Req)[":uid"]
 | 
						|
		cmd.SignedInUser = c.SignedInUser
 | 
						|
		theFolder, err := hs.folderService.Move(c.Req.Context(), &cmd)
 | 
						|
		if err != nil {
 | 
						|
			return response.ErrOrFallback(http.StatusInternalServerError, "move folder failed", err)
 | 
						|
		}
 | 
						|
 | 
						|
		folderDTO, err := hs.newToFolderDto(c, theFolder)
 | 
						|
		if err != nil {
 | 
						|
			return response.Err(err)
 | 
						|
		}
 | 
						|
		return response.JSON(http.StatusOK, folderDTO)
 | 
						|
	}
 | 
						|
	result := map[string]string{}
 | 
						|
	result["message"] = "To use this service, you need to activate nested folder feature."
 | 
						|
	return response.JSON(http.StatusNotFound, result)
 | 
						|
}
 | 
						|
 | 
						|
// swagger:route PUT /folders/{folder_uid} folders updateFolder
 | 
						|
//
 | 
						|
// Update folder.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: folderResponse
 | 
						|
// 400: badRequestError
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 404: notFoundError
 | 
						|
// 409: conflictError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) UpdateFolder(c *contextmodel.ReqContext) response.Response {
 | 
						|
	cmd := folder.UpdateFolderCommand{}
 | 
						|
	if err := web.Bind(c.Req, &cmd); err != nil {
 | 
						|
		return response.Error(http.StatusBadRequest, "bad request data", err)
 | 
						|
	}
 | 
						|
 | 
						|
	cmd.OrgID = c.SignedInUser.GetOrgID()
 | 
						|
	cmd.UID = web.Params(c.Req)[":uid"]
 | 
						|
	cmd.SignedInUser = c.SignedInUser
 | 
						|
	result, err := hs.folderService.Update(c.Req.Context(), &cmd)
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
	folderDTO, err := hs.newToFolderDto(c, result)
 | 
						|
	if err != nil {
 | 
						|
		return response.Err(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return response.JSON(http.StatusOK, folderDTO)
 | 
						|
}
 | 
						|
 | 
						|
// 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.
 | 
						|
// If nested folders are enabled then it also deletes all the subfolders.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: deleteFolderResponse
 | 
						|
// 400: badRequestError
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 404: notFoundError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) DeleteFolder(c *contextmodel.ReqContext) response.Response { // temporarily adding this function to HTTPServer, will be removed from HTTPServer when librarypanels featuretoggle is removed
 | 
						|
	err := hs.LibraryElementService.DeleteLibraryElementsInFolder(c.Req.Context(), c.SignedInUser, web.Params(c.Req)[":uid"])
 | 
						|
	if err != nil {
 | 
						|
		if errors.Is(err, model.ErrFolderHasConnectedLibraryElements) {
 | 
						|
			return response.Error(403, "Folder could not be deleted because it contains library elements in use", err)
 | 
						|
		}
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
	/* TODO: after a decision regarding folder deletion permissions has been made
 | 
						|
	(https://github.com/grafana/grafana-enterprise/issues/5144),
 | 
						|
	remove the previous call to hs.LibraryElementService.DeleteLibraryElementsInFolder
 | 
						|
	and remove "user" from the signature of DeleteInFolder in the folder RegistryService.
 | 
						|
	Context: https://github.com/grafana/grafana/pull/69149#discussion_r1235057903
 | 
						|
	*/
 | 
						|
 | 
						|
	uid := web.Params(c.Req)[":uid"]
 | 
						|
	err = hs.folderService.Delete(c.Req.Context(), &folder.DeleteFolderCommand{UID: uid, OrgID: c.SignedInUser.GetOrgID(), ForceDeleteRules: c.QueryBool("forceDeleteRules"), SignedInUser: c.SignedInUser})
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return response.JSON(http.StatusOK, util.DynMap{
 | 
						|
		"message": "Folder deleted",
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// swagger:route GET /folders/{folder_uid}/counts folders getFolderDescendantCounts
 | 
						|
//
 | 
						|
// Gets the count of each descendant of a folder by kind. The folder is identified by UID.
 | 
						|
//
 | 
						|
// Responses:
 | 
						|
// 200: getFolderDescendantCountsResponse
 | 
						|
// 401: unauthorisedError
 | 
						|
// 403: forbiddenError
 | 
						|
// 404: notFoundError
 | 
						|
// 500: internalServerError
 | 
						|
func (hs *HTTPServer) GetFolderDescendantCounts(c *contextmodel.ReqContext) response.Response {
 | 
						|
	uid := web.Params(c.Req)[":uid"]
 | 
						|
	counts, err := hs.folderService.GetDescendantCounts(c.Req.Context(), &folder.GetDescendantCountsQuery{OrgID: c.SignedInUser.GetOrgID(), UID: &uid, SignedInUser: c.SignedInUser})
 | 
						|
	if err != nil {
 | 
						|
		return apierrors.ToFolderErrorResponse(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return response.JSON(http.StatusOK, counts)
 | 
						|
}
 | 
						|
func (hs *HTTPServer) newToFolderDto(c *contextmodel.ReqContext, f *folder.Folder) (dtos.Folder, error) {
 | 
						|
	ctx := c.Req.Context()
 | 
						|
	toDTO := func(f *folder.Folder, checkCanView bool) (dtos.Folder, error) {
 | 
						|
		g, err := guardian.NewByFolder(c.Req.Context(), f, c.SignedInUser.GetOrgID(), c.SignedInUser)
 | 
						|
		if err != nil {
 | 
						|
			return dtos.Folder{}, err
 | 
						|
		}
 | 
						|
 | 
						|
		canEdit, _ := g.CanEdit()
 | 
						|
		canSave, _ := g.CanSave()
 | 
						|
		canAdmin, _ := g.CanAdmin()
 | 
						|
		canDelete, _ := g.CanDelete()
 | 
						|
 | 
						|
		// Finding creator and last updater of the folder
 | 
						|
		updater, creator := anonString, anonString
 | 
						|
		if f.CreatedBy > 0 {
 | 
						|
			creator = hs.getUserLogin(ctx, f.CreatedBy)
 | 
						|
		}
 | 
						|
		if f.UpdatedBy > 0 {
 | 
						|
			updater = hs.getUserLogin(ctx, f.UpdatedBy)
 | 
						|
		}
 | 
						|
 | 
						|
		acMetadata, _ := hs.getFolderACMetadata(c, f)
 | 
						|
 | 
						|
		if checkCanView {
 | 
						|
			canView, _ := g.CanView()
 | 
						|
			if !canView {
 | 
						|
				return dtos.Folder{
 | 
						|
					Uid:   REDACTED,
 | 
						|
					Title: REDACTED,
 | 
						|
				}, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return dtos.Folder{
 | 
						|
			Id:            f.ID,
 | 
						|
			Uid:           f.UID,
 | 
						|
			Title:         f.Title,
 | 
						|
			Url:           f.URL,
 | 
						|
			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,
 | 
						|
		}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	// 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
 | 
						|
	}
 | 
						|
 | 
						|
	if !hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
 | 
						|
		return folderDTO, nil
 | 
						|
	}
 | 
						|
 | 
						|
	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)
 | 
						|
	}
 | 
						|
 | 
						|
	folderDTO.Parents = make([]dtos.Folder, 0, len(parents))
 | 
						|
	for _, f := range parents {
 | 
						|
		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)
 | 
						|
	}
 | 
						|
 | 
						|
	return folderDTO, nil
 | 
						|
}
 | 
						|
 | 
						|
func (hs *HTTPServer) getFolderACMetadata(c *contextmodel.ReqContext, f *folder.Folder) (accesscontrol.Metadata, error) {
 | 
						|
	if !c.QueryBool("accesscontrol") {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	parents, err := hs.folderService.GetParents(c.Req.Context(), folder.GetParentsQuery{UID: f.UID, OrgID: c.SignedInUser.GetOrgID()})
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	folderIDs := map[string]bool{f.UID: true}
 | 
						|
	for _, p := range parents {
 | 
						|
		folderIDs[p.UID] = true
 | 
						|
	}
 | 
						|
 | 
						|
	allMetadata := hs.getMultiAccessControlMetadata(c, dashboards.ScopeFoldersPrefix, folderIDs)
 | 
						|
	metadata := allMetadata[f.UID]
 | 
						|
 | 
						|
	// 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
 | 
						|
}
 | 
						|
 | 
						|
func (hs *HTTPServer) searchFolders(c *contextmodel.ReqContext) ([]*folder.Folder, error) {
 | 
						|
	searchQuery := search.Query{
 | 
						|
		SignedInUser: c.SignedInUser,
 | 
						|
		DashboardIds: make([]int64, 0),
 | 
						|
		FolderIds:    make([]int64, 0),
 | 
						|
		Limit:        c.QueryInt64("limit"),
 | 
						|
		OrgId:        c.SignedInUser.GetOrgID(),
 | 
						|
		Type:         "dash-folder",
 | 
						|
		Permission:   dashboards.PERMISSION_VIEW,
 | 
						|
		Page:         c.QueryInt64("page"),
 | 
						|
	}
 | 
						|
 | 
						|
	hits, err := hs.SearchService.SearchHandler(c.Req.Context(), &searchQuery)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	folders := make([]*folder.Folder, 0)
 | 
						|
 | 
						|
	for _, hit := range hits {
 | 
						|
		folders = append(folders, &folder.Folder{
 | 
						|
			ID:    hit.ID,
 | 
						|
			UID:   hit.UID,
 | 
						|
			Title: hit.Title,
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return folders, nil
 | 
						|
}
 | 
						|
 | 
						|
// 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"`
 | 
						|
	// The parent folder UID
 | 
						|
	// in:query
 | 
						|
	// required:false
 | 
						|
	ParentUID string `json:"parentUid"`
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
	Body folder.UpdateFolderCommand `json:"body"`
 | 
						|
}
 | 
						|
 | 
						|
// swagger:parameters getFolderByID
 | 
						|
type GetFolderByIDParams struct {
 | 
						|
	// in:path
 | 
						|
	// required:true
 | 
						|
	FolderID int64 `json:"folder_id"`
 | 
						|
}
 | 
						|
 | 
						|
// swagger:parameters createFolder
 | 
						|
type CreateFolderParams struct {
 | 
						|
	// in:body
 | 
						|
	// required:true
 | 
						|
	Body folder.CreateFolderCommand `json:"body"`
 | 
						|
}
 | 
						|
 | 
						|
// swagger:parameters moveFolder
 | 
						|
type MoveFolderParams struct {
 | 
						|
	// in:path
 | 
						|
	// required:true
 | 
						|
	FolderUID string `json:"folder_uid"`
 | 
						|
	// in:body
 | 
						|
	// required:true
 | 
						|
	Body folder.MoveFolderCommand `json:"body"`
 | 
						|
}
 | 
						|
 | 
						|
// 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"`
 | 
						|
}
 | 
						|
 | 
						|
// swagger:parameters getFolderDescendantCounts
 | 
						|
type GetFolderDescendantCountsParams struct {
 | 
						|
	// in:path
 | 
						|
	// required:true
 | 
						|
	FolderUID string `json:"folder_uid"`
 | 
						|
}
 | 
						|
 | 
						|
// swagger:response getFolderDescendantCountsResponse
 | 
						|
type GetFolderDescendantCountsResponse struct {
 | 
						|
	// The response message
 | 
						|
	// in: body
 | 
						|
	Body folder.DescendantCounts `json:"body"`
 | 
						|
}
 |