Skip to content

Commit ed1fd31

Browse files
committed
fix(get): Apply format to get when using a selector
Example: `qri get commit.title me/my_ds --format yaml`
1 parent 240f2fd commit ed1fd31

File tree

4 files changed

+122
-103
lines changed

4 files changed

+122
-103
lines changed

api/testdata/api.snapshot

16 Bytes
Binary file not shown.

lib/datasets.go

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,7 @@ func (r *DatasetRequests) Get(p *GetParams, res *GetResult) (err error) {
130130
return
131131
}
132132

133-
if p.Selector == "" {
134-
// `qri get` without a selector loads only the dataset head
135-
switch p.Format {
136-
case "json":
137-
if p.Concise {
138-
res.Bytes, err = json.Marshal(res.Dataset)
139-
} else {
140-
res.Bytes, err = json.MarshalIndent(res.Dataset, "", " ")
141-
}
142-
default:
143-
res.Bytes, err = yaml.Marshal(res.Dataset)
144-
}
145-
return err
146-
} else if p.Selector == "body" {
133+
if p.Selector == "body" {
147134
// `qri get body` loads the body
148135
if !p.All && (p.Limit < 0 || p.Offset < 0) {
149136
return fmt.Errorf("invalid limit / offset settings")
@@ -161,20 +148,36 @@ func (r *DatasetRequests) Get(p *GetParams, res *GetResult) (err error) {
161148
res.Bytes = bufData
162149
return err
163150
} else if p.Selector == "transform.script" && ds.Transform != nil && ds.Transform.ScriptFile() != nil {
164-
// accomodate two special case script file fields
151+
// `qri get transform.script` loads the transform script, as a special case
165152
// TODO (b5): this is a hack that should be generalized
166153
res.Bytes, err = ioutil.ReadAll(ds.Transform.ScriptFile())
167-
return
154+
return err
168155
} else if p.Selector == "viz.script" && ds.Viz != nil && ds.Viz.ScriptFile() != nil {
156+
// `qri get viz.script` loads the visualization script, as a special case
169157
res.Bytes, err = ioutil.ReadAll(ds.Viz.ScriptFile())
170-
return
158+
return err
171159
} else {
172-
// `qri get <selector>` loads the dataset but only returns the applicable component / field
173-
value, err := base.ApplyPath(res.Dataset, p.Selector)
174-
if err != nil {
175-
return err
160+
var value interface{}
161+
if p.Selector == "" {
162+
// `qri get` without a selector loads only the dataset head
163+
value = res.Dataset
164+
} else {
165+
// `qri get <selector>` loads only the applicable component / field
166+
value, err = base.ApplyPath(res.Dataset, p.Selector)
167+
if err != nil {
168+
return err
169+
}
170+
}
171+
switch p.Format {
172+
case "json":
173+
if p.Concise {
174+
res.Bytes, err = json.Marshal(value)
175+
} else {
176+
res.Bytes, err = json.MarshalIndent(value, "", " ")
177+
}
178+
default:
179+
res.Bytes, err = yaml.Marshal(value)
176180
}
177-
res.Bytes, err = json.MarshalIndent(value, "", " ")
178181
return err
179182
}
180183
}

lib/datasets_test.go

Lines changed: 96 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
package lib
22

33
import (
4-
"bytes"
54
"context"
6-
"encoding/csv"
75
"encoding/json"
86
"fmt"
97
"net/http"
108
"net/http/httptest"
119
"strconv"
10+
"strings"
1211
"sync"
1312
"testing"
1413
"time"
1514

15+
"github.com/ghodss/yaml"
1616
"github.com/qri-io/dataset"
1717
"github.com/qri-io/dataset/dsfs"
18+
"github.com/qri-io/dataset/dsio"
1819
"github.com/qri-io/dataset/dstest"
1920
"github.com/qri-io/dsdiff"
2021
"github.com/qri-io/jsonschema"
@@ -384,103 +385,118 @@ func TestDatasetRequestsGet(t *testing.T) {
384385
t.Fatalf("error loading dataset: %s", err.Error())
385386
}
386387

388+
moviesDs.OpenBodyFile(node.Repo.Filesystem())
389+
moviesBody := make([]interface{}, 0)
390+
moviesBodyFile := moviesDs.BodyFile()
391+
reader := dsio.NewCSVReader(moviesDs.Structure, moviesBodyFile)
392+
for {
393+
ent, err := reader.ReadEntry()
394+
if err != nil {
395+
if err.Error() == "EOF" {
396+
break
397+
}
398+
t.Fatal(err.Error())
399+
}
400+
moviesBody = append(moviesBody, ent.Value)
401+
}
402+
387403
cases := []struct {
388404
params *GetParams
389-
res *dataset.Dataset
390-
err string
405+
expect string
391406
}{
392-
// TODO: probably delete some of these
393-
{&GetParams{Path: "peer/ABC@abc"}, nil, "'peer/ABC@abc' is not a valid dataset reference"},
394-
// repo.DatasetRef{Peername: "peer", Path: ref.Path, Name: "ABC"}.String()
395-
{&GetParams{Path: fmt.Sprintf("peer/ABC@%s", ref.Path)}, nil, ""},
396-
// repo.DatasetRef{Peername: "peer", Path: ref.Path, Name: "movies"}.String()
397-
{&GetParams{Path: "peer/movies"}, moviesDs, ""},
398-
// repo.DatasetRef{Peername: "peer", Path: ref.Path, Name: "cats"}.String()
399-
// {, moviesDs, ""},
400-
}
407+
{&GetParams{Path: "peer/ABC@abc"}, "'peer/ABC@abc' is not a valid dataset reference"},
401408

402-
req := NewDatasetRequests(node, nil)
403-
for i, c := range cases {
404-
got := &GetResult{}
405-
err := req.Get(c.params, got)
406-
if !(err == nil && c.err == "" || err != nil && err.Error() == c.err) {
407-
t.Errorf("case %d error mismatch: expected: %s, got: %s", i, c.err, err)
408-
continue
409-
}
410-
// TODO (dlong): Inspect the contents of `got`
411-
}
412-
}
409+
{&GetParams{Path: fmt.Sprintf("peer/ABC@%s", ref.Path)},
410+
componentToString(setDatasetName(moviesDs, "peer/ABC"), "yaml")},
413411

414-
func TestDatasetRequestsGetBody(t *testing.T) {
415-
rc, _ := regmock.NewMockServer()
416-
mr, err := testrepo.NewTestRepo(rc)
417-
if err != nil {
418-
t.Fatalf("error allocating test repo: %s", err.Error())
419-
return
420-
}
421-
node, err := p2p.NewQriNode(mr, config.DefaultP2PForTesting())
422-
if err != nil {
423-
t.Fatal(err.Error())
424-
}
412+
{&GetParams{Path: "peer/movies"},
413+
componentToString(setDatasetName(moviesDs, "peer/movies"), "yaml")},
425414

426-
moviesRef, err := mr.GetRef(repo.DatasetRef{Peername: "peer", Name: "movies"})
427-
if err != nil {
428-
t.Fatalf("error getting movies ref: %s", err.Error())
429-
}
430-
clRef, err := mr.GetRef(repo.DatasetRef{Peername: "peer", Name: "craigslist"})
431-
if err != nil {
432-
t.Fatalf("error getting craigslist ref: %s", err.Error())
433-
}
434-
sitemapRef, err := mr.GetRef(repo.DatasetRef{Peername: "peer", Name: "sitemap"})
435-
if err != nil {
436-
t.Fatalf("error getting sitemap ref: %s", err.Error())
437-
}
415+
{&GetParams{Path: "peer/movies", Format: "json"},
416+
componentToString(setDatasetName(moviesDs, "peer/movies"), "json")},
438417

439-
cases := []struct {
440-
p *GetParams
441-
resCount int
442-
err string
443-
}{
444-
{&GetParams{Selector: "body"}, 0, "repo: empty dataset reference"},
445-
{&GetParams{Selector: "body", Format: "json", Path: moviesRef.String(), Limit: 5, Offset: 0, All: false}, 5, ""},
446-
{&GetParams{Selector: "body", Format: "json", Path: moviesRef.String(), Limit: -5, Offset: -100, All: false}, 0, "invalid limit / offset settings"},
447-
{&GetParams{Selector: "body", Format: "json", Path: moviesRef.String(), Limit: -5, Offset: -100, All: true}, 0, ""},
448-
{&GetParams{Selector: "body", Format: "json", Path: clRef.String(), Limit: 0, Offset: 0, All: true}, 0, ""},
449-
{&GetParams{Selector: "body", Format: "json", Path: clRef.String(), Limit: 2, Offset: 0, All: false}, 2, ""},
450-
{&GetParams{Selector: "body", Format: "json", Path: sitemapRef.String(), Limit: 3, Offset: 0, All: false}, 3, ""},
418+
{&GetParams{Path: "peer/movies", Selector: "commit"},
419+
componentToString(moviesDs.Commit, "yaml")},
420+
421+
{&GetParams{Path: "peer/movies", Selector: "commit", Format: "json"},
422+
componentToString(moviesDs.Commit, "json")},
423+
424+
{&GetParams{Path: "peer/movies", Selector: "commit.title"}, "initial commit\n"},
425+
426+
{&GetParams{Path: "peer/movies", Selector: "commit.title", Format: "json"}, "\"initial commit\""},
427+
428+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "json"}, "[]"},
429+
430+
{&GetParams{Path: "", Selector: "body", Format: "json"}, "repo: empty dataset reference"},
431+
432+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "csv"}, "title,duration\n"},
433+
434+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "json",
435+
Limit: 5, Offset: 0, All: false}, bodyToString(moviesBody[:5])},
436+
437+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "json",
438+
Limit: -5, Offset: -100, All: false}, "invalid limit / offset settings"},
439+
440+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "json",
441+
Limit: -5, Offset: -100, All: true}, bodyToString(moviesBody)},
442+
443+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "json",
444+
Limit: 0, Offset: 0, All: true}, bodyToString(moviesBody)},
445+
446+
{&GetParams{Path: "peer/movies", Selector: "body", Format: "json",
447+
Limit: 2, Offset: 10, All: false}, bodyToString(moviesBody[10:12])},
451448
}
452449

