mirror of
https://github.com/rclone/rclone
synced 2024-12-03 13:14:37 +01:00
fs: define the optional interface SetMetadata and implement it in wrapping backends
This also implements backend integration tests for the feature
This commit is contained in:
parent
e9e9feb21e
commit
cc634213a5
@ -36,6 +36,7 @@ func TestIntegration(t *testing.T) {
|
||||
"GetTier",
|
||||
"SetTier",
|
||||
"Metadata",
|
||||
"SetMetadata",
|
||||
},
|
||||
UnimplementableFsMethods: []string{
|
||||
"PublicLink",
|
||||
|
@ -1119,6 +1119,17 @@ func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
|
||||
return do.Metadata(ctx)
|
||||
}
|
||||
|
||||
// SetMetadata sets metadata for an Object
|
||||
//
|
||||
// It should return fs.ErrorNotImplemented if it can't set metadata
|
||||
func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
|
||||
do, ok := o.Object.(fs.SetMetadataer)
|
||||
if !ok {
|
||||
return fs.ErrorNotImplemented
|
||||
}
|
||||
return do.SetMetadata(ctx, metadata)
|
||||
}
|
||||
|
||||
// SetTier performs changing storage tier of the Object if
|
||||
// multiple storage classes supported
|
||||
func (o *Object) SetTier(tier string) error {
|
||||
|
@ -1286,6 +1286,17 @@ func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
|
||||
return do.Metadata(ctx)
|
||||
}
|
||||
|
||||
// SetMetadata sets metadata for an Object
|
||||
//
|
||||
// It should return fs.ErrorNotImplemented if it can't set metadata
|
||||
func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
|
||||
do, ok := o.Object.(fs.SetMetadataer)
|
||||
if !ok {
|
||||
return fs.ErrorNotImplemented
|
||||
}
|
||||
return do.SetMetadata(ctx, metadata)
|
||||
}
|
||||
|
||||
// Hash returns the selected checksum of the file
|
||||
// If no checksum is available it returns ""
|
||||
func (o *Object) Hash(ctx context.Context, ht hash.Type) (string, error) {
|
||||
|
@ -1248,6 +1248,17 @@ func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
|
||||
return do.Metadata(ctx)
|
||||
}
|
||||
|
||||
// SetMetadata sets metadata for an Object
|
||||
//
|
||||
// It should return fs.ErrorNotImplemented if it can't set metadata
|
||||
func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
|
||||
do, ok := o.Object.(fs.SetMetadataer)
|
||||
if !ok {
|
||||
return fs.ErrorNotImplemented
|
||||
}
|
||||
return do.SetMetadata(ctx, metadata)
|
||||
}
|
||||
|
||||
// MimeType returns the content type of the Object if
|
||||
// known, or "" if not
|
||||
//
|
||||
|
@ -535,6 +535,17 @@ func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
|
||||
return do.Metadata(ctx)
|
||||
}
|
||||
|
||||
// SetMetadata sets metadata for an Object
|
||||
//
|
||||
// It should return fs.ErrorNotImplemented if it can't set metadata
|
||||
func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
|
||||
do, ok := o.Object.(fs.SetMetadataer)
|
||||
if !ok {
|
||||
return fs.ErrorNotImplemented
|
||||
}
|
||||
return do.SetMetadata(ctx, metadata)
|
||||
}
|
||||
|
||||
// Check the interfaces are satisfied
|
||||
var (
|
||||
_ fs.Fs = (*Fs)(nil)
|
||||
|
@ -322,6 +322,17 @@ func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
|
||||
return do.Metadata(ctx)
|
||||
}
|
||||
|
||||
// SetMetadata sets metadata for an Object
|
||||
//
|
||||
// It should return fs.ErrorNotImplemented if it can't set metadata
|
||||
func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
|
||||
do, ok := o.Object.(fs.SetMetadataer)
|
||||
if !ok {
|
||||
return fs.ErrorNotImplemented
|
||||
}
|
||||
return do.SetMetadata(ctx, metadata)
|
||||
}
|
||||
|
||||
// Metadata returns metadata for an DirEntry
|
||||
//
|
||||
// It should return nil if there is no Metadata
|
||||
|
@ -242,6 +242,7 @@ type FullObject interface {
|
||||
GetTierer
|
||||
SetTierer
|
||||
Metadataer
|
||||
SetMetadataer
|
||||
}
|
||||
|
||||
// ObjectOptionalInterfaces returns the names of supported and
|
||||
@ -273,6 +274,9 @@ func ObjectOptionalInterfaces(o Object) (supported, unsupported []string) {
|
||||
_, ok = o.(Metadataer)
|
||||
store(ok, "Metadata")
|
||||
|
||||
_, ok = o.(SetMetadataer)
|
||||
store(ok, "SetMetadata")
|
||||
|
||||
return supported, unsupported
|
||||
}
|
||||
|
||||
|
@ -1668,6 +1668,67 @@ func Run(t *testing.T, opt *Opt) {
|
||||
} // else: Have some metadata here we didn't write - can't really check it!
|
||||
})
|
||||
|
||||
// TestObjectSetMetadata tests the SetMetadata of the object
|
||||
t.Run("ObjectSetMetadata", func(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
ctx, ci := fs.AddConfig(ctx)
|
||||
ci.Metadata = true
|
||||
features := f.Features()
|
||||
|
||||
// Test to see if SetMetadata is supported on an existing object before creating a new one
|
||||
obj := fstest.NewObject(ctx, t, f, file1.Path)
|
||||
_, objectHasSetMetadata := obj.(fs.SetMetadataer)
|
||||
if !objectHasSetMetadata {
|
||||
t.Skip("SetMetadata method not supported")
|
||||
}
|
||||
if !features.Overlay {
|
||||
require.True(t, features.WriteMetadata, "Features.WriteMetadata is false but Object.SetMetadata found")
|
||||
}
|
||||
if !features.ReadMetadata {
|
||||
t.Skip("SetMetadata can't be tested without ReadMetadata")
|
||||
}
|
||||
|
||||
// Create file with metadata
|
||||
const fileName = "test set metadata.txt"
|
||||
t1 := fstest.Time("2003-02-03T04:05:06.499999999Z")
|
||||
t2 := fstest.Time("2004-03-03T04:05:06.499999999Z")
|
||||
contents := random.String(100)
|
||||
file := fstest.NewItem(fileName, contents, t1)
|
||||
var testMetadata = fs.Metadata{
|
||||
// System metadata supported by all backends
|
||||
"mtime": t1.Format(time.RFC3339Nano),
|
||||
// User metadata
|
||||
"potato": "jersey",
|
||||
}
|
||||
obj = PutTestContentsMetadata(ctx, t, f, &file, contents, true, "text/plain", testMetadata)
|
||||
fstest.CheckEntryMetadata(ctx, t, f, obj, testMetadata)
|
||||
do, objectHasSetMetadata := obj.(fs.SetMetadataer)
|
||||
require.True(t, objectHasSetMetadata)
|
||||
|
||||
// Set new metadata
|
||||
err := do.SetMetadata(ctx, fs.Metadata{
|
||||
// System metadata supported by all backends
|
||||
"mtime": t2.Format(time.RFC3339Nano),
|
||||
// User metadata
|
||||
"potato": "royal",
|
||||
})
|
||||
if err == fs.ErrorNotImplemented {
|
||||
t.Log("SetMetadata returned fs.ErrorNotImplemented")
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
file.ModTime = t2
|
||||
fstest.CheckListing(t, f, []fstest.Item{file1, file2, file})
|
||||
|
||||
// Check metadata is correct
|
||||
fstest.CheckEntryMetadata(ctx, t, f, obj, ci.MetadataSet)
|
||||
obj = fstest.NewObject(ctx, t, f, fileName)
|
||||
fstest.CheckEntryMetadata(ctx, t, f, obj, ci.MetadataSet)
|
||||
}
|
||||
|
||||
// Remove test file
|
||||
require.NoError(t, obj.Remove(ctx))
|
||||
})
|
||||
|
||||
// TestObjectSetModTime tests that SetModTime works
|
||||
t.Run("ObjectSetModTime", func(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
|
Loading…
Reference in New Issue
Block a user