mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8s/Folders: Allow recursive creation of DTO (#96439)
* Fix toDTO * Remove conversion function for folder dto * Convert toDTO to a standalone function --------- Co-authored-by: Jean-Philippe Quéméner <JohnnyQQQQ@users.noreply.github.com>
This commit is contained in:
@@ -675,7 +675,7 @@ func (fk8s *folderK8sHandler) searchFolders(c *contextmodel.ReqContext) {
|
||||
query := strings.ToUpper(c.Query("query"))
|
||||
folders := []folder.Folder{}
|
||||
for _, item := range out.Items {
|
||||
p := internalfolders.UnstructuredToLegacyFolder(item, c.SignedInUser.GetOrgID())
|
||||
p, _ := internalfolders.UnstructuredToLegacyFolder(item, c.SignedInUser.GetOrgID())
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
@@ -821,76 +821,12 @@ func (fk8s *folderK8sHandler) writeError(c *contextmodel.ReqContext, err 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, orgID)
|
||||
|
||||
fDTO, err := internalfolders.UnstructuredToLegacyFolderDTO(item)
|
||||
if err != nil {
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
|
||||
// #TODO Is there a preexisting function we can use instead, something along the lines of UserIdentifier?
|
||||
toUID := func(rawIdentifier string) string {
|
||||
parts := strings.Split(rawIdentifier, ":")
|
||||
if len(parts) < 2 {
|
||||
return ""
|
||||
}
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
canEdit, _ := g.CanEdit()
|
||||
canSave, _ := g.CanSave()
|
||||
canAdmin, _ := g.CanAdmin()
|
||||
canDelete, _ := g.CanDelete()
|
||||
|
||||
// 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 {
|
||||
creator = fk8s.getUserLogin(ctx, toUID(fDTO.CreatedBy))
|
||||
}
|
||||
if len(fDTO.UpdatedBy) > 0 {
|
||||
updater = fk8s.getUserLogin(ctx, toUID(fDTO.UpdatedBy))
|
||||
}
|
||||
|
||||
acMetadata, _ := fk8s.getFolderACMetadata(c, fold)
|
||||
|
||||
if checkCanView {
|
||||
canView, _ := g.CanView()
|
||||
if !canView {
|
||||
return dtos.Folder{
|
||||
UID: REDACTED,
|
||||
Title: REDACTED,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
metrics.MFolderIDsAPICount.WithLabelValues(metrics.NewToFolderDTO).Inc()
|
||||
|
||||
fDTO.CanSave = canSave
|
||||
fDTO.CanEdit = canEdit
|
||||
fDTO.CanAdmin = canAdmin
|
||||
fDTO.CanDelete = canDelete
|
||||
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
|
||||
}
|
||||
f, createdBy := internalfolders.UnstructuredToLegacyFolder(item, orgID)
|
||||
|
||||
dontCheckCanView := false
|
||||
checkCanView := true
|
||||
// no need to check view permission for the starting folder since it's already checked by the callers
|
||||
folderDTO, err := toDTO(f, false)
|
||||
folderDTO, err := fk8s.toDTO(c, f, createdBy, dontCheckCanView)
|
||||
if err != nil {
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
@@ -917,12 +853,18 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
uid := parentsFullPathUIDs[1:][i]
|
||||
url := dashboards.GetFolderURL(uid, slug)
|
||||
|
||||
parents = append(parents, dtos.Folder{
|
||||
ff := folder.Folder{
|
||||
UID: uid,
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
Title: v,
|
||||
URL: url,
|
||||
})
|
||||
}
|
||||
parentDTO, err := fk8s.toDTO(c, &ff, "", checkCanView)
|
||||
if err != nil {
|
||||
// #TODO should we log this error?
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
|
||||
parents = append(parents, parentDTO)
|
||||
}
|
||||
|
||||
folderDTO.Parents = parents
|
||||
@@ -930,6 +872,74 @@ func (fk8s *folderK8sHandler) newToFolderDto(c *contextmodel.ReqContext, item un
|
||||
return folderDTO, nil
|
||||
}
|
||||
|
||||
func toUID(rawIdentifier string) string {
|
||||
// #TODO Is there a preexisting function we can use instead, something along the lines of UserIdentifier?
|
||||
parts := strings.Split(rawIdentifier, ":")
|
||||
if len(parts) < 2 {
|
||||
return ""
|
||||
}
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
func (fk8s *folderK8sHandler) toDTO(c *contextmodel.ReqContext, fold *folder.Folder, createdBy string, checkCanView bool) (dtos.Folder, error) {
|
||||
// #TODO revisit how/where we get orgID
|
||||
ctx := c.Req.Context()
|
||||
|
||||
g, err := guardian.NewByFolder(c.Req.Context(), fold, 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
|
||||
// #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(createdBy) > 0 {
|
||||
creator = fk8s.getUserLogin(ctx, toUID(createdBy))
|
||||
}
|
||||
if len(createdBy) > 0 {
|
||||
updater = fk8s.getUserLogin(ctx, toUID(createdBy))
|
||||
}
|
||||
|
||||
acMetadata, _ := fk8s.getFolderACMetadata(c, fold)
|
||||
|
||||
if checkCanView {
|
||||
canView, _ := g.CanView()
|
||||
if !canView {
|
||||
return dtos.Folder{
|
||||
UID: REDACTED,
|
||||
Title: REDACTED,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
metrics.MFolderIDsAPICount.WithLabelValues(metrics.NewToFolderDTO).Inc()
|
||||
|
||||
return dtos.Folder{
|
||||
ID: fold.ID, // nolint:staticcheck
|
||||
UID: fold.UID,
|
||||
Title: fold.Title,
|
||||
URL: fold.URL,
|
||||
HasACL: fold.HasACL,
|
||||
CanSave: canSave,
|
||||
CanEdit: canEdit,
|
||||
CanAdmin: canAdmin,
|
||||
CanDelete: canDelete,
|
||||
CreatedBy: creator,
|
||||
Created: fold.Created,
|
||||
UpdatedBy: updater,
|
||||
Updated: fold.Updated,
|
||||
// #TODO version doesn't seem to be used--confirm or set it properly
|
||||
Version: 1,
|
||||
AccessControl: acMetadata,
|
||||
ParentUID: fold.ParentUID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fk8s *folderK8sHandler) getUserLogin(ctx context.Context, userUID string) string {
|
||||
ctx, span := tracer.Start(ctx, "api.getUserLogin")
|
||||
defer span.End()
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
@@ -64,7 +63,7 @@ func LegacyUpdateCommandToUnstructured(cmd folder.UpdateFolderCommand) (unstruct
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func UnstructuredToLegacyFolder(item unstructured.Unstructured, orgID int64) *folder.Folder {
|
||||
func UnstructuredToLegacyFolder(item unstructured.Unstructured, orgID int64) (*folder.Folder, string) {
|
||||
// #TODO reduce duplication of the different conversion functions
|
||||
spec := item.Object["spec"].(map[string]any)
|
||||
uid := item.GetName()
|
||||
@@ -72,22 +71,24 @@ func UnstructuredToLegacyFolder(item unstructured.Unstructured, orgID int64) *fo
|
||||
|
||||
meta, err := utils.MetaAccessor(&item)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
id, err := getLegacyID(meta)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
created, err := getCreated(meta)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// avoid panic
|
||||
var createdTime time.Time
|
||||
if created != nil {
|
||||
// #TODO Fix this time format. The legacy time format seems to be along the lines of time.Now()
|
||||
// which includes a part that represents a fraction of a second. Format should be "2024-09-12T15:37:41.09466+02:00"
|
||||
createdTime = created.Local()
|
||||
}
|
||||
|
||||
@@ -97,10 +98,11 @@ func UnstructuredToLegacyFolder(item unstructured.Unstructured, orgID int64) *fo
|
||||
ID: id,
|
||||
ParentUID: meta.GetFolder(),
|
||||
// #TODO add created by field if necessary
|
||||
// CreatedBy: meta.GetCreatedBy(),
|
||||
// UpdatedBy: meta.GetCreatedBy(),
|
||||
URL: getURL(meta, title),
|
||||
URL: getURL(meta, title),
|
||||
// #TODO get Created in format "2024-09-12T15:37:41.09466+02:00"
|
||||
Created: createdTime,
|
||||
// #TODO figure out whether we want to set "updated" and "updated by". Could replace with
|
||||
// meta.GetUpdatedTimestamp() but it currently gets overwritten in prepareObjectForStorage().
|
||||
Updated: createdTime,
|
||||
OrgID: orgID,
|
||||
|
||||
@@ -114,57 +116,10 @@ func UnstructuredToLegacyFolder(item unstructured.Unstructured, orgID int64) *fo
|
||||
// nolint:staticcheck
|
||||
FullpathUIDs: meta.GetFullPathUIDs(),
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func UnstructuredToLegacyFolderDTO(item unstructured.Unstructured) (*dtos.Folder, error) {
|
||||
spec := item.Object["spec"].(map[string]any)
|
||||
uid := item.GetName()
|
||||
title := spec["title"].(string)
|
||||
|
||||
meta, err := utils.MetaAccessor(&item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := getLegacyID(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
created, err := getCreated(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// avoid panic
|
||||
var createdTime time.Time
|
||||
if created != nil {
|
||||
// #TODO Fix this time format. The legacy time format seems to be along the lines of time.Now()
|
||||
// which includes a part that represents a fraction of a second.
|
||||
createdTime = created.Local()
|
||||
}
|
||||
|
||||
dto := &dtos.Folder{
|
||||
UID: uid,
|
||||
Title: title,
|
||||
ID: id,
|
||||
ParentUID: meta.GetFolder(),
|
||||
// #TODO add back CreatedBy, UpdatedBy once we figure out how to access userService
|
||||
// to translate user ID into user login. meta.GetCreatedBy() only stores user ID
|
||||
// Could convert meta.GetCreatedBy() return value to a struct--id and name
|
||||
CreatedBy: meta.GetCreatedBy(),
|
||||
UpdatedBy: meta.GetCreatedBy(),
|
||||
URL: getURL(meta, title),
|
||||
// #TODO get Created in format "2024-09-12T15:37:41.09466+02:00"
|
||||
Created: createdTime,
|
||||
// #TODO figure out whether we want to set "updated" and "updated by". Could replace with
|
||||
// meta.GetUpdatedTimestamp() but it currently gets overwritten in prepareObjectForStorage().
|
||||
Updated: createdTime,
|
||||
|
||||
// #TODO figure out about adding version, parents, orgID fields
|
||||
}
|
||||
return dto, nil
|
||||
// CreatedBy needs to be returned separately because it's the user UID (string) but
|
||||
// folder.Folder expects user ID (int64).
|
||||
return f, meta.GetCreatedBy()
|
||||
// #TODO figure out about adding version, parents, orgID fields
|
||||
}
|
||||
|
||||
func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper) (*v0alpha1.Folder, error) {
|
||||
@@ -198,6 +153,7 @@ func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper)
|
||||
// #TODO: turns out these get overwritten by Unified Storage (see pkg/storage/unified/apistore/prepare.go)
|
||||
// We're going to have to align with that. For now we do need the user ID because the folder type stores it
|
||||
// as the only user identifier
|
||||
|
||||
if v.CreatedByUID != "" {
|
||||
meta.SetCreatedBy(v.UpdatedByUID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user