Repository: rclone/rclone (language: go)

Issue:
jottacloud: metadata isn't set properly on server side copy and move
In 41b1250eafcb8018160659a68f22f2e31e50780c we added tests for correct behaviour for or Metadata on server side Move and Copy

At this point the integration tests for Jottacloud started failing

```
"go test -v -timeout 1h0m0s -remote TestJottacloud: -verbose -test.run '^TestIntegration$/^FsMkdir$/^FsPutFiles$/^FsCopy$/^Metadata$|^TestIntegration$/^FsMkdir$/^FsPutFiles$/^FsMove$/^Metadata$'" - Starting (try 5/5)
```

giving

```
=== RUN   TestIntegration/FsMkdir/FsPutFiles/FsCopy/Metadata
    fstest.go:122: 
        	Error Trace:	/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:122
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:127
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:146
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:190
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:303
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:333
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:339
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstests/fstests.go:1310
        	Error:      	Should be true
        	Test:       	TestIntegration/FsMkdir/FsPutFiles/FsCopy/Metadata
        	Messages:   	test metadata copied.txt: Modification time difference too big |9456h0m0.499999999s| > 1s (want 2004-03-03 04:05:06.499999999 +0000 UTC vs got 2003-02-03 04:05:06 +0000 UTC) (precision 1s)
    fstest.go:122: 
        	Error Trace:	/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:122
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:606
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstests/fstests.go:1313
        	Error:      	Should be true
        	Test:       	TestIntegration/FsMkdir/FsPutFiles/FsCopy/Metadata
        	Messages:   	test metadata copied.txt: Modification time difference too big |9456h0m0.499999999s| > 1s (want 2004-03-03 04:05:06.499999999 +0000 UTC vs got 2003-02-03 04:05:06 +0000 UTC) (precision 1s)
    fstest.go:122: 
        	Error Trace:	/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:122
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:606
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstests/fstests.go:1315
        	Error:      	Should be true
        	Test:       	TestIntegration/FsMkdir/FsPutFiles/FsCopy/Metadata
        	Messages:   	test metadata copied.txt: Modification time difference too big |9456h0m0.499999999s| > 1s (want 2004-03-03 04:05:06.499999999 +0000 UTC vs got 2003-02-03 04:05:06 +0000 UTC) (precision 1s)
```

and

```
=== RUN   TestIntegration/FsMkdir/FsPutFiles/FsMove/Metadata
    fstest.go:122: 
        	Error Trace:	/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:122
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:127
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:146
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:190
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:303
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:333
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:339
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstests/fstests.go:1418
        	Error:      	Should be true
        	Test:       	TestIntegration/FsMkdir/FsPutFiles/FsMove/Metadata
        	Messages:   	test metadata moved.txt: Modification time difference too big |9456h0m0.499999999s| > 1s (want 2004-03-03 04:05:06.499999999 +0000 UTC vs got 2003-02-03 04:05:06 +0000 UTC) (precision 1s)
    fstest.go:122: 
        	Error Trace:	/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:122
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:606
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstests/fstests.go:1421
        	Error:      	Should be true
        	Test:       	TestIntegration/FsMkdir/FsPutFiles/FsMove/Metadata
        	Messages:   	test metadata moved.txt: Modification time difference too big |9456h0m0.499999999s| > 1s (want 2004-03-03 04:05:06.499999999 +0000 UTC vs got 2003-02-03 04:05:06 +0000 UTC) (precision 1s)
    fstest.go:122: 
        	Error Trace:	/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:122
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstest.go:606
        	            				/home/rclone/go/src/github.com/rclone/rclone/fstest/fstests/fstests.go:1423
        	Error:      	Should be true
        	Test:       	TestIntegration/FsMkdir/FsPutFiles/FsMove/Metadata
        	Messages:   	test metadata moved.txt: Modification time difference too big |9456h0m0.499999999s| > 1s (want 2004-03-03 04:05:06.499999999 +0000 UTC vs got 2003-02-03 04:05:06 +0000 UTC) (precision 1s)
```

These are caused by the jottacloud backend not picking up metadata set by --metadata-set and the metadata mapper.

Similar fixes were made for google drive: 9f2ce2c7fc330f6441749a3e51325189e5001d48 and local: 6e85a39e9997189d1b828b49a2dc8049c67ad138

@albertony do you fancy taking a look at this since you added the metadata support to the jotta backend? Thank you

#### How to use GitHub

* Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are affected by the same issue.
* Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
* Subscribe to receive notifications on status change and new comments.



Discussion / hints:

Fix patch:
diff --git a/backend/jottacloud/jottacloud.go b/backend/jottacloud/jottacloud.go
index 6fb68b289015e..04e4bca10e650 100644
--- a/backend/jottacloud/jottacloud.go
+++ b/backend/jottacloud/jottacloud.go
@@ -1487,16 +1487,38 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
 		return nil, fs.ErrorCantMove
 	}
 
-	err := f.mkParentDir(ctx, remote)
+	meta, err := fs.GetMetadataOptions(ctx, f, src, fs.MetadataAsOpenOptions(ctx))
 	if err != nil {
 		return nil, err
 	}
