mirror of https://github.com/databricks/cli.git
Make filer.Filer return fs.DirEntry from ReadDir (#415)
## Changes This allows for compatibility with the stdlib functions in io/fs. ## Tests Integration tests pass.
This commit is contained in:
parent
27df4e765c
commit
42cd8daee0
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -75,6 +76,7 @@ func runFilerReadWriteTest(t *testing.T, ctx context.Context, f filer.Filer) {
|
|||
|
||||
func runFilerReadDirTest(t *testing.T, ctx context.Context, f filer.Filer) {
|
||||
var err error
|
||||
var info fs.FileInfo
|
||||
|
||||
// We start with an empty directory.
|
||||
entries, err := f.ReadDir(ctx, ".")
|
||||
|
@ -105,23 +107,32 @@ func runFilerReadDirTest(t *testing.T, ctx context.Context, f filer.Filer) {
|
|||
entries, err = f.ReadDir(ctx, ".")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, entries, 2)
|
||||
assert.Equal(t, "dir", entries[0].Name)
|
||||
assert.Equal(t, "hello.txt", entries[1].Name)
|
||||
assert.Greater(t, entries[1].ModTime.Unix(), int64(0))
|
||||
assert.Equal(t, "dir", entries[0].Name())
|
||||
assert.True(t, entries[0].IsDir())
|
||||
assert.Equal(t, "hello.txt", entries[1].Name())
|
||||
assert.False(t, entries[1].IsDir())
|
||||
info, err = entries[1].Info()
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, info.ModTime().Unix(), int64(0))
|
||||
|
||||
// Expect two entries in the directory.
|
||||
entries, err = f.ReadDir(ctx, "/dir")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, entries, 2)
|
||||
assert.Equal(t, "a", entries[0].Name)
|
||||
assert.Equal(t, "world.txt", entries[1].Name)
|
||||
assert.Greater(t, entries[1].ModTime.Unix(), int64(0))
|
||||
assert.Equal(t, "a", entries[0].Name())
|
||||
assert.True(t, entries[0].IsDir())
|
||||
assert.Equal(t, "world.txt", entries[1].Name())
|
||||
assert.False(t, entries[1].IsDir())
|
||||
info, err = entries[1].Info()
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, info.ModTime().Unix(), int64(0))
|
||||
|
||||
// Expect a single entry in the nested path.
|
||||
entries, err = f.ReadDir(ctx, "/dir/a/b")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Equal(t, "c", entries[0].Name)
|
||||
assert.Equal(t, "c", entries[0].Name())
|
||||
assert.True(t, entries[0].IsDir())
|
||||
}
|
||||
|
||||
func temporaryWorkspaceDir(t *testing.T, w *databricks.WorkspaceClient) string {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path"
|
||||
"sort"
|
||||
|
@ -15,6 +16,52 @@ import (
|
|||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Type that implements fs.DirEntry for DBFS.
|
||||
type dbfsDirEntry struct {
|
||||
dbfsFileInfo
|
||||
}
|
||||
|
||||
func (entry dbfsDirEntry) Type() fs.FileMode {
|
||||
typ := fs.ModePerm
|
||||
if entry.fi.IsDir {
|
||||
typ |= fs.ModeDir
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
func (entry dbfsDirEntry) Info() (fs.FileInfo, error) {
|
||||
return entry.dbfsFileInfo, nil
|
||||
}
|
||||
|
||||
// Type that implements fs.FileInfo for DBFS.
|
||||
type dbfsFileInfo struct {
|
||||
fi files.FileInfo
|
||||
}
|
||||
|
||||
func (info dbfsFileInfo) Name() string {
|
||||
return path.Base(info.fi.Path)
|
||||
}
|
||||
|
||||
func (info dbfsFileInfo) Size() int64 {
|
||||
return info.fi.FileSize
|
||||
}
|
||||
|
||||
func (info dbfsFileInfo) Mode() fs.FileMode {
|
||||
return fs.ModePerm
|
||||
}
|
||||
|
||||
func (info dbfsFileInfo) ModTime() time.Time {
|
||||
return time.UnixMilli(info.fi.ModificationTime)
|
||||
}
|
||||
|
||||
func (info dbfsFileInfo) IsDir() bool {
|
||||
return info.fi.IsDir
|
||||
}
|
||||
|
||||
func (info dbfsFileInfo) Sys() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DbfsClient implements the [Filer] interface for the DBFS backend.
|
||||
type DbfsClient struct {
|
||||
workspaceClient *databricks.WorkspaceClient
|
||||
|
@ -152,7 +199,7 @@ func (w *DbfsClient) Delete(ctx context.Context, name string) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (w *DbfsClient) ReadDir(ctx context.Context, name string) ([]FileInfo, error) {
|
||||
func (w *DbfsClient) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, error) {
|
||||
absPath, err := w.root.Join(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -175,17 +222,13 @@ func (w *DbfsClient) ReadDir(ctx context.Context, name string) ([]FileInfo, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := make([]FileInfo, len(res.Files))
|
||||
info := make([]fs.DirEntry, len(res.Files))
|
||||
for i, v := range res.Files {
|
||||
info[i] = FileInfo{
|
||||
Name: path.Base(v.Path),
|
||||
Size: v.FileSize,
|
||||
ModTime: time.UnixMilli(v.ModificationTime),
|
||||
}
|
||||
info[i] = dbfsDirEntry{dbfsFileInfo: dbfsFileInfo{fi: v}}
|
||||
}
|
||||
|
||||
// Sort by name for parity with os.ReadDir.
|
||||
sort.Slice(info, func(i, j int) bool { return info[i].Name < info[j].Name })
|
||||
sort.Slice(info, func(i, j int) bool { return info[i].Name() < info[j].Name() })
|
||||
return info, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
type WriteMode int
|
||||
|
@ -14,22 +14,6 @@ const (
|
|||
CreateParentDirectories = iota << 1
|
||||
)
|
||||
|
||||
// FileInfo abstracts over file information from different file systems.
|
||||
// Inspired by https://pkg.go.dev/io/fs#FileInfo.
|
||||
type FileInfo struct {
|
||||
// The type of the file in workspace.
|
||||
Type string
|
||||
|
||||
// Base name.
|
||||
Name string
|
||||
|
||||
// Size in bytes.
|
||||
Size int64
|
||||
|
||||
// Modification time.
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
type FileAlreadyExistsError struct {
|
||||
path string
|
||||
}
|
||||
|
@ -68,7 +52,7 @@ type Filer interface {
|
|||
Delete(ctx context.Context, path string) error
|
||||
|
||||
// Return contents of directory at `path`.
|
||||
ReadDir(ctx context.Context, path string) ([]FileInfo, error)
|
||||
ReadDir(ctx context.Context, path string) ([]fs.DirEntry, error)
|
||||
|
||||
// Creates directory at `path`, creating any intermediate directories as required.
|
||||
Mkdir(ctx context.Context, path string) error
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
@ -20,6 +21,53 @@ import (
|
|||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Type that implements fs.DirEntry for WSFS.
|
||||
type wsfsDirEntry struct {
|
||||
wsfsFileInfo
|
||||
}
|
||||
|
||||
func (entry wsfsDirEntry) Type() fs.FileMode {
|
||||
return entry.wsfsFileInfo.Mode()
|
||||
}
|
||||
|
||||
func (entry wsfsDirEntry) Info() (fs.FileInfo, error) {
|
||||
return entry.wsfsFileInfo, nil
|
||||
}
|
||||
|
||||
// Type that implements fs.FileInfo for WSFS.
|
||||
type wsfsFileInfo struct {
|
||||
oi workspace.ObjectInfo
|
||||
}
|
||||
|
||||
func (info wsfsFileInfo) Name() string {
|
||||
return path.Base(info.oi.Path)
|
||||
}
|
||||
|
||||
func (info wsfsFileInfo) Size() int64 {
|
||||
return info.oi.Size
|
||||
}
|
||||
|
||||
func (info wsfsFileInfo) Mode() fs.FileMode {
|
||||
switch info.oi.ObjectType {
|
||||
case workspace.ObjectTypeDirectory:
|
||||
return fs.ModeDir
|
||||
default:
|
||||
return fs.ModePerm
|
||||
}
|
||||
}
|
||||
|
||||
func (info wsfsFileInfo) ModTime() time.Time {
|
||||
return time.UnixMilli(info.oi.ModifiedAt)
|
||||
}
|
||||
|
||||
func (info wsfsFileInfo) IsDir() bool {
|
||||
return info.oi.ObjectType == workspace.ObjectTypeDirectory
|
||||
}
|
||||
|
||||
func (info wsfsFileInfo) Sys() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WorkspaceFilesClient implements the files-in-workspace API.
|
||||
|
||||
// NOTE: This API is available for files under /Repos if a workspace has files-in-repos enabled.
|
||||
|
@ -165,7 +213,7 @@ func (w *WorkspaceFilesClient) Delete(ctx context.Context, name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (w *WorkspaceFilesClient) ReadDir(ctx context.Context, name string) ([]FileInfo, error) {
|
||||
func (w *WorkspaceFilesClient) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, error) {
|
||||
absPath, err := w.root.Join(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -189,18 +237,13 @@ func (w *WorkspaceFilesClient) ReadDir(ctx context.Context, name string) ([]File
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := make([]FileInfo, len(objects))
|
||||
info := make([]fs.DirEntry, len(objects))
|
||||
for i, v := range objects {
|
||||
info[i] = FileInfo{
|
||||
Type: string(v.ObjectType),
|
||||
Name: path.Base(v.Path),
|
||||
Size: v.Size,
|
||||
ModTime: time.UnixMilli(v.ModifiedAt),
|
||||
}
|
||||
info[i] = wsfsDirEntry{wsfsFileInfo{oi: v}}
|
||||
}
|
||||
|
||||
// Sort by name for parity with os.ReadDir.
|
||||
sort.Slice(info, func(i, j int) bool { return info[i].Name < info[j].Name })
|
||||
sort.Slice(info, func(i, j int) bool { return info[i].Name() < info[j].Name() })
|
||||
return info, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue