diff --git a/actions/list_datasets.go b/actions/list_datasets.go index b567d745b..0f13eb0bf 100644 --- a/actions/list_datasets.go +++ b/actions/list_datasets.go @@ -10,7 +10,7 @@ import ( ) // ListDatasets lists a peer's datasets -func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, limit, offset int, RPC, publishedOnly bool) (res []repo.DatasetRef, err error) { +func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, limit, offset int, RPC, publishedOnly, showVersions bool) (res []repo.DatasetRef, err error) { r := node.Repo pro, err := r.Profile() if err != nil { @@ -74,5 +74,5 @@ func ListDatasets(node *p2p.QriNode, ds *repo.DatasetRef, limit, offset int, RPC return } - return base.ListDatasets(node.Repo, limit, offset, RPC, publishedOnly) + return base.ListDatasets(node.Repo, limit, offset, RPC, publishedOnly, showVersions) } diff --git a/actions/list_datasets_test.go b/actions/list_datasets_test.go index a33b8c141..68e5d4d86 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) + res, err := ListDatasets(node, &repo.DatasetRef{Peername: "me"}, 1, 0, false, false, false) if err != nil { t.Error(err.Error()) } @@ -18,13 +18,16 @@ func TestListDatasets(t *testing.T) { if len(res) != 1 { t.Error("expected one dataset response") } + if res[0].Dataset.NumVersions != 0 { + t.Error("expected no versions were requested") + } } func TestListDatasetsNotFound(t *testing.T) { node := newTestNode(t) addCitiesDataset(t, node) - _, err := ListDatasets(node, &repo.DatasetRef{Peername: "not_found"}, 1, 0, false, false) + _, err := ListDatasets(node, &repo.DatasetRef{Peername: "not_found"}, 1, 0, false, false, false) if err == nil { t.Error("expected to get error") } @@ -33,3 +36,20 @@ func TestListDatasetsNotFound(t *testing.T) { t.Errorf("expected error \"%s\", got \"%s\"", expect, err.Error()) } } + +func TestListDatasetsWithVersions(t *testing.T) { + node := newTestNode(t) + addCitiesDataset(t, node) + + res, err := ListDatasets(node, &repo.DatasetRef{Peername: "me"}, 1, 0, false, false, true) + if err != nil { + t.Error(err.Error()) + } + + if len(res) != 1 { + t.Error("expected one dataset response") + } + if res[0].Dataset.NumVersions != 1 { + t.Error("expected one version") + } +} diff --git a/api/registry_test.go b/api/registry_test.go index 10bce1ed6..8bf8a8d87 100644 --- a/api/registry_test.go +++ b/api/registry_test.go @@ -28,7 +28,6 @@ func TestRegistryHandlers(t *testing.T) { runHandlerTestCases(t, "registryDatasets", h.RegistryDatasetsHandler, registryDatasetsCases) } - func TestRegistryGet(t *testing.T) { node, teardown := newTestNodeWithNumDatasets(t, 1) defer teardown() @@ -51,7 +50,6 @@ func TestRegistryGet(t *testing.T) { } } - func TestRegistryGetNotFound(t *testing.T) { node, teardown := newTestNodeWithNumDatasets(t, 1) defer teardown() diff --git a/base/dataset.go b/base/dataset.go index 3f592feba..b49ac3d95 100644 --- a/base/dataset.go +++ b/base/dataset.go @@ -64,7 +64,7 @@ func CloseDataset(ds *dataset.Dataset) (err error) { } // ListDatasets lists datasets from a repo -func ListDatasets(r repo.Repo, limit, offset int, RPC, publishedOnly bool) (res []repo.DatasetRef, err error) { +func ListDatasets(r repo.Repo, limit, offset int, RPC, publishedOnly, showVersions bool) (res []repo.DatasetRef, err error) { store := r.Store() res, err = r.References(limit, offset) if err != nil { @@ -99,6 +99,14 @@ func ListDatasets(r repo.Repo, limit, offset int, RPC, publishedOnly bool) (res if RPC { res[i].Dataset.Structure.Schema = nil } + + if showVersions { + dsVersions, err := DatasetLog(r, ref, 0, 0, false) + if err != nil { + return nil, err + } + res[i].Dataset.NumVersions = len(dsVersions) + } } // TODO: If renames.Renames is non-empty, apply it to r diff --git a/base/dataset_test.go b/base/dataset_test.go index 29b047257..6797022a3 100644 --- a/base/dataset_test.go +++ b/base/dataset_test.go @@ -25,7 +25,7 @@ func TestListDatasets(t *testing.T) { r := newTestRepo(t) ref := addCitiesDataset(t, r) - res, err := ListDatasets(r, 1, 0, false, false) + res, err := ListDatasets(r, 1, 0, false, false, false) if err != nil { t.Error(err.Error()) } @@ -33,7 +33,7 @@ func TestListDatasets(t *testing.T) { t.Error("expected one dataset response") } - res, err = ListDatasets(r, 1, 0, false, true) + res, err = ListDatasets(r, 1, 0, false, true, false) if err != nil { t.Error(err.Error()) } @@ -46,7 +46,7 @@ func TestListDatasets(t *testing.T) { t.Fatal(err) } - res, err = ListDatasets(r, 1, 0, false, true) + res, err = ListDatasets(r, 1, 0, false, true, false) if err != nil { t.Error(err.Error()) } diff --git a/cmd/list.go b/cmd/list.go index d0f253cd2..1ec6e3eda 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -51,6 +51,7 @@ must have ` + "`qri connect`" + ` running in a separate terminal window.`, cmd.Flags().IntVarP(&o.Limit, "limit", "l", 25, "limit results, default 25") 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.NumVersions, "num-versions", "n", false, "show number of versions") return cmd } @@ -59,11 +60,12 @@ must have ` + "`qri connect`" + ` running in a separate terminal window.`, type ListOptions struct { ioes.IOStreams - Format string - Limit int - Offset int - Peername string - Published bool + Format string + Limit int + Offset int + Peername string + Published bool + NumVersions bool DatasetRequests *lib.DatasetRequests } @@ -79,32 +81,20 @@ func (o *ListOptions) Complete(f Factory, args []string) (err error) { // Run executes the list command func (o *ListOptions) Run() (err error) { + + refs := []repo.DatasetRef{} + if o.Peername == "" { p := &lib.ListParams{ - Limit: o.Limit, - Offset: o.Offset, - Published: o.Published, + Limit: o.Limit, + Offset: o.Offset, + Published: o.Published, + NumVersions: o.NumVersions, } - refs := []repo.DatasetRef{} if err = o.DatasetRequests.List(p, &refs); err != nil { return err } - - switch o.Format { - case "": - for i, ref := range refs { - printDatasetRefInfo(o.Out, i+1, ref) - } - case dataset.JSONDataFormat.String(): - data, err := json.MarshalIndent(refs, "", " ") - if err != nil { - return err - } - fmt.Fprintf(o.Out, "%s\n", string(data)) - default: - return fmt.Errorf("unrecognized format: %s", o.Format) - } } else { // if user provides "me/my_dataset", split into peername="me" and name="my_dataset" peername := o.Peername @@ -114,13 +104,14 @@ func (o *ListOptions) Run() (err error) { 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, + Peername: peername, + Limit: o.Limit, + Offset: o.Offset, + NumVersions: o.NumVersions, } - refs := []repo.DatasetRef{} if err = o.DatasetRequests.List(p, &refs); err != nil { return err } @@ -148,21 +139,21 @@ func (o *ListOptions) Run() (err error) { } return } + } - switch o.Format { - case "": - for i, ref := range refs { - printDatasetRefInfo(o.Out, i+1, ref) - } - case dataset.JSONDataFormat.String(): - data, err := json.MarshalIndent(refs, "", " ") - if err != nil { - return err - } - fmt.Fprintf(o.Out, "%s\n", string(data)) - default: - return fmt.Errorf("unrecognized format: %s", o.Format) + switch o.Format { + case "": + for i, ref := range refs { + printDatasetRefInfo(o.Out, i+1, ref) + } + case dataset.JSONDataFormat.String(): + data, err := json.MarshalIndent(refs, "", " ") + if err != nil { + return err } + fmt.Fprintf(o.Out, "%s\n", string(data)) + default: + return fmt.Errorf("unrecognized format: %s", o.Format) } return nil diff --git a/cmd/print.go b/cmd/print.go index f3c6d5b80..862e6cc68 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -123,10 +123,27 @@ func printDatasetRefInfo(w io.Writer, i int, ref repo.DatasetRef) { fmt.Fprintf(w, " %s\n", ref.Path) } if ds != nil && ds.Structure != nil { - fmt.Fprintf(w, " %s, %d entries, %d errors", printByteInfo(ds.Structure.Length), ds.Structure.Entries, ds.Structure.ErrCount) + fmt.Fprintf(w, " %s", printByteInfo(ds.Structure.Length)) + if ds.Structure.Entries == 1 { + fmt.Fprintf(w, ", %d entry", ds.Structure.Entries) + } else { + fmt.Fprintf(w, ", %d entries", ds.Structure.Entries) + } + if ds.Structure.ErrCount == 1 { + fmt.Fprintf(w, ", %d error", ds.Structure.ErrCount) + } else { + fmt.Fprintf(w, ", %d errors", ds.Structure.ErrCount) + } + if ds.NumVersions == 0 { + // nothing + } else if ds.NumVersions == 1 { + fmt.Fprintf(w, ", %d version", ds.NumVersions) + } else { + fmt.Fprintf(w, ", %d versions", ds.NumVersions) + } } - fmt.Fprintln(w, "") + fmt.Fprintf(w, "\n") } func printSearchResult(w io.Writer, i int, result lib.SearchResult) { diff --git a/lib/datasets.go b/lib/datasets.go index 23c02dc33..6576bf983 100644 --- a/lib/datasets.go +++ b/lib/datasets.go @@ -64,7 +64,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) + replies, err := actions.ListDatasets(r.node, ds, p.Limit, p.Offset, p.RPC, p.Published, p.NumVersions) *res = replies return err diff --git a/lib/params.go b/lib/params.go index 95c2949e8..92ca71dba 100644 --- a/lib/params.go +++ b/lib/params.go @@ -25,6 +25,8 @@ type ListParams struct { RPC bool // Published only applies to listing datasets Published bool + // NumVersions only applies to listing datasets + NumVersions bool } // NewListParams creates a ListParams from page & pagesize, pages are 1-indexed diff --git a/p2p/datasets.go b/p2p/datasets.go index 9f624a92c..eb067d99c 100644 --- a/p2p/datasets.go +++ b/p2p/datasets.go @@ -70,7 +70,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) + refs, err := base.ListDatasets(n.Repo, dlp.Limit, dlp.Offset, false, true, false) if err != nil { log.Error(err) return