cmd/syncthing: Fix /rest/system/browse for folder path completion (fixes #4590)
Two issues since the filesystem migration; - filepath.Base() doesn't do what the code expected when the path ends in a slash, and we make sure the path ends in a slash. - the return should be fully qualified, not just the last component. With this change it works as before, and passes the new test for it. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4591 LGTM: imsodin
This commit is contained in:
parent
673bd5fcc0
commit
a4147d9019
|
@ -1280,18 +1280,21 @@ func (s *apiService) getPeerCompletion(w http.ResponseWriter, r *http.Request) {
|
||||||
func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
|
func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
|
||||||
qs := r.URL.Query()
|
qs := r.URL.Query()
|
||||||
current := qs.Get("current")
|
current := qs.Get("current")
|
||||||
|
|
||||||
// Default value or in case of error unmarshalling ends up being basic fs.
|
// Default value or in case of error unmarshalling ends up being basic fs.
|
||||||
var fsType fs.FilesystemType
|
var fsType fs.FilesystemType
|
||||||
fsType.UnmarshalText([]byte(qs.Get("filesystem")))
|
fsType.UnmarshalText([]byte(qs.Get("filesystem")))
|
||||||
|
|
||||||
|
sendJSON(w, browseFiles(current, fsType))
|
||||||
|
}
|
||||||
|
|
||||||
|
func browseFiles(current string, fsType fs.FilesystemType) []string {
|
||||||
if current == "" {
|
if current == "" {
|
||||||
filesystem := fs.NewFilesystem(fsType, "")
|
filesystem := fs.NewFilesystem(fsType, "")
|
||||||
if roots, err := filesystem.Roots(); err == nil {
|
if roots, err := filesystem.Roots(); err == nil {
|
||||||
sendJSON(w, roots)
|
return roots
|
||||||
} else {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
search, _ := fs.ExpandTilde(current)
|
search, _ := fs.ExpandTilde(current)
|
||||||
pathSeparator := string(fs.PathSeparator)
|
pathSeparator := string(fs.PathSeparator)
|
||||||
|
@ -1300,7 +1303,13 @@ func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
|
||||||
search = search + pathSeparator
|
search = search + pathSeparator
|
||||||
}
|
}
|
||||||
searchDir := filepath.Dir(search)
|
searchDir := filepath.Dir(search)
|
||||||
searchFile := filepath.Base(search)
|
|
||||||
|
// The searchFile should be the last component of search, or empty if it
|
||||||
|
// ends with a path separator
|
||||||
|
var searchFile string
|
||||||
|
if !strings.HasSuffix(search, pathSeparator) {
|
||||||
|
searchFile = filepath.Base(search)
|
||||||
|
}
|
||||||
|
|
||||||
fs := fs.NewFilesystem(fsType, searchDir)
|
fs := fs.NewFilesystem(fsType, searchDir)
|
||||||
|
|
||||||
|
@ -1310,11 +1319,10 @@ func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, subdirectory := range subdirectories {
|
for _, subdirectory := range subdirectories {
|
||||||
info, err := fs.Stat(subdirectory)
|
info, err := fs.Stat(subdirectory)
|
||||||
if err == nil && info.IsDir() {
|
if err == nil && info.IsDir() {
|
||||||
ret = append(ret, subdirectory+pathSeparator)
|
ret = append(ret, filepath.Join(searchDir, subdirectory)+pathSeparator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret
|
||||||
sendJSON(w, ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *apiService) getCPUProf(w http.ResponseWriter, r *http.Request) {
|
func (s *apiService) getCPUProf(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -24,6 +26,7 @@ import (
|
||||||
"github.com/d4l3k/messagediff"
|
"github.com/d4l3k/messagediff"
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/events"
|
"github.com/syncthing/syncthing/lib/events"
|
||||||
|
"github.com/syncthing/syncthing/lib/fs"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
"github.com/syncthing/syncthing/lib/sync"
|
||||||
"github.com/thejerf/suture"
|
"github.com/thejerf/suture"
|
||||||
|
@ -957,3 +960,61 @@ func TestEventMasks(t *testing.T) {
|
||||||
t.Errorf("should have returned a valid, non-default event sub")
|
t.Errorf("should have returned a valid, non-default event sub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBrowse(t *testing.T) {
|
||||||
|
pathSep := string(os.PathSeparator)
|
||||||
|
|
||||||
|
tmpDir, err := ioutil.TempDir("", "syncthing")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
if err := os.Mkdir(filepath.Join(tmpDir, "dir"), 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(tmpDir, "file"), []byte("hello"), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect completion to return the full path to the completed
|
||||||
|
// directory, with an ending slash.
|
||||||
|
dirPath := filepath.Join(tmpDir, "dir") + pathSep
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
current string
|
||||||
|
returns []string
|
||||||
|
}{
|
||||||
|
// The direcotory without slash is completed to one with slash.
|
||||||
|
{tmpDir, []string{tmpDir + pathSep}},
|
||||||
|
// With slash it's completed to its contents.
|
||||||
|
// Dirs are given pathSeps.
|
||||||
|
// Files are not returned.
|
||||||
|
{tmpDir + pathSep, []string{dirPath}},
|
||||||
|
// Globbing is automatic based on prefix.
|
||||||
|
{tmpDir + pathSep + "d", []string{dirPath}},
|
||||||
|
{tmpDir + pathSep + "di", []string{dirPath}},
|
||||||
|
{tmpDir + pathSep + "dir", []string{dirPath}},
|
||||||
|
{tmpDir + pathSep + "f", nil},
|
||||||
|
{tmpDir + pathSep + "q", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
ret := browseFiles(tc.current, fs.FilesystemTypeBasic)
|
||||||
|
if !equalStrings(ret, tc.returns) {
|
||||||
|
t.Errorf("browseFiles(%q) => %q, expected %q", tc.current, ret, tc.returns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalStrings(a, b []string) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue