add readdir function, fix writing files on local filesystem if within folder.

This commit is contained in:
Shane C. 2024-11-20 10:19:07 -05:00
parent f14c451ea8
commit 98ee01a2d5
Signed by: Shane C.
GPG key ID: E46B5FEA35B22FF9
4 changed files with 102 additions and 4 deletions

6
go.mod
View file

@ -2,7 +2,10 @@ module egtyl.xyz/omnibill/fsys
go 1.23.2 go 1.23.2
require github.com/minio/minio-go/v7 v7.0.80 require (
github.com/minio/minio-go/v7 v7.0.80
github.com/stretchr/testify v1.9.0
)
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
@ -15,7 +18,6 @@ require (
github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/xid v1.6.0 // indirect github.com/rs/xid v1.6.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/crypto v0.28.0 // indirect golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/sys v0.26.0 // indirect

1
go.sum
View file

@ -32,6 +32,7 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -19,6 +19,7 @@ var (
ErrInvalidStorageType = errors.New("invalid storage type") ErrInvalidStorageType = errors.New("invalid storage type")
ErrInvalidPath = errors.New("invalid path") ErrInvalidPath = errors.New("invalid path")
ErrFileNotFound = errors.New("file not found") ErrFileNotFound = errors.New("file not found")
ErrFolderNotFound = errors.New("folder not found")
) )
type Storage struct { type Storage struct {
@ -182,6 +183,17 @@ func (s *Storage) Write(name string, data []byte, opts WriteOptions) error {
} }
return nil return nil
} else { } else {
if fileInfo, err := os.Stat(filepath.Join(s.config.Path, filepath.Dir(name))); err != nil && errors.Is(err, fs.ErrNotExist) {
if err := os.MkdirAll(filepath.Join(s.config.Path, filepath.Dir(name)), 0740); err != nil {
return err
}
} else if err != nil {
return err
} else {
if !fileInfo.IsDir() {
return ErrInvalidPath
}
}
return os.WriteFile(filepath.Join(s.config.Path, name), data, 0600) return os.WriteFile(filepath.Join(s.config.Path, name), data, 0600)
} }
} }
@ -205,6 +217,7 @@ type FileInfo struct {
Size int64 Size int64
ModTime time.Time ModTime time.Time
IsDir bool IsDir bool
// The file permissions, will always be 0777 when using S3.
Mode os.FileMode Mode os.FileMode
} }
@ -228,7 +241,7 @@ func (s *Storage) Stat(name string) (*FileInfo, error) {
fileInfo.Name = objInfo.Key fileInfo.Name = objInfo.Key
fileInfo.Size = objInfo.Size fileInfo.Size = objInfo.Size
fileInfo.ModTime = objInfo.LastModified fileInfo.ModTime = objInfo.LastModified
fileInfo.Mode = 0777 // Used as a fake perm. Doesn't actually do anything. fileInfo.Mode = fs.FileMode(0777) // Used as a fake perm. Doesn't actually do anything.
} else { } else {
fInfo, err := os.Stat(filepath.Join(s.config.Path, name)) fInfo, err := os.Stat(filepath.Join(s.config.Path, name))
if err != nil { if err != nil {
@ -301,3 +314,52 @@ func (s *Storage) Move(name string, dest string) error {
return os.Rename(filepath.Join(s.config.Path, name), filepath.Join(s.config.Path, dest)) return os.Rename(filepath.Join(s.config.Path, name), filepath.Join(s.config.Path, dest))
} }
} }
// ReadDir reads all files within a certain directory.
// It takes a folder name as its parameter.
// The function returns an array of FileInfo, and an error, if any.
func (s *Storage) ReadDir(name string) ([]FileInfo, error) {
var fileInfo []FileInfo
if s.s3Client != nil {
for object := range s.s3Client.ListObjects(s.ctx, s.config.S3BucketName, minio.ListObjectsOptions{
Prefix: name + "/",
}) {
if object.Err != nil {
minioErr := minio.ToErrorResponse(object.Err)
if minioErr.Code == "NoSuchKey" {
return nil, ErrFolderNotFound
}
return nil, object.Err
}
fileInfo = append(fileInfo, FileInfo{
Name: object.Key,
Size: object.Size,
ModTime: object.LastModified,
Mode: fs.FileMode(0777),
IsDir: object.Size == 0,
})
}
} else {
dirInfo, err := os.ReadDir(filepath.Join(s.config.Path, name))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil, ErrFolderNotFound
}
return nil, err
}
for _, fInfo := range dirInfo {
info, err := fInfo.Info()
if err != nil {
return nil, err
}
fileInfo = append(fileInfo, FileInfo{
Name: info.Name(),
Size: info.Size(),
ModTime: info.ModTime(),
Mode: info.Mode(),
IsDir: info.IsDir(),
})
}
}
return fileInfo, nil
}

