Skip to content

Commit c5553c4

Browse files
Merge pull request #243 from dropbox/migrate-search-v2
Migrate search from deprecated Search to SearchV2 API
2 parents 2d4ca30 + 74cef13 commit c5553c4

3 files changed

Lines changed: 144 additions & 21 deletions

File tree

cmd/mock_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type mockFilesClient struct {
2222
listFolderContinueFn func(arg *files.ListFolderContinueArg) (*files.ListFolderResult, error)
2323
moveV2Fn func(arg *files.RelocationArg) (*files.RelocationResult, error)
2424
permanentlyDeleteFn func(arg *files.DeleteArg) error
25+
searchV2Fn func(arg *files.SearchV2Arg) (*files.SearchV2Result, error)
26+
searchContinueV2Fn func(arg *files.SearchV2ContinueArg) (*files.SearchV2Result, error)
2527
}
2628

2729
func (m *mockFilesClient) Download(arg *files.DownloadArg) (*files.FileMetadata, io.ReadCloser, error) {
@@ -242,9 +244,15 @@ func (m *mockFilesClient) Search(arg *files.SearchArg) (*files.SearchResult, err
242244
return nil, nil
243245
}
244246
func (m *mockFilesClient) SearchV2(arg *files.SearchV2Arg) (*files.SearchV2Result, error) {
247+
if m.searchV2Fn != nil {
248+
return m.searchV2Fn(arg)
249+
}
245250
return nil, nil
246251
}
247252
func (m *mockFilesClient) SearchContinueV2(arg *files.SearchV2ContinueArg) (*files.SearchV2Result, error) {
253+
if m.searchContinueV2Fn != nil {
254+
return m.searchContinueV2Fn(arg)
255+
}
248256
return nil, nil
249257
}
250258
func (m *mockFilesClient) TagsAdd(arg *files.AddTagArg) error { return nil }

cmd/search.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"errors"
1919
"fmt"
2020
"io"
21-
"os"
2221
"strings"
2322
"text/tabwriter"
2423

@@ -31,7 +30,6 @@ func search(cmd *cobra.Command, args []string) (err error) {
3130
return errors.New("`search` requires a `query` argument")
3231
}
3332

34-
// Parse path scope, if provided.
3533
var scope string
3634
if len(args) == 2 {
3735
scope = args[1]
@@ -40,33 +38,55 @@ func search(cmd *cobra.Command, args []string) (err error) {
4038
}
4139
}
4240

43-
arg := files.NewSearchArg(scope, args[0])
41+
arg := files.NewSearchV2Arg(args[0])
42+
if scope != "" {
43+
opts := files.NewSearchOptions()
44+
opts.Path = scope
45+
arg.Options = opts
46+
}
4447

45-
dbx := files.New(config)
46-
res, err := dbx.Search(arg)
48+
dbx := filesNewFunc(config)
49+
res, err := dbx.SearchV2(arg)
4750
if err != nil {
48-
return
51+
return err
52+
}
53+
54+
var entries []files.IsMetadata
55+
for _, m := range res.Matches {
56+
if m.Metadata != nil && m.Metadata.Metadata != nil {
57+
entries = append(entries, m.Metadata.Metadata)
58+
}
59+
}
60+
61+
for res.HasMore {
62+
contArg := files.NewSearchV2ContinueArg(res.Cursor)
63+
res, err = dbx.SearchContinueV2(contArg)
64+
if err != nil {
65+
return err
66+
}
67+
for _, m := range res.Matches {
68+
if m.Metadata != nil && m.Metadata.Metadata != nil {
69+
entries = append(entries, m.Metadata.Metadata)
70+
}
71+
}
4972
}
5073

5174
opts := parseLsOptions(cmd)
75+
sortEntries(entries, opts)
5276

53-
return renderSearchResults(os.Stdout, res, opts)
77+
return commandOutput(cmd).RenderText(func(w io.Writer) error {
78+
return renderSearchResults(w, entries, opts)
79+
})
5480
}
5581

