package folderimpl import ( "context" "fmt" "strconv" "strings" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/infra/slugify" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/user" ) func convertUnstructuredToFolder(item *unstructured.Unstructured, identifiers map[string]*user.User) (*folder.Folder, error) { meta, err := utils.MetaAccessor(item) if err != nil { return nil, err } info, _ := authlib.ParseNamespace(meta.GetNamespace()) if info.OrgID < 0 { info.OrgID = 1 // This resolves all test cases that assume org 1 } title, _, _ := unstructured.NestedString(item.Object, "spec", "title") description, _, _ := unstructured.NestedString(item.Object, "spec", "description") uid := meta.GetName() url := "" if uid != folder.RootFolder.UID { slug := slugify.Slugify(title) url = dashboards.GetFolderURL(uid, slug) } created := meta.GetCreationTimestamp().Local() updated, _ := meta.GetUpdatedTimestamp() if updated == nil { updated = &created } else { tmp := updated.Local() updated = &tmp } createdBy, updatedBy := int64(0), int64(0) createdByUID, updatedByUID := "", "" if len(identifiers) > 0 { user, ok := identifiers[meta.GetCreatedBy()] if ok { createdBy = user.ID createdByUID = user.UID } user, ok = identifiers[meta.GetUpdatedBy()] if ok { updatedBy = user.ID updatedByUID = user.UID } } manager, _ := meta.GetManagerProperties() return &folder.Folder{ UID: uid, Title: title, Description: description, ID: meta.GetDeprecatedInternalID(), // nolint:staticcheck ParentUID: meta.GetFolder(), Version: int(meta.GetGeneration()), ManagedBy: manager.Kind, Fullpath: meta.GetFullpath(), FullpathUIDs: meta.GetFullpathUIDs(), URL: url, Created: created, Updated: *updated, OrgID: info.OrgID, CreatedBy: createdBy, CreatedByUID: createdByUID, UpdatedBy: updatedBy, UpdatedByUID: updatedByUID, }, nil } func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolder(ctx context.Context, item *unstructured.Unstructured) (*folder.Folder, error) { meta, err := utils.MetaAccessor(item) if err != nil { return nil, err } identifiers := make(map[string]struct{}, 0) identifiers[meta.GetCreatedBy()] = struct{}{} identifiers[meta.GetUpdatedBy()] = struct{}{} folderUserIdentifiers, err := ss.getFolderIdentifiers(ctx, identifiers) if err != nil { return nil, err } folder, err := convertUnstructuredToFolder(item, folderUserIdentifiers) if err != nil { return nil, err } return folder, nil } func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolderList(ctx context.Context, unstructuredList *unstructured.UnstructuredList) ([]*folder.Folder, error) { identifiers := make(map[string]struct{}, 0) for _, item := range unstructuredList.Items { meta, err := utils.MetaAccessor(&item) if err != nil { return nil, fmt.Errorf("unable to convert unstructured item to legacy folder %w", err) } identifiers[meta.GetCreatedBy()] = struct{}{} identifiers[meta.GetUpdatedBy()] = struct{}{} } folderUserIdentifiers, err := ss.getFolderIdentifiers(ctx, identifiers) if err != nil { return nil, err } folders := make([]*folder.Folder, 0) for _, item := range unstructuredList.Items { folder, err := convertUnstructuredToFolder(&item, folderUserIdentifiers) if err != nil { return nil, err } folders = append(folders, folder) } return folders, nil } func (ss *FolderUnifiedStoreImpl) getFolderIdentifiers(ctx context.Context, identifiers map[string]struct{}) (map[string]*user.User, error) { identifierMap, userUIDs, userIds := separateUIDsAndIDs(identifiers) if len(userUIDs) == 0 && len(userIds) == 0 { return nil, nil } users, err := ss.userService.ListByIdOrUID(ctx, userUIDs, userIds) if err != nil { return nil, err } userMap := make(map[string]*user.User, len(users)) for _, u := range users { if _, ok := identifierMap[fmt.Sprintf("user:%d", u.ID)]; ok { userMap[fmt.Sprintf("user:%d", u.ID)] = u } if _, ok := identifierMap[fmt.Sprintf("user:%s", u.UID)]; ok { userMap[fmt.Sprintf("user:%s", u.UID)] = u } } return userMap, nil } func parseIdentifier(identifier string) string { parts := strings.Split(identifier, ":") if len(parts) < 2 { return "" } if parts[0] != "user" { return "" } return parts[1] } func separateUIDsAndIDs(identifiers map[string]struct{}) (map[string]string, []string, []int64) { uids := make([]string, 0) ids := make([]int64, 0) identifierMap := make(map[string]string, 0) for identifier := range identifiers { value := parseIdentifier(identifier) if value == "" { continue } identifierMap[identifier] = value id, err := strconv.ParseInt(value, 10, 64) if err == nil { ids = append(ids, id) } else { uids = append(uids, value) } } return identifierMap, uids, ids }