453450
req := NewDatasetRequests(node, nil)
454451
for i, c := range cases {
455452
got := &GetResult{}
456-
err := req.Get(c.p, got)
457-
458-
if !(err == nil && c.err == "" || err != nil && err.Error() == c.err) {
459-
t.Errorf("case %d error mismatch: expected: %s, got: %s", i, c.err, err)
453+
err := req.Get(c.params, got)
454+
if err != nil {
455+
if err.Error() != c.expect {
456+
t.Errorf("case %d error mismatch: expected: %s, got: %s", i, c.expect, err)
457+
}
460458
continue
461459
}
462460

463-
if got.Bytes == nil && c.resCount == 0 {
464-
continue
461+
result := string(got.Bytes)
462+
if result != c.expect {
463+
t.Errorf("case %d Bytes mismatch: expected:\n\"%s\", got:\n\"%s\"", i, c.expect, result)
465464
}
465+
}
466+
}
466467

467-
switch c.p.Format {
468-
default:
469-
// default should be json format
470-
_, err := json.Marshal(got.Dataset)
471-
if err != nil {
472-
t.Errorf("case %d error parsing response data: %s", i, err.Error())
473-
continue
474-
}
475-
case "csv":
476-
r := csv.NewReader(bytes.NewBuffer(got.Bytes))
477-
_, err := r.ReadAll()
478-
if err != nil {
479-
t.Errorf("case %d error parsing response data: %s", i, err.Error())
480-
continue
481-
}
468+
func setDatasetName(ds *dataset.Dataset, name string) *dataset.Dataset {
469+
parts := strings.Split(name, "/")
470+
ds.Peername = parts[0]
471+
ds.Name = parts[1]
472+
return ds
473+
}
474+
475+
func componentToString(component interface{}, format string) string {
476+
switch format {
477+
case "json":
478+
bytes, err := json.MarshalIndent(component, "", " ")
479+
if err != nil {
480+
return err.Error()
481+
}
482+
return string(bytes)
483+
case "yaml":
484+
bytes, err := yaml.Marshal(component)
485+
if err != nil {
486+
return err.Error()
482487
}
488+
return string(bytes)
489+
default:
490+
return "Unknown format"
491+
}
492+
}
493+
494+
func bodyToString(component interface{}) string {
495+
bytes, err := json.Marshal(component)
496+
if err != nil {
497+
return err.Error()
483498
}
499+
return string(bytes)
484500
}
485501

486502
func TestDatasetRequestsGetP2p(t *testing.T) {

lib/lib.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
var log = golog.Logger("lib")
1414

1515
// VersionNumber is the current version qri
16-
const VersionNumber = "0.7.0"
16+
const VersionNumber = "0.7.1-dev"
1717

1818
// Requests defines a set of library methods
1919
type Requests interface {

0 commit comments

Comments
 (0)