56-
func renderSearchResults(out io.Writer, res *files.SearchResult, opts listOptions) error {
82+
func renderSearchResults(out io.Writer, entries []files.IsMetadata, opts listOptions) error {
5783
w := new(tabwriter.Writer)
5884
w.Init(out, 4, 8, 1, ' ', 0)
5985

6086
if opts.long {
6187
_, _ = fmt.Fprint(w, "Revision\tSize\tLast modified\tPath\n")
6288
}
6389

64-
entries := make([]files.IsMetadata, 0, len(res.Matches))
65-
for _, m := range res.Matches {
66-
entries = append(entries, m.Metadata)
67-
}
68-
sortEntries(entries, opts)
69-
7090
for _, entry := range entries {
7191
switch f := entry.(type) {
7292
case *files.FileMetadata:

cmd/search_test.go

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"strings"
66
"testing"
77

8+
"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox"
89
"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox/files"
10+
"github.com/spf13/cobra"
911
)
1012

1113
func TestSearchArgValidation(t *testing.T) {
@@ -23,17 +25,17 @@ func TestSearchPathScopeValidation(t *testing.T) {
2325
}
2426

2527
func TestRenderSearchResultsSeparatesMatchesWithNewlines(t *testing.T) {
26-
res := files.NewSearchResult([]*files.SearchMatch{
27-
files.NewSearchMatch(nil, &files.FileMetadata{
28+
entries := []files.IsMetadata{
29+
&files.FileMetadata{
2830
Metadata: files.Metadata{PathDisplay: "/first.txt"},
29-
}),
30-
files.NewSearchMatch(nil, &files.FolderMetadata{
31+
},
32+
&files.FolderMetadata{
3133
Metadata: files.Metadata{PathDisplay: "/second"},
32-
}),
33-
}, false, 0)
34+
},
35+
}
3436

3537
var out bytes.Buffer
36-
if err := renderSearchResults(&out, res, listOptions{long: false}); err != nil {
38+
if err := renderSearchResults(&out, entries, listOptions{long: false}); err != nil {
3739
t.Fatalf("renderSearchResults returned error: %v", err)
3840
}
3941

@@ -49,3 +51,96 @@ func TestRenderSearchResultsSeparatesMatchesWithNewlines(t *testing.T) {
4951
t.Errorf("second rendered match = %q, want %q", got, want)
5052
}
5153
}
54+
55+
func TestRenderSearchResultsLongModeIncludesHeader(t *testing.T) {
56+
entries := []files.IsMetadata{
57+
&files.FileMetadata{
58+
Metadata: files.Metadata{PathDisplay: "/first.txt"},
59+
Rev: "abc123",
60+
Size: 42,
61+
},
62+
}
63+
64+
var out bytes.Buffer
65+
if err := renderSearchResults(&out, entries, listOptions{long: true}); err != nil {
66+
t.Fatalf("renderSearchResults returned error: %v", err)
67+
}
68+
69+
got := out.String()
70+
for _, want := range []string{"Revision", "Size", "Last modified", "Path", "abc123", "/first.txt"} {
71+
if !strings.Contains(got, want) {
72+
t.Errorf("output = %q, want to contain %q", got, want)
73+
}
74+
}
75+
}
76+
77+
func TestSearchUsesSearchV2AndCommandOutput(t *testing.T) {
78+
cmd, stdout := testSearchCmd()
79+
var firstArg *files.SearchV2Arg
80+
var continueCursor string
81+
82+
mock := &mockFilesClient{
83+
searchV2Fn: func(arg *files.SearchV2Arg) (*files.SearchV2Result, error) {
84+
firstArg = arg
85+
res := files.NewSearchV2Result([]*files.SearchMatchV2{
86+
searchMatch(&files.FileMetadata{
87+
Metadata: files.Metadata{PathDisplay: "/docs/first.txt"},
88+
}),
89+
}, true)
90+
res.Cursor = "cursor-1"
91+
return res, nil
92+
},
93+
searchContinueV2Fn: func(arg *files.SearchV2ContinueArg) (*files.SearchV2Result, error) {
94+
continueCursor = arg.Cursor
95+
return files.NewSearchV2Result([]*files.SearchMatchV2{
96+
searchMatch(&files.FolderMetadata{
97+
Metadata: files.Metadata{PathDisplay: "/docs/second"},
98+
}),
99+
}, false), nil
100+
},
101+
}
102+
stubFilesClient(t, mock)
103+
104+
if err := search(cmd, []string{"needle", "/docs"}); err != nil {
105+
t.Fatalf("search error: %v", err)
106+
}
107+
108+
if firstArg == nil {
109+
t.Fatal("SearchV2 was not called")
110+
}
111+
if firstArg.Query != "needle" {
112+
t.Errorf("query = %q, want %q", firstArg.Query, "needle")
113+
}
114+
if firstArg.Options == nil || firstArg.Options.Path != "/docs" {
115+
t.Fatalf("options path = %#v, want /docs", firstArg.Options)
116+
}
117+
if continueCursor != "cursor-1" {
118+
t.Errorf("continue cursor = %q, want cursor-1", continueCursor)
119+
}
120+
121+
got := stdout.String()
122+
for _, want := range []string{"/docs/first.txt", "/docs/second"} {
123+
if !strings.Contains(got, want) {
124+
t.Errorf("stdout = %q, want to contain %q", got, want)
125+
}
126+
}
127+
}
128+
129+
func testSearchCmd() (*cobra.Command, *bytes.Buffer) {
130+
var stdout bytes.Buffer
131+
cmd := &cobra.Command{Use: "search"}
132+
cmd.SetOut(&stdout)
133+
cmd.Flags().BoolP("long", "l", false, "")
134+
cmd.Flags().String("sort", "", "")
135+
cmd.Flags().BoolP("reverse", "r", false, "")
136+
cmd.Flags().String("time", "server", "")
137+
cmd.Flags().String("time-format", "", "")
138+
return cmd, &stdout
139+
}
140+
141+
func searchMatch(metadata files.IsMetadata) *files.SearchMatchV2 {
142+
return files.NewSearchMatchV2(&files.MetadataV2{
143+
Tagged: dropbox.Tagged{Tag: files.MetadataV2Metadata},
144+
Metadata: metadata,
145+
})
146+
}

0 commit comments

Comments
 (0)