drive: implement backend shortcut command for creating shortcuts #4098

This commit is contained in:
Nick Craig-Wood 2020-05-07 18:35:39 +01:00
parent bc0f487369
commit bb65974e2f

View file

@ -29,6 +29,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/cache"
"github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fs/config/configstruct" "github.com/rclone/rclone/fs/config/configstruct"
@ -2783,39 +2784,125 @@ func (f *Fs) changeServiceAccountFile(file string) (err error) {
return nil return nil
} }
var commandHelp = []fs.CommandHelp{ // Create a shortcut from (f, srcPath) to (dstFs, dstPath)
{ //
Name: "get", // Will not overwrite existing files
Short: "Get command for fetching the drive config parameters", func (f *Fs) makeShorcut(ctx context.Context, srcPath string, dstFs *Fs, dstPath string) (o fs.Object, err error) {
Long: `This is a get command which will be used to fetch the various drive config parameters srcFs := f
Usage Examples: // Find source
srcObj, err := srcFs.NewObject(ctx, srcPath)
var srcID string
isDir := false
if err != nil {
if err != fs.ErrorNotAFile {
return nil, errors.Wrap(err, "can't find source")
}
// source was a directory
srcID, err = srcFs.dirCache.FindDir(ctx, srcPath, false)
if err != nil {
return nil, errors.Wrap(err, "failed to find source dir")
}
isDir = true
rclone backend get drive: [-o service_account_file] [-o chunk_size] } else {
rclone rc backend/command command=get fs=drive: [-o service_account_file] [-o chunk_size] // source was a file
`, srcID = srcObj.(*Object).id
Opts: map[string]string{ }
"chunk_size": "show the current upload chunk size", srcID = actualID(srcID) // link to underlying object not to shortcut
"service_account_file": "show the current service account file",
},
},
{
Name: "set",
Short: "Set command for updating the drive config parameters",
Long: `This is a set command which will be used to update the various drive config parameters
Usage Examples: // Find destination
_, err = dstFs.NewObject(ctx, dstPath)
if err != fs.ErrorObjectNotFound {
if err == nil {
err = errors.New("existing file")
} else if err == fs.ErrorNotAFile {
err = errors.New("existing directory")
}
return nil, errors.Wrap(err, "not overwriting shortcut target")
}
rclone backend set drive: [-o service_account_file=sa.json] [-o chunk_size=67108864] // Create destination shortcut
rclone rc backend/command command=set fs=drive: [-o service_account_file=sa.json] [-o chunk_size=67108864] createInfo, err := dstFs.createFileInfo(ctx, dstPath, time.Now())
`, if err != nil {
Opts: map[string]string{ return nil, errors.Wrap(err, "shortcut destination failed")
"chunk_size": "update the current upload chunk size", }
"service_account_file": "update the current service account file", createInfo.MimeType = shortcutMimeType
}, createInfo.ShortcutDetails = &drive.FileShortcutDetails{
}, TargetId: srcID,
}
var info *drive.File
err = dstFs.pacer.CallNoRetry(func() (bool, error) {
info, err = dstFs.svc.Files.Create(createInfo).
Fields(partialFields).
SupportsAllDrives(true).
KeepRevisionForever(dstFs.opt.KeepRevisionForever).
Do()
return dstFs.shouldRetry(err)
})
if err != nil {
return nil, errors.Wrap(err, "shortcut creation failed")
}
if isDir {
return nil, nil
}
return dstFs.newObjectWithInfo(dstPath, info)
} }
var commandHelp = []fs.CommandHelp{{
Name: "get",
Short: "Get command for fetching the drive config parameters",
Long: `This is a get command which will be used to fetch the various drive config parameters
Usage Examples:
rclone backend get drive: [-o service_account_file] [-o chunk_size]
rclone rc backend/command command=get fs=drive: [-o service_account_file] [-o chunk_size]
`,
Opts: map[string]string{
"chunk_size": "show the current upload chunk size",
"service_account_file": "show the current service account file",
},
}, {
Name: "set",
Short: "Set command for updating the drive config parameters",
Long: `This is a set command which will be used to update the various drive config parameters
Usage Examples:
rclone backend set drive: [-o service_account_file=sa.json] [-o chunk_size=67108864]
rclone rc backend/command command=set fs=drive: [-o service_account_file=sa.json] [-o chunk_size=67108864]
`,
Opts: map[string]string{
"chunk_size": "update the current upload chunk size",
"service_account_file": "update the current service account file",
},
}, {
Name: "shortcut",
Short: "Create shortcuts from files or directories",
Long: `This command creates shortcuts from files or directories.
Usage:
rclone backend shortcut drive: source_item destination_shortcut
rclone backend shortcut drive: source_item -o target=drive2: destination_shortcut
In the first example this creates a shortcut from the "source_item"
which can be a file or a directory to the "destination_shortcut". The
"source_item" and the "destination_shortcut" should be relative paths
from "drive:"
In the second example this creates a shortcut from the "source_item"
relative to "drive:" to the "destination_shortcut" relative to
"drive2:". This may fail with a permission error if the user
authenticated with "drive2:" can't read files from "drive:".
`,
Opts: map[string]string{
"target": "optional target remote for the shortcut destination",
},
}}
// Command the backend to run a named command // Command the backend to run a named command
// //
// The command run is name // The command run is name
@ -2860,6 +2947,23 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
out["chunk_size"] = chunkSizeMap out["chunk_size"] = chunkSizeMap
} }
return out, nil return out, nil
case "shortcut":
if len(arg) != 2 {
return nil, errors.New("need exactly 2 arguments")
}
dstFs := f
target, ok := opt["target"]
if ok {
targetFs, err := cache.Get(target)
if err != nil {
return nil, errors.Wrap(err, "couldn't find target")
}
dstFs, ok = targetFs.(*Fs)
if !ok {
return nil, errors.New("target is not a drive backend")
}
}
return f.makeShorcut(ctx, arg[0], dstFs, arg[1])
default: default:
return nil, fs.ErrorCommandNotFound return nil, fs.ErrorCommandNotFound
} }