mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 05:08:36 +08:00
Unified Storage /Folders: Allow Unified Storage subfolders creation (#94327)
* Add parents field to folder DTO * Allow subfolder creation when folder flag is enabled * Update UnstructuredToLegacyFolder * Include parents field when creating folder
This commit is contained in:
@ -635,6 +635,8 @@ type folderK8sHandler struct {
|
||||
// #TODO check if it makes more sense to move this to FolderAPIBuilder
|
||||
accesscontrolService accesscontrol.Service
|
||||
userService user.Service
|
||||
// #TODO remove after we handle the nested folder case
|
||||
folderService folder.Service
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
@ -648,6 +650,7 @@ func newFolderK8sHandler(hs *HTTPServer) *folderK8sHandler {
|
||||
clientConfigProvider: hs.clientConfigProvider,
|
||||
accesscontrolService: hs.accesscontrolService,
|
||||
userService: hs.userService,
|
||||
folderService: hs.folderService,
|
||||
}
|
||||
}
|
||||
|
||||
@ -700,7 +703,7 @@ func (fk8s *folderK8sHandler) createFolder(c *contextmodel.ReqContext) {
|
||||
}
|
||||
|
||||
fk8s.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
|
||||
folderDTO, err := fk8s.newToFolderDto(c, *out)
|
||||
folderDTO, err := fk8s.newToFolderDto(c, *out, c.SignedInUser.GetOrgID())
|
||||
if err != nil {
|
||||
fk8s.writeError(c, err)
|
||||
return
|
||||
@ -795,10 +798,11 @@ func (fk8s *folderK8sHandler) writeError(c *contextmodel.ReqContext, err error)
|
||||
errhttp.Write(c.Req.Context(), err, c.Resp)
|
||||
}
|
||||
|
||||
func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item unstructured.Unstructured) (dtos.Folder, error) {
|
||||
func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item unstructured.Unstructured, orgID int64) (dtos.Folder, error) {
|
||||
// #TODO revisit how/where we get orgID
|
||||
ctx := c.Req.Context()
|
||||
|
||||
f := internalfolders.UnstructuredToLegacyFolder(item)
|
||||
f := internalfolders.UnstructuredToLegacyFolder(item, orgID)
|
||||
|
||||
fDTO, err := internalfolders.UnstructuredToLegacyFolderDTO(item)
|
||||
if err != nil {
|
||||
@ -817,8 +821,8 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
toDTO := func(f *folder.Folder, checkCanView bool) (dtos.Folder, error) {
|
||||
g, err := guardian.NewByFolder(c.Req.Context(), f, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
toDTO := func(fold *folder.Folder, checkCanView bool) (dtos.Folder, error) {
|
||||
g, err := guardian.NewByFolder(c.Req.Context(), fold, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
@ -830,6 +834,8 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
|
||||
// Finding creator and last updater of the folder
|
||||
updater, creator := anonString, anonString
|
||||
// #TODO refactor the various conversions of the folder so that we either set created by in folder.Folder or
|
||||
// we convert from unstructured to folder DTO without an intermediate conversion to folder.Folder
|
||||
if len(fDTO.CreatedBy) > 0 {
|
||||
id, err := toID(fDTO.CreatedBy)
|
||||
if err != nil {
|
||||
@ -845,7 +851,7 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
updater = fk8s.getUserLogin(ctx, id)
|
||||
}
|
||||
|
||||
acMetadata, _ := fk8s.getFolderACMetadata(c, f)
|
||||
acMetadata, _ := fk8s.getFolderACMetadata(c, fold)
|
||||
|
||||
if checkCanView {
|
||||
canView, _ := g.CanView()
|
||||
@ -865,6 +871,9 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
fDTO.CreatedBy = creator
|
||||
fDTO.UpdatedBy = updater
|
||||
fDTO.AccessControl = acMetadata
|
||||
fDTO.OrgID = f.OrgID
|
||||
// #TODO version doesn't seem to be used--confirm or set it properly
|
||||
fDTO.Version = 1
|
||||
|
||||
return *fDTO, nil
|
||||
}
|
||||
@ -875,24 +884,54 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
|
||||
// TODO: handle parents
|
||||
/*
|
||||
parents, err := fk8s.folderService.GetParents(ctx, folder.GetParentsQuery{UID: f.UID, OrgID: f.OrgID})
|
||||
parents := []*folder.Folder{}
|
||||
if folderDTO.ParentUID != "" {
|
||||
parents, err = fk8s.folderService.GetParents(
|
||||
c.Req.Context(),
|
||||
folder.GetParentsQuery{
|
||||
UID: folderDTO.UID,
|
||||
OrgID: folderDTO.OrgID,
|
||||
})
|
||||
if err != nil {
|
||||
// log the error instead of failing
|
||||
fk8s.log.Error("failed to fetch folder parents", "folder", f.UID, "org", f.OrgID, "error", err)
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// #TODO refactor so that we have just one function for converting to folder DTO
|
||||
toParentDTO := func(fold *folder.Folder, checkCanView bool) (dtos.Folder, error) {
|
||||
g, err := guardian.NewByFolder(c.Req.Context(), fold, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
|
||||
folderDTO.Parents = make([]dtos.Folder, 0, len(parents))
|
||||
for _, f := range parents {
|
||||
DTO, err := toDTO(f, true)
|
||||
if err != nil {
|
||||
// fk8s.log.Error("failed to convert folder to DTO", "folder", f.UID, "org", f.OrgID, "error", err)
|
||||
continue
|
||||
if checkCanView {
|
||||
canView, _ := g.CanView()
|
||||
if !canView {
|
||||
return dtos.Folder{
|
||||
UID: REDACTED,
|
||||
Title: REDACTED,
|
||||
}, nil
|
||||
}
|
||||
folderDTO.Parents = append(folderDTO.Parents, DTO)
|
||||
}
|
||||
*/
|
||||
metrics.MFolderIDsAPICount.WithLabelValues(metrics.NewToFolderDTO).Inc()
|
||||
|
||||
return dtos.Folder{
|
||||
UID: fold.UID,
|
||||
Title: fold.Title,
|
||||
URL: fold.URL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
folderDTO.Parents = make([]dtos.Folder, 0, len(parents))
|
||||
for _, f := range parents {
|
||||
DTO, err := toParentDTO(f, true)
|
||||
if err != nil {
|
||||
// #TODO add logging
|
||||
// fk8s.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
|
||||
}
|
||||
@ -914,20 +953,24 @@ func (fk8s *folderK8sHandler) getFolderACMetadata(c *contextmodel.ReqContext, f
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
folderIDs := map[string]bool{f.UID: true}
|
||||
|
||||
// TODO: handle parents
|
||||
/*
|
||||
parents, err := fk8s.folderService.GetParents(c.Req.Context(), folder.GetParentsQuery{UID: f.UID, OrgID: c.SignedInUser.GetOrgID()})
|
||||
var err error
|
||||
parents := []*folder.Folder{}
|
||||
if f.ParentUID != "" {
|
||||
parents, err = fk8s.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
|
||||
}
|
||||
*/
|
||||
folderIDs := map[string]bool{f.UID: true}
|
||||
for _, p := range parents {
|
||||
folderIDs[p.UID] = true
|
||||
}
|
||||
|
||||
allMetadata := getMultiAccessControlMetadata(c, dashboards.ScopeFoldersPrefix, folderIDs)
|
||||
metadata := map[string]bool{}
|
||||
|
@ -50,13 +50,47 @@ func LegacyUpdateCommandToUnstructured(cmd folder.UpdateFolderCommand) unstructu
|
||||
return obj
|
||||
}
|
||||
|
||||
func UnstructuredToLegacyFolder(item unstructured.Unstructured) *folder.Folder {
|
||||
func UnstructuredToLegacyFolder(item unstructured.Unstructured, orgID int64) *folder.Folder {
|
||||
// #TODO reduce duplication of the different conversion functions
|
||||
spec := item.Object["spec"].(map[string]any)
|
||||
return &folder.Folder{
|
||||
UID: item.GetName(),
|
||||
Title: spec["title"].(string),
|
||||
// #TODO add other fields
|
||||
uid := item.GetName()
|
||||
title := spec["title"].(string)
|
||||
|
||||
meta, err := utils.MetaAccessor(&item)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
id, err := getLegacyID(meta)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
created, err := getCreated(meta)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// avoid panic
|
||||
var createdTime time.Time
|
||||
if created != nil {
|
||||
createdTime = created.Local()
|
||||
}
|
||||
|
||||
f := &folder.Folder{
|
||||
UID: uid,
|
||||
Title: title,
|
||||
ID: id,
|
||||
ParentUID: meta.GetFolder(),
|
||||
// #TODO add created by field if necessary
|
||||
// CreatedBy: meta.GetCreatedBy(),
|
||||
// UpdatedBy: meta.GetCreatedBy(),
|
||||
URL: getURL(meta, title),
|
||||
Created: createdTime,
|
||||
Updated: createdTime,
|
||||
OrgID: orgID,
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func UnstructuredToLegacyFolderDTO(item unstructured.Unstructured) (*dtos.Folder, error) {
|
||||
|
@ -1,6 +1,9 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
infraDB "github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
@ -31,5 +34,16 @@ func NewResourceServer(db infraDB.DB, cfg *setting.Cfg, features featuremgmt.Fea
|
||||
opts.Index = resource.NewResourceIndexServer()
|
||||
}
|
||||
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesFolders) {
|
||||
opts.WriteAccess = resource.WriteAccessHooks{
|
||||
Folder: func(ctx context.Context, user claims.AuthInfo, uid string) bool {
|
||||
// #TODO build on the logic here
|
||||
// #TODO only enable write access when the resource being written in the folder
|
||||
// is another folder
|
||||
return true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return resource.NewResourceServer(opts)
|
||||
}
|
||||
|
Reference in New Issue
Block a user