diff --git a/pkg/services/store/service.go b/pkg/services/store/service.go index 3412616b5e1..be879f00133 100644 --- a/pkg/services/store/service.go +++ b/pkg/services/store/service.go @@ -6,7 +6,6 @@ import ( "fmt" "strings" - "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/infra/filestorage" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" @@ -34,7 +33,7 @@ type StorageService interface { registry.BackgroundService // List folder contents - List(ctx context.Context, user *models.SignedInUser, path string) (*data.Frame, error) + List(ctx context.Context, user *models.SignedInUser, path string) (*StorageListFrame, error) // Read raw file contents out of the store Read(ctx context.Context, user *models.SignedInUser, path string) (*filestorage.File, error) @@ -114,7 +113,7 @@ func getOrgId(user *models.SignedInUser) int64 { return user.OrgId } -func (s *standardStorageService) List(ctx context.Context, user *models.SignedInUser, path string) (*data.Frame, error) { +func (s *standardStorageService) List(ctx context.Context, user *models.SignedInUser, path string) (*StorageListFrame, error) { // apply access control here return s.tree.ListFolder(ctx, getOrgId(user), path) } diff --git a/pkg/services/store/service_test.go b/pkg/services/store/service_test.go index 026ed76669b..9dba7282639 100644 --- a/pkg/services/store/service_test.go +++ b/pkg/services/store/service_test.go @@ -43,15 +43,15 @@ func TestListFiles(t *testing.T) { frame, err := store.List(context.Background(), dummyUser, "public/testdata") require.NoError(t, err) - experimental.CheckGoldenJSONFrame(t, "testdata", "public_testdata.golden", frame, true) + experimental.CheckGoldenJSONFrame(t, "testdata", "public_testdata.golden", frame.Frame, true) file, err := store.Read(context.Background(), dummyUser, "public/testdata/js_libraries.csv") require.NoError(t, err) require.NotNil(t, file) - frame, err = testdatasource.LoadCsvContent(bytes.NewReader(file.Contents), file.Name) + testDsFrame, err := testdatasource.LoadCsvContent(bytes.NewReader(file.Contents), file.Name) require.NoError(t, err) - experimental.CheckGoldenJSONFrame(t, "testdata", "public_testdata_js_libraries.golden", frame, true) + experimental.CheckGoldenJSONFrame(t, "testdata", "public_testdata_js_libraries.golden", testDsFrame, true) } func TestUpload(t *testing.T) { diff --git a/pkg/services/store/testdata/example_list_frame.json b/pkg/services/store/testdata/example_list_frame.json new file mode 100644 index 00000000000..92814399aa8 --- /dev/null +++ b/pkg/services/store/testdata/example_list_frame.json @@ -0,0 +1,73 @@ +{ + "schema": { + "meta": { + "type": "directory-listing", + "custom": { + "HasMore": false + } + }, + "fields": [ + { + "name": "name", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "mediaType", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "size", + "type": "number", + "typeInfo": { + "frame": "int64" + }, + "config": { + "unit": "bytes" + } + } + ] + }, + "data": { + "values": [ + [ + "DL_1.jpg", + "Screen Shot 2022-06-23 at 9.05.39 PM.png", + "Screen Shot 2022-06-24 at 11.58.32 AM.png", + "Screen Shot 2022-06-30 at 3.45.03 PM.png", + "Screen Shot 2022-07-05 at 3.24.27 PM.png", + "image.png", + "rocket_1f680.png", + "test-folder", + "topcoder12.png" + ], + [ + "image/jpeg", + "image/png", + "image/png", + "image/png", + "image/png", + "image/png", + "image/png", + "directory", + "image/png" + ], + [ + 943004, + 684257, + 256396, + 8796, + 388290, + 182568, + 29066, + 0, + 90563 + ] + ] + } +} diff --git a/pkg/services/store/testdata/example_root_level_list.json b/pkg/services/store/testdata/example_root_level_list.json new file mode 100644 index 00000000000..1846b25b29e --- /dev/null +++ b/pkg/services/store/testdata/example_root_level_list.json @@ -0,0 +1,90 @@ +{ + "schema": { + "meta": { + "type": "directory-listing" + }, + "fields": [ + { + "name": "name", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "title", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "description", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "mediaType", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "storageType", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "name": "readOnly", + "type": "boolean", + "typeInfo": { + "frame": "bool" + } + }, + { + "name": "builtIn", + "type": "boolean", + "typeInfo": { + "frame": "bool" + } + } + ] + }, + "data": { + "values": [ + [ + "public-static", + "resources" + ], + [ + "Public static files", + "Resources" + ], + [ + "Access files from the static public files", + "Upload custom resource files" + ], + [ + "directory", + "directory" + ], + [ + "disk", + "sql" + ], + [ + true, + false + ], + [ + true, + true + ] + ] + } +} diff --git a/pkg/services/store/tree.go b/pkg/services/store/tree.go index 89469b198b6..24f32377dbd 100644 --- a/pkg/services/store/tree.go +++ b/pkg/services/store/tree.go @@ -85,7 +85,7 @@ func (t *nestedTree) GetFile(ctx context.Context, orgId int64, path string) (*fi return root.Get(ctx, path) } -func (t *nestedTree) ListFolder(ctx context.Context, orgId int64, path string) (*data.Frame, error) { +func (t *nestedTree) ListFolder(ctx context.Context, orgId int64, path string) (*StorageListFrame, error) { if path == "" || path == "/" { t.assureOrgIsInitialized(orgId) @@ -102,13 +102,13 @@ func (t *nestedTree) ListFolder(ctx context.Context, orgId int64, path string) ( readOnly := data.NewFieldFromFieldType(data.FieldTypeBool, count) builtIn := data.NewFieldFromFieldType(data.FieldTypeBool, count) mtype := data.NewFieldFromFieldType(data.FieldTypeString, count) - title.Name = "title" - names.Name = "name" - descr.Name = "description" - mtype.Name = "mediaType" - types.Name = "storageType" - readOnly.Name = "readOnly" - builtIn.Name = "builtIn" + title.Name = titleListFrameField + names.Name = nameListFrameField + descr.Name = descriptionListFrameField + mtype.Name = mediaTypeListFrameField + types.Name = storageTypeListFrameField + readOnly.Name = readOnlyListFrameField + builtIn.Name = builtInListFrameField for _, f := range t.rootsByOrgId[ac.GlobalOrgID] { meta := f.Meta() names.Set(idx, meta.Config.Prefix) @@ -138,7 +138,7 @@ func (t *nestedTree) ListFolder(ctx context.Context, orgId int64, path string) ( frame.SetMeta(&data.FrameMeta{ Type: data.FrameTypeDirectoryListing, }) - return frame, nil + return &StorageListFrame{frame}, nil } root, path := t.getRoot(orgId, path) @@ -160,9 +160,9 @@ func (t *nestedTree) ListFolder(ctx context.Context, orgId int64, path string) ( names := data.NewFieldFromFieldType(data.FieldTypeString, count) mtype := data.NewFieldFromFieldType(data.FieldTypeString, count) fsize := data.NewFieldFromFieldType(data.FieldTypeInt64, count) - names.Name = "name" - mtype.Name = "mediaType" - fsize.Name = "size" + names.Name = nameListFrameField + mtype.Name = mediaTypeListFrameField + fsize.Name = sizeListFrameField fsize.Config = &data.FieldConfig{ Unit: "bytes", } @@ -178,5 +178,5 @@ func (t *nestedTree) ListFolder(ctx context.Context, orgId int64, path string) ( "HasMore": listResponse.HasMore, }, }) - return frame, nil + return &StorageListFrame{frame}, nil } diff --git a/pkg/services/store/types.go b/pkg/services/store/types.go index 29151efae31..db9cdfa8202 100644 --- a/pkg/services/store/types.go +++ b/pkg/services/store/types.go @@ -30,7 +30,7 @@ type WriteValueResponse struct { type storageTree interface { GetFile(ctx context.Context, orgId int64, path string) (*filestorage.File, error) - ListFolder(ctx context.Context, orgId int64, path string) (*data.Frame, error) + ListFolder(ctx context.Context, orgId int64, path string) (*StorageListFrame, error) } //------------------------------------------- @@ -95,3 +95,38 @@ type RootStorageMeta struct { Config RootStorageConfig `json:"config"` } + +type StorageListFrame struct { + *data.Frame +} + +const ( + titleListFrameField = "title" + nameListFrameField = "name" + descriptionListFrameField = "description" + mediaTypeListFrameField = "mediaType" + storageTypeListFrameField = "storageType" + readOnlyListFrameField = "readOnly" + builtInListFrameField = "builtIn" + sizeListFrameField = "size" +) + +func (s *StorageListFrame) GetFileNames() []string { + var fileNames []string + if s == nil { + return fileNames + } + + field, idx := s.FieldByName(nameListFrameField) + if field.Len() == 0 || idx == -1 { + return fileNames + } + + for i := 0; i < field.Len(); i++ { + if stringValue, ok := field.At(i).(string); ok { + fileNames = append(fileNames, stringValue) + } + } + + return fileNames +} diff --git a/pkg/services/store/types_test.go b/pkg/services/store/types_test.go new file mode 100644 index 00000000000..3374c04aed8 --- /dev/null +++ b/pkg/services/store/types_test.go @@ -0,0 +1,47 @@ +package store + +import ( + _ "embed" + "testing" + + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/require" +) + +var ( + //go:embed testdata/example_list_frame.json + exampleListFrameJSON string + + //go:embed testdata/example_root_level_list.json + exampleRootLevelListJSON string +) + +func TestGetFileNames(t *testing.T) { + frame := &data.Frame{} + err := frame.UnmarshalJSON([]byte(exampleListFrameJSON)) + require.NoError(t, err) + + listFrame := StorageListFrame{frame} + require.Equal(t, []string{ + "DL_1.jpg", + "Screen Shot 2022-06-23 at 9.05.39 PM.png", + "Screen Shot 2022-06-24 at 11.58.32 AM.png", + "Screen Shot 2022-06-30 at 3.45.03 PM.png", + "Screen Shot 2022-07-05 at 3.24.27 PM.png", + "image.png", "rocket_1f680.png", + "test-folder", + "topcoder12.png", + }, listFrame.GetFileNames()) +} + +func TestGetFileNamesRootLevel(t *testing.T) { + frame := &data.Frame{} + err := frame.UnmarshalJSON([]byte(exampleRootLevelListJSON)) + require.NoError(t, err) + + listFrame := StorageListFrame{frame} + require.Equal(t, []string{ + "public-static", + "resources", + }, listFrame.GetFileNames()) +} diff --git a/pkg/tsdb/grafanads/grafana.go b/pkg/tsdb/grafanads/grafana.go index 129f3ea875d..a1011f39962 100644 --- a/pkg/tsdb/grafanads/grafana.go +++ b/pkg/tsdb/grafanads/grafana.go @@ -109,10 +109,10 @@ func (s *Service) doListQuery(ctx context.Context, query backend.DataQuery) back } path := store.RootPublicStatic + "/" + q.Path - frame, err := s.store.List(ctx, nil, path) + listFrame, err := s.store.List(ctx, nil, path) response.Error = err - if frame != nil { - response.Frames = data.Frames{frame} + if listFrame != nil { + response.Frames = data.Frames{listFrame.Frame} } return response }