View file

@ -16,9 +16,16 @@ func TestStorageLocal(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
t.Log("== Write ==") t.Log("== Write ==")
err = stor.Write("test.txt", []byte("hi"), WriteOptions{}) err = stor.Write("test.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err) assert.NoError(t, err)
err = stor.Write("testing/1.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err)
err = stor.Write("testing/2.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err)
err = stor.Write("testing/test/2.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err)
t.Log("== Stat ==") t.Log("== Stat ==")
fileInfo, err := stor.Stat("test.txt") fileInfo, err := stor.Stat("test.txt")
assert.NoError(t, err) assert.NoError(t, err)
@ -31,6 +38,12 @@ func TestStorageLocal(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, []byte("hi"), fileContent) assert.Equal(t, []byte("hi"), fileContent)
t.Log("== ReadDir ==")
files, err := stor.ReadDir("testing")
assert.NoError(t, err)
assert.Equal(t, 3, len(files))
t.Log("== Copy ==") t.Log("== Copy ==")
err = stor.Copy("test.txt", "test2.txt") err = stor.Copy("test.txt", "test2.txt")
assert.NoError(t, err) assert.NoError(t, err)
@ -81,10 +94,19 @@ func TestStorageS3(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
t.Log("== Write ==") t.Log("== Write ==")
err = stor.Write("test.txt", []byte("hi"), WriteOptions{}) err = stor.Write("test.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err) assert.NoError(t, err)
err = stor.Write("testing/1.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err)
err = stor.Write("testing/2.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err)
err = stor.Write("testing/test/2.txt", []byte("hi"), WriteOptions{})
assert.NoError(t, err)
t.Log("== Stat ==") t.Log("== Stat ==")
fileInfo, err := stor.Stat("test.txt") fileInfo, err := stor.Stat("test.txt")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "test.txt", fileInfo.Name) assert.Equal(t, "test.txt", fileInfo.Name)
@ -92,11 +114,19 @@ func TestStorageS3(t *testing.T) {
assert.Equal(t, fs.FileMode(0777), fileInfo.Mode) assert.Equal(t, fs.FileMode(0777), fileInfo.Mode)
t.Log("== Read ==") t.Log("== Read ==")
fileContent, err := stor.Read("test.txt") fileContent, err := stor.Read("test.txt")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, []byte("hi"), fileContent) assert.Equal(t, []byte("hi"), fileContent)
t.Log("== ReadDir ==")
files, err := stor.ReadDir("testing")
assert.NoError(t, err)
assert.Equal(t, 3, len(files))
t.Log("== Copy ==") t.Log("== Copy ==")
err = stor.Copy("test.txt", "test2.txt") err = stor.Copy("test.txt", "test2.txt")
assert.NoError(t, err) assert.NoError(t, err)
@ -107,6 +137,7 @@ func TestStorageS3(t *testing.T) {
assert.Equal(t, int64(len("hi")), copyFileInfo.Size) assert.Equal(t, int64(len("hi")), copyFileInfo.Size)
t.Log("== Move ==") t.Log("== Move ==")
err = stor.Move("test2.txt", "test3.txt") err = stor.Move("test2.txt", "test3.txt")
assert.NoError(t, err) assert.NoError(t, err)
@ -117,6 +148,7 @@ func TestStorageS3(t *testing.T) {
assert.Equal(t, int64(len("hi")), moveFileInfo.Size) assert.Equal(t, int64(len("hi")), moveFileInfo.Size)
t.Log("== Open ==") t.Log("== Open ==")
file, err := stor.Open("test.txt") file, err := stor.Open("test.txt")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "test.txt", file.Name) assert.Equal(t, "test.txt", file.Name)
@ -129,6 +161,7 @@ func TestStorageS3(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
t.Log("== Delete ==") t.Log("== Delete ==")
err = stor.Delete("test.txt") err = stor.Delete("test.txt")
assert.NoError(t, err) assert.NoError(t, err)