From 2b56af60167d3ca6d96b25ca0d43e91d6b9019e6 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 1 Jun 2023 20:23:22 +0200 Subject: [PATCH] Add Stat function to filer.Filer interface (#421) ## Changes TSIA ## Tests New integration test passes. --- internal/filer_test.go | 21 +++++++++++++++ libs/filer/dbfs_client.go | 38 +++++++++++++++++++++++----- libs/filer/filer.go | 3 +++ libs/filer/workspace_files_client.go | 23 +++++++++++++++++ 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/internal/filer_test.go b/internal/filer_test.go index b8fd6365..0dda1d1b 100644 --- a/internal/filer_test.go +++ b/internal/filer_test.go @@ -68,11 +68,32 @@ func runFilerReadWriteTest(t *testing.T, ctx context.Context, f filer.Filer) { assert.NoError(t, err) filerTest{t, f}.assertContents(ctx, "/foo/bar", `hello universe`) + // Stat on a directory should succeed. + // Note: size and modification time behave differently between WSFS and DBFS. + info, err := f.Stat(ctx, "/foo") + require.NoError(t, err) + assert.Equal(t, "foo", info.Name()) + assert.True(t, info.Mode().IsDir()) + assert.Equal(t, true, info.IsDir()) + + // Stat on a file should succeed. + // Note: size and modification time behave differently between WSFS and DBFS. + info, err = f.Stat(ctx, "/foo/bar") + require.NoError(t, err) + assert.Equal(t, "bar", info.Name()) + assert.True(t, info.Mode().IsRegular()) + assert.Equal(t, false, info.IsDir()) + // Delete should fail if the file doesn't exist. err = f.Delete(ctx, "/doesnt_exist") assert.True(t, errors.As(err, &filer.FileDoesNotExistError{})) assert.True(t, errors.Is(err, fs.ErrNotExist)) + // Stat should fail if the file doesn't exist. + _, err = f.Stat(ctx, "/doesnt_exist") + assert.True(t, errors.As(err, &filer.FileDoesNotExistError{})) + assert.True(t, errors.Is(err, fs.ErrNotExist)) + // Delete should succeed for file that does exist. err = f.Delete(ctx, "/foo/bar") assert.NoError(t, err) diff --git a/libs/filer/dbfs_client.go b/libs/filer/dbfs_client.go index 7a3084ac..c86a80b1 100644 --- a/libs/filer/dbfs_client.go +++ b/libs/filer/dbfs_client.go @@ -22,11 +22,7 @@ type dbfsDirEntry struct { } func (entry dbfsDirEntry) Type() fs.FileMode { - typ := fs.ModePerm - if entry.fi.IsDir { - typ |= fs.ModeDir - } - return typ + return entry.Mode() } func (entry dbfsDirEntry) Info() (fs.FileInfo, error) { @@ -47,7 +43,11 @@ func (info dbfsFileInfo) Size() int64 { } func (info dbfsFileInfo) Mode() fs.FileMode { - return fs.ModePerm + mode := fs.ModePerm + if info.fi.IsDir { + mode |= fs.ModeDir + } + return mode } func (info dbfsFileInfo) ModTime() time.Time { @@ -240,3 +240,29 @@ func (w *DbfsClient) Mkdir(ctx context.Context, name string) error { return w.workspaceClient.Dbfs.MkdirsByPath(ctx, dirPath) } + +func (w *DbfsClient) Stat(ctx context.Context, name string) (fs.FileInfo, error) { + absPath, err := w.root.Join(name) + if err != nil { + return nil, err + } + + info, err := w.workspaceClient.Dbfs.GetStatusByPath(ctx, absPath) + if err != nil { + var aerr *apierr.APIError + if !errors.As(err, &aerr) { + return nil, err + } + + // This API returns a 404 if the file doesn't exist. + if aerr.StatusCode == http.StatusNotFound { + if aerr.ErrorCode == "RESOURCE_DOES_NOT_EXIST" { + return nil, FileDoesNotExistError{absPath} + } + } + + return nil, err + } + + return dbfsFileInfo{*info}, nil +} diff --git a/libs/filer/filer.go b/libs/filer/filer.go index e54efb96..ff01ea79 100644 --- a/libs/filer/filer.go +++ b/libs/filer/filer.go @@ -68,4 +68,7 @@ type Filer interface { // Creates directory at `path`, creating any intermediate directories as required. Mkdir(ctx context.Context, path string) error + + // Stat returns information about the file at `path`. + Stat(ctx context.Context, name string) (fs.FileInfo, error) } diff --git a/libs/filer/workspace_files_client.go b/libs/filer/workspace_files_client.go index b9f0f3db..967f9a1d 100644 --- a/libs/filer/workspace_files_client.go +++ b/libs/filer/workspace_files_client.go @@ -256,3 +256,26 @@ func (w *WorkspaceFilesClient) Mkdir(ctx context.Context, name string) error { Path: dirPath, }) } + +func (w *WorkspaceFilesClient) Stat(ctx context.Context, name string) (fs.FileInfo, error) { + absPath, err := w.root.Join(name) + if err != nil { + return nil, err + } + + info, err := w.workspaceClient.Workspace.GetStatusByPath(ctx, absPath) + if err != nil { + // If we got an API error we deal with it below. + var aerr *apierr.APIError + if !errors.As(err, &aerr) { + return nil, err + } + + // This API returns a 404 if the specified path does not exist. + if aerr.StatusCode == http.StatusNotFound { + return nil, FileDoesNotExistError{absPath} + } + } + + return wsfsFileInfo{*info}, nil +}