From 1087ed623fdcd23dec941fbf646e4a2d1cd21d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Qu=C3=A9m=C3=A9ner?= Date: Wed, 29 Jan 2025 14:50:49 +0100 Subject: [PATCH] feat(unified-storage): fetch full path if needed (#99747) --- .../folder/folderimpl/unifiedstore.go | 18 +++++ .../folder/folderimpl/unifiedstore_test.go | 77 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 pkg/services/folder/folderimpl/unifiedstore_test.go diff --git a/pkg/services/folder/folderimpl/unifiedstore.go b/pkg/services/folder/folderimpl/unifiedstore.go index 18c40751185..481ab6c704d 100644 --- a/pkg/services/folder/folderimpl/unifiedstore.go +++ b/pkg/services/folder/folderimpl/unifiedstore.go @@ -352,6 +352,14 @@ func (ss *FolderUnifiedStoreImpl) GetFolders(ctx context.Context, q folder.GetFo if f == nil { return nil, fmt.Errorf("unable to convert unstructured item to legacy folder %w", err) } + if q.WithFullpath || q.WithFullpathUIDs { + parents, err := ss.GetParents(ctx, folder.GetParentsQuery{UID: f.UID, OrgID: q.OrgID}) + if err != nil { + return nil, fmt.Errorf("failed to get parents for folder %s: %w", f.UID, err) + } + // If we don't have a parent, we just return the current folder as the full path + f.Fullpath, f.FullpathUIDs = computeFullPath(append(parents, f)) + } m[f.UID] = f } @@ -529,3 +537,13 @@ func (ss *FolderUnifiedStoreImpl) getK8sContext(ctx context.Context) (context.Co return newCtx, nil, nil } + +func computeFullPath(parents []*folder.Folder) (string, string) { + fullpath := make([]string, len(parents)) + fullpathUIDs := make([]string, len(parents)) + for i, p := range parents { + fullpath[i] = p.Title + fullpathUIDs[i] = p.UID + } + return strings.Join(fullpath, "/"), strings.Join(fullpathUIDs, "/") +} diff --git a/pkg/services/folder/folderimpl/unifiedstore_test.go b/pkg/services/folder/folderimpl/unifiedstore_test.go new file mode 100644 index 00000000000..d25cffa6e27 --- /dev/null +++ b/pkg/services/folder/folderimpl/unifiedstore_test.go @@ -0,0 +1,77 @@ +package folderimpl + +import ( + "testing" + + "github.com/grafana/grafana/pkg/services/folder" + "github.com/stretchr/testify/require" +) + +func TestComputeFullPath(t *testing.T) { + testCases := []struct { + name string + parents []*folder.Folder + wantPath string + wantPathUIDs string + }{ + { + name: "empty slice should return empty paths", + parents: []*folder.Folder{}, + wantPath: "", + wantPathUIDs: "", + }, + { + name: "single element should return single path", + parents: []*folder.Folder{ + { + Title: "Element", + UID: "Element-uid", + }, + }, + wantPath: "Element", + wantPathUIDs: "Element-uid", + }, + { + name: "multiple parents should return hierarchical path", + parents: []*folder.Folder{ + { + Title: "Grandparent", + UID: "grandparent-uid", + }, + { + Title: "Parent", + UID: "parent-uid", + }, + { + Title: "Element", + UID: "Element-uid", + }, + }, + wantPath: "Grandparent/Parent/Element", + wantPathUIDs: "grandparent-uid/parent-uid/Element-uid", + }, + { + name: "should handle special characters in titles", + parents: []*folder.Folder{ + { + Title: "Parent/With/Slashes", + UID: "parent-uid", + }, + { + Title: "Element With Spaces", + UID: "Element-uid", + }, + }, + wantPath: "Parent/With/Slashes/Element With Spaces", + wantPathUIDs: "parent-uid/Element-uid", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gotPath, gotPathUIDs := computeFullPath(tc.parents) + require.Equal(t, tc.wantPath, gotPath) + require.Equal(t, tc.wantPathUIDs, gotPathUIDs) + }) + } +}