From daf2b70682fded9b8bf89f1ceb51637e64aa8352 Mon Sep 17 00:00:00 2001 From: Dustin Long Date: Fri, 12 Apr 2019 13:45:38 -0400 Subject: [PATCH] fix(list): qri list new command-line interface Calling `qri list [term]` will list only datasets that have the substring "term" in their name. To list another peer's datasets, use the flag --peer. --- actions/list_datasets.go | 6 ++- actions/list_datasets_test.go | 6 +-- base/dataset.go | 20 +++++++- base/dataset_test.go | 27 +++++++++-- cmd/list.go | 87 ++++++++++++----------------------- lib/datasets.go | 2 +- lib/params.go | 1 + p2p/datasets.go | 3 +- 8 files changed, 83 insertions(+), 69 deletions(-) diff --git a/actions/list_datasets.go b/actions/list_datasets.go index c6eb0a9f6..9552f81f5 100644 --- a/actions/list_datasets.go +++ b/actions/list_datasets.go @@ -10,7 +10,8 @@ import ( ) // ListDatasets lists a peer's datasets -func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, limit, offset int, RPC, publishedOnly, showVersions bool) (res []repo.DatasetRef, err error) { +func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, term string, limit, offset int, RPC, publishedOnly, showVersions bool) (res []repo.DatasetRef, err error) { + r := node.Repo pro, err := r.Profile() if err != nil { @@ -57,6 +58,7 @@ func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, limit, offset int, RPC } res, err = node.RequestDatasetsList(pro.PeerIDs[0], p2p.DatasetsListParams{ + Term: term, Limit: limit, Offset: offset, }) @@ -74,5 +76,5 @@ func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, limit, offset int, RPC return } - return base.ListDatasets(node.Repo, limit, offset, RPC, publishedOnly, showVersions) + return base.ListDatasets(node.Repo, term, limit, offset, RPC, publishedOnly, showVersions) } diff --git a/actions/list_datasets_test.go b/actions/list_datasets_test.go index 68e5d4d86..188f3fbd1 100644 --- a/actions/list_datasets_test.go +++ b/actions/list_datasets_test.go @@ -10,7 +10,7 @@ func TestListDatasets(t *testing.T) { node := newTestNode(t) addCitiesDataset(t, node) - res, err := ListDatasets(node, &repo.DatasetRef{Peername: "me"}, 1, 0, false, false, false) + res, err := ListDatasets(node, &repo.DatasetRef{Peername: "me"}, "", 1, 0, false, false, false) if err != nil { t.Error(err.Error()) } @@ -27,7 +27,7 @@ func TestListDatasetsNotFound(t *testing.T) { node := newTestNode(t) addCitiesDataset(t, node) - _, err := ListDatasets(node, &repo.DatasetRef{Peername: "not_found"}, 1, 0, false, false, false) + _, err := ListDatasets(node, &repo.DatasetRef{Peername: "not_found"}, "", 1, 0, false, false, false) if err == nil { t.Error("expected to get error") } @@ -41,7 +41,7 @@ func TestListDatasetsWithVersions(t *testing.T) { node := newTestNode(t) addCitiesDataset(t, node) - res, err := ListDatasets(node, &repo.DatasetRef{Peername: "me"}, 1, 0, false, false, true) + res, err := ListDatasets(node, &repo.DatasetRef{Peername: "me"}, "", 1, 0, false, false, true) if err != nil { t.Error(err.Error()) } diff --git a/base/dataset.go b/base/dataset.go index 7baf0c8b7..39033d765 100644 --- a/base/dataset.go +++ b/base/dataset.go @@ -75,14 +75,30 @@ func CloseDataset(ds *dataset.Dataset) (err error) { } // ListDatasets lists datasets from a repo -func ListDatasets(r repo.Repo, limit, offset int, RPC, publishedOnly, showVersions bool) (res []repo.DatasetRef, err error) { +func ListDatasets(r repo.Repo, term string, limit, offset int, RPC, publishedOnly, showVersions bool) (res []repo.DatasetRef, err error) { store := r.Store() - res, err = r.References(limit, offset) + num, err := r.RefCount() + if err != nil { + return nil, err + } + res, err = r.References(num, 0) if err != nil { log.Debug(err.Error()) return nil, fmt.Errorf("error getting dataset list: %s", err.Error()) } + if term != "" { + matched := make([]repo.DatasetRef, len(res)) + i := 0 + for _, ref := range res { + if strings.Contains(ref.Name, term) { + matched[i] = ref + i++ + } + } + res = matched[:i] + } + if publishedOnly { pub := make([]repo.DatasetRef, len(res)) i := 0 diff --git a/base/dataset_test.go b/base/dataset_test.go index db990ec68..645f0e7d1 100644 --- a/base/dataset_test.go +++ b/base/dataset_test.go @@ -25,7 +25,8 @@ func TestListDatasets(t *testing.T) { r := newTestRepo(t) ref := addCitiesDataset(t, r) - res, err := ListDatasets(r, 1, 0, false, false, false) + // Limit to one + res, err := ListDatasets(r, "", 1, 0, false, false, false) if err != nil { t.Error(err.Error()) } @@ -33,7 +34,8 @@ func TestListDatasets(t *testing.T) { t.Error("expected one dataset response") } - res, err = ListDatasets(r, 1, 0, false, true, false) + // Limit to published datasets + res, err = ListDatasets(r, "", 1, 0, false, true, false) if err != nil { t.Error(err.Error()) } @@ -46,7 +48,8 @@ func TestListDatasets(t *testing.T) { t.Fatal(err) } - res, err = ListDatasets(r, 1, 0, false, true, false) + // Limit to published datasets, after publishing cities + res, err = ListDatasets(r, "", 1, 0, false, true, false) if err != nil { t.Error(err.Error()) } @@ -54,6 +57,24 @@ func TestListDatasets(t *testing.T) { if len(res) != 1 { t.Error("expected one published dataset response") } + + // Limit to datasets with "city" in their name + res, err = ListDatasets(r, "city", 1, 0, false, false, false) + if err != nil { + t.Error(err.Error()) + } + if len(res) != 0 { + t.Error("expected no datasets with \"city\" in their name") + } + + // Limit to datasets with "cit" in their name + res, err = ListDatasets(r, "cit", 1, 0, false, false, false) + if err != nil { + t.Error(err.Error()) + } + if len(res) != 1 { + t.Error("expected one dataset with \"cit\" in their name") + } } func TestCreateDataset(t *testing.T) { diff --git a/cmd/list.go b/cmd/list.go index 0f474ac5a..184fcc1b0 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -3,7 +3,6 @@ package cmd import ( "encoding/json" "fmt" - "strings" "github.com/qri-io/dataset" "github.com/qri-io/ioes" @@ -23,19 +22,23 @@ func NewListCommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command { List shows lists of datasets, including names and current hashes. The default list is the latest version of all datasets you have on your local -qri repository. +qri repository. The first argument can be used to find datasets with a certain +substring in their name. When used in conjunction with ` + "`qri connect`" + `, list can list a peer's dataset. You must have ` + "`qri connect`" + ` running in a separate terminal window.`, Example: ` # show all of your datasets: qri list + # show datasets with the substring "new" in their name + qri list new + # to view the list of your peer's dataset, # in one terminal window: qri connect # in a separate terminal window, to show all of b5's datasets: - qri list b5`, + qri list --peer b5`, Annotations: map[string]string{ "group": "dataset", }, @@ -52,6 +55,7 @@ must have ` + "`qri connect`" + ` running in a separate terminal window.`, cmd.Flags().IntVarP(&o.Offset, "offset", "o", 0, "offset results, default 0") cmd.Flags().BoolVarP(&o.Published, "published", "p", false, "list only published datasets") cmd.Flags().BoolVarP(&o.ShowNumVersions, "num-versions", "n", false, "show number of versions") + cmd.Flags().StringVar(&o.Peername, "peer", "", "peer whose datasets to list") return cmd } @@ -63,6 +67,7 @@ type ListOptions struct { Format string Limit int Offset int + Term string Peername string Published bool ShowNumVersions bool @@ -73,7 +78,7 @@ type ListOptions struct { // Complete adds any missing configuration that can only be added just before calling Run func (o *ListOptions) Complete(f Factory, args []string) (err error) { if len(args) > 0 { - o.Peername = args[0] + o.Term = args[0] } o.DatasetRequests, err = f.DatasetRequests() return @@ -83,62 +88,30 @@ func (o *ListOptions) Complete(f Factory, args []string) (err error) { func (o *ListOptions) Run() (err error) { refs := []repo.DatasetRef{} + p := &lib.ListParams{ + Term: o.Term, + Peername: o.Peername, + Limit: o.Limit, + Offset: o.Offset, + Published: o.Published, + ShowNumVersions: o.ShowNumVersions, + } + if err = o.DatasetRequests.List(p, &refs); err != nil { + return err + } - if o.Peername == "" { - - p := &lib.ListParams{ - Limit: o.Limit, - Offset: o.Offset, - Published: o.Published, - ShowNumVersions: o.ShowNumVersions, - } - if err = o.DatasetRequests.List(p, &refs); err != nil { - return err - } - } else { - // if user provides "me/my_dataset", split into peername="me" and name="my_dataset" - peername := o.Peername - dsName := "" - parts := strings.Split(peername, "/") - if len(parts) > 1 { - peername = parts[0] - dsName = parts[1] - } - // TODO: It would be a bit more efficient to pass dsName to the ListParams - // and only retrieve information about that one dataset. - p := &lib.ListParams{ - Peername: peername, - Limit: o.Limit, - Offset: o.Offset, - ShowNumVersions: o.ShowNumVersions, - } - if err = o.DatasetRequests.List(p, &refs); err != nil { - return err - } - - replace := make([]repo.DatasetRef, 0) - for _, ref := range refs { - // remove profileID so names print pretty - ref.ProfileID = "" - // if there's a dsName that restricts the list operation, append matches - if dsName != "" && dsName == ref.Name { - replace = append(replace, ref) - } - } - - // if there's a dsName that restricts the list operation, only show that dataset - if dsName != "" { - refs = replace - } + for _, ref := range refs { + // remove profileID so names print pretty + ref.ProfileID = "" + } - if len(refs) == 0 { - if dsName != "" { - printInfo(o.Out, "%s has no datasets that match \"%s\"", peername, dsName) - } else { - printInfo(o.Out, "%s has no datasets", peername) - } - return + if len(refs) == 0 { + if o.Term == "" { + printInfo(o.Out, "%s has no datasets", o.Peername) + } else { + printInfo(o.Out, "%s has no datasets that match \"%s\"", o.Peername, o.Term) } + return } switch o.Format { diff --git a/lib/datasets.go b/lib/datasets.go index 530b09bea..732d7bddf 100644 --- a/lib/datasets.go +++ b/lib/datasets.go @@ -63,7 +63,7 @@ func (r *DatasetRequests) List(p *ListParams, res *[]repo.DatasetRef) error { p.Offset = 0 } - replies, err := actions.ListDatasets(r.node, ds, p.Limit, p.Offset, p.RPC, p.Published, p.ShowNumVersions) + replies, err := actions.ListDatasets(r.node, ds, p.Term, p.Limit, p.Offset, p.RPC, p.Published, p.ShowNumVersions) *res = replies return err diff --git a/lib/params.go b/lib/params.go index ae5d9184f..979896853 100644 --- a/lib/params.go +++ b/lib/params.go @@ -17,6 +17,7 @@ const DefaultPageSize = 100 // TODO - rename this to PageParams. type ListParams struct { ProfileID profile.ID + Term string Peername string OrderBy string Limit int diff --git a/p2p/datasets.go b/p2p/datasets.go index eb067d99c..c9eecc5e7 100644 --- a/p2p/datasets.go +++ b/p2p/datasets.go @@ -18,6 +18,7 @@ const listMax = 30 // DatasetsListParams encapsulates options for requesting datasets type DatasetsListParams struct { + Term string Limit int Offset int } @@ -70,7 +71,7 @@ func (n *QriNode) handleDatasetsList(ws *WrappedStream, msg Message) (hangup boo dlp.Limit = listMax } - refs, err := base.ListDatasets(n.Repo, dlp.Limit, dlp.Offset, false, true, false) + refs, err := base.ListDatasets(n.Repo, dlp.Term, dlp.Limit, dlp.Offset, false, true, false) if err != nil { log.Error(err) return