diff --git a/pkg/infra/filestorage/cdk_blob_filestorage.go b/pkg/infra/filestorage/cdk_blob_filestorage.go index 2ed287abc6c..1ece2574bee 100644 --- a/pkg/infra/filestorage/cdk_blob_filestorage.go +++ b/pkg/infra/filestorage/cdk_blob_filestorage.go @@ -310,7 +310,24 @@ func (c cdkBlobStorage) listFolderPaths(ctx context.Context, parentFolderPath st if options.IsAllowed(obj.Key) { if obj.IsDir && !recursive && options.IsAllowed(obj.Key) { - foundPaths = append(foundPaths, strings.TrimSuffix(obj.Key, Delimiter)) + var nestedDirPath string + dirMPath := obj.Key + directoryMarker + attributes, err := c.bucket.Attributes(ctx, dirMPath) + if err != nil { + c.log.Error("Failed while retrieving attributes", "path", obj.Key, "err", err) + } + + if attributes != nil && attributes.Metadata != nil { + if path, ok := attributes.Metadata[originalPathAttributeKey]; ok { + nestedDirPath = getParentFolderPath(path) + } + } + + if nestedDirPath != "" { + foundPaths = append(foundPaths, nestedDirPath) + } else { + foundPaths = append(foundPaths, strings.TrimSuffix(obj.Key, Delimiter)) + } } if dirPath == "" && !obj.IsDir { diff --git a/pkg/infra/filestorage/db_filestorage.go b/pkg/infra/filestorage/db_filestorage.go index 514c107f347..0b7dde660f2 100644 --- a/pkg/infra/filestorage/db_filestorage.go +++ b/pkg/infra/filestorage/db_filestorage.go @@ -318,6 +318,8 @@ func (s dbFileStorage) ListFiles(ctx context.Context, folderPath string, paging func (s dbFileStorage) ListFolders(ctx context.Context, parentFolderPath string, options *ListOptions) ([]FileMetadata, error) { folders := make([]FileMetadata, 0) + + parentFolderPath = strings.TrimSuffix(parentFolderPath, Delimiter) err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { var foundPaths []string @@ -327,7 +329,7 @@ func (s dbFileStorage) ListFolders(ctx context.Context, parentFolderPath string, if options.Recursive { sess.Where("LOWER(parent_folder_path) > ?", strings.ToLower(parentFolderPath)) } else { - sess.Where("LOWER(parent_folder_path) = ?", strings.ToLower(parentFolderPath)) + sess.Where("LOWER(parent_folder_path) LIKE ? AND LOWER(parent_folder_path) NOT LIKE ?", strings.ToLower(parentFolderPath)+Delimiter+"%", strings.ToLower(parentFolderPath)+Delimiter+"%"+Delimiter+"%") } if options.PathFilters.isDenyAll() { diff --git a/pkg/infra/filestorage/fs_integration_test.go b/pkg/infra/filestorage/fs_integration_test.go index 4678c21437c..8e9957ffe71 100644 --- a/pkg/infra/filestorage/fs_integration_test.go +++ b/pkg/infra/filestorage/fs_integration_test.go @@ -419,6 +419,53 @@ func TestFsStorage(t *testing.T) { }, }, }, + { + name: "listing folders non recursively", + steps: []interface{}{ + cmdUpsert{ + cmd: UpsertFileCommand{ + Path: "/folder1/folder2/file.jpg", + Contents: &[]byte{}, + }, + }, + cmdUpsert{ + cmd: UpsertFileCommand{ + Path: "/folder1/file-inner.jpg", + Contents: &[]byte{}, + }, + }, + cmdUpsert{ + cmd: UpsertFileCommand{ + Path: "/folderX/folderZ/file.txt", + Contents: &[]byte{}, + }, + }, + cmdUpsert{ + cmd: UpsertFileCommand{ + Path: "/folderA/folderB/file.txt", + Contents: &[]byte{}, + }, + }, + queryListFolders{ + input: queryListFoldersInput{path: "/folder1", options: &ListOptions{Recursive: false}}, + checks: [][]interface{}{ + checks(fPath("/folder1/folder2")), + }, + }, + queryListFolders{ + input: queryListFoldersInput{path: "/folderZ", options: &ListOptions{Recursive: false}}, + checks: [][]interface{}{}, + }, + queryListFolders{ + input: queryListFoldersInput{path: "/", options: &ListOptions{Recursive: false}}, + checks: [][]interface{}{ + checks(fPath("/folder1")), + checks(fPath("/folderA")), + checks(fPath("/folderX")), + }, + }, + }, + }, } } diff --git a/pkg/infra/filestorage/wrapper.go b/pkg/infra/filestorage/wrapper.go index 6a0c5f547e9..07b4c62641e 100644 --- a/pkg/infra/filestorage/wrapper.go +++ b/pkg/infra/filestorage/wrapper.go @@ -197,11 +197,12 @@ func (b wrapper) Get(ctx context.Context, path string) (*File, error) { return nil, err } - if !b.pathFilters.IsAllowed(path) { + rootedPath := b.addRoot(path) + if !b.pathFilters.IsAllowed(rootedPath) { return nil, nil } - file, err := b.wrapped.Get(ctx, b.addRoot(path)) + file, err := b.wrapped.Get(ctx, rootedPath) if file != nil { file.FullPath = b.removeRoot(file.FullPath) } @@ -212,11 +213,12 @@ func (b wrapper) Delete(ctx context.Context, path string) error { return err } - if !b.pathFilters.IsAllowed(path) { + rootedPath := b.addRoot(path) + if !b.pathFilters.IsAllowed(rootedPath) { return nil } - return b.wrapped.Delete(ctx, b.addRoot(path)) + return b.wrapped.Delete(ctx, rootedPath) } func detectContentType(path string, originalGuess string) string { @@ -235,7 +237,8 @@ func (b wrapper) Upsert(ctx context.Context, file *UpsertFileCommand) error { return err } - if !b.pathFilters.IsAllowed(file.Path) { + rootedPath := b.addRoot(file.Path) + if !b.pathFilters.IsAllowed(rootedPath) { return nil } @@ -250,7 +253,7 @@ func (b wrapper) Upsert(ctx context.Context, file *UpsertFileCommand) error { } return b.wrapped.Upsert(ctx, &UpsertFileCommand{ - Path: b.addRoot(file.Path), + Path: rootedPath, MimeType: file.MimeType, Contents: file.Contents, Properties: file.Properties, @@ -362,11 +365,12 @@ func (b wrapper) CreateFolder(ctx context.Context, path string) error { return err } - if !b.pathFilters.IsAllowed(path) { + rootedPath := b.addRoot(path) + if !b.pathFilters.IsAllowed(rootedPath) { return nil } - return b.wrapped.CreateFolder(ctx, b.addRoot(path)) + return b.wrapped.CreateFolder(ctx, rootedPath) } func (b wrapper) DeleteFolder(ctx context.Context, path string) error { @@ -374,7 +378,8 @@ func (b wrapper) DeleteFolder(ctx context.Context, path string) error { return err } - if !b.pathFilters.IsAllowed(path) { + rootedPath := b.addRoot(path) + if !b.pathFilters.IsAllowed(rootedPath) { return nil } @@ -387,7 +392,7 @@ func (b wrapper) DeleteFolder(ctx context.Context, path string) error { return fmt.Errorf("folder %s is not empty - cant remove it", path) } - return b.wrapped.DeleteFolder(ctx, b.addRoot(path)) + return b.wrapped.DeleteFolder(ctx, rootedPath) } func (b wrapper) isFolderEmpty(ctx context.Context, path string) (bool, error) {