+
+	if err := f.mkParentDir(ctx, remote); err != nil {
+		return nil, err
+	}
 	info, err := f.copyOrMove(ctx, "cp", srcObj.filePath(), remote)
 
-	// if destination was a trashed file then after a successful copy the copied file is still in trash (bug in api?)
-	if err == nil && bool(info.Deleted) && !f.opt.TrashedOnly && info.State == "COMPLETED" {
-		fs.Debugf(src, "Server-side copied to trashed destination, restoring")
-		info, err = f.createOrUpdate(ctx, remote, srcObj.createTime, srcObj.modTime, srcObj.size, srcObj.md5)
+	if err == nil {
+		var createTime time.Time
+		var createTimeMeta bool
+		var modTime time.Time
+		var modTimeMeta bool
+		if meta != nil {
+			createTime, createTimeMeta = srcObj.parseFsMetadataTime(meta, "btime")
+			if !createTimeMeta {
+				createTime = srcObj.createTime
+			}
+			modTime, modTimeMeta = srcObj.parseFsMetadataTime(meta, "mtime")
+			if !modTimeMeta {
+				modTime = srcObj.modTime
+			}
+		}
+		if bool(info.Deleted) && !f.opt.TrashedOnly && info.State == "COMPLETED" {
+			// Workaround necessary when destination was a trashed file, to avoid the copied file also being in trash (bug in api?)
+			fs.Debugf(src, "Server-side copied to trashed destination, restoring")
+			info, err = f.createOrUpdate(ctx, remote, createTime, modTime, info.Size, info.MD5)
+		} else if createTimeMeta || modTimeMeta {
+			info, err = f.createOrUpdate(ctx, remote, createTime, modTime, info.Size, info.MD5)
+		}
 	}
 
 	if err != nil {
@@ -1523,12 +1545,30 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
 		return nil, fs.ErrorCantMove
 	}
 
-	err := f.mkParentDir(ctx, remote)
+	meta, err := fs.GetMetadataOptions(ctx, f, src, fs.MetadataAsOpenOptions(ctx))
 	if err != nil {
 		return nil, err
 	}
+
+	if err := f.mkParentDir(ctx, remote); err != nil {
+		return nil, err
+	}
 	info, err := f.copyOrMove(ctx, "mv", srcObj.filePath(), remote)
 
+	if err != nil && meta != nil {
+		createTime, createTimeMeta := srcObj.parseFsMetadataTime(meta, "btime")
+		if !createTimeMeta {
+			createTime = srcObj.createTime
+		}
+		modTime, modTimeMeta := srcObj.parseFsMetadataTime(meta, "mtime")
+		if !modTimeMeta {
+			modTime = srcObj.modTime
+		}
+		if createTimeMeta || modTimeMeta {
+			info, err = f.createOrUpdate(ctx, remote, createTime, modTime, info.Size, info.MD5)
+		}
+	}
+
 	if err != nil {
 		return nil, fmt.Errorf("couldn't move file: %w", err)
 	}
@@ -1786,6 +1826,20 @@ func (o *Object) readMetaData(ctx context.Context, force bool) (err error) {
 	return o.setMetaData(info)
 }
 
+// parseFsMetadataTime parses a time string from fs.Metadata with key
+func (o *Object) parseFsMetadataTime(m fs.Metadata, key string) (t time.Time, ok bool) {
+	value, ok := m[key]
+	if ok {
+		var err error
+		t, err = time.Parse(time.RFC3339Nano, value) // metadata stores RFC3339Nano timestamps
+		if err != nil {
+			fs.Debugf(o, "failed to parse metadata %s: %q: %v", key, value, err)
+			ok = false
+		}
+	}
+	return t, ok
+}
+
 // ModTime returns the modification time of the object
 //
 // It attempts to read the objects mtime and if that isn't present the
@@ -1957,21 +2011,11 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
 	var createdTime string
 	var modTime string
 	if meta != nil {
-		if v, ok := meta["btime"]; ok {
-			t, err := time.Parse(time.RFC3339Nano, v) // metadata stores RFC3339Nano timestamps
-			if err != nil {
-				fs.Debugf(o, "failed to parse metadata btime: %q: %v", v, err)
-			} else {
-				createdTime = api.Rfc3339Time(t).String() // jottacloud api wants RFC3339 timestamps
-			}
+		if t, ok := o.parseFsMetadataTime(meta, "btime"); ok {
+			createdTime = api.Rfc3339Time(t).String() // jottacloud api wants RFC3339 timestamps
 		}
-		if v, ok := meta["mtime"]; ok {
-			t, err := time.Parse(time.RFC3339Nano, v)
-			if err != nil {
-				fs.Debugf(o, "failed to parse metadata mtime: %q: %v", v, err)
-			} else {
-				modTime = api.Rfc3339Time(t).String()
-			}
+		if t, ok := o.parseFsMetadataTime(meta, "mtime"); ok {
+			modTime = api.Rfc3339Time(t).String()
 		}
 	}
 	if modTime == "" { // prefer mtime in meta as Modified time, fallback to source ModTime


Test patch: