Skip to content

Commit

Permalink
feat(Reference Selection): support reference selection
Browse files Browse the repository at this point in the history
I think we should steal the concept of "USE" from database land to set the current dataset. From the CLI this'll dramatically cut down on typing, and abate the need for tab completions. We can also plumb this up into the API & frontend as needed.

In coming commits lib methods can leverage the current Reference selection to return values when no reference is specified. I'm setting selections to be a slice of datasets instead of a single dataset b/c & think we should adapt the entire lib interface to support working with multiple datasets at once where possible. for example. "qri info me/dataset_a me/dataset_b" should return info on both datasets, supplying a third would return info on all three datasets. I think we can carry this pattern into a number of places for interesting results, like dataset comparison.

This commit also removes the repo.SetPrivateKey() method. Instead users should set Private key through using repo.SetProfile with a private key specified.
  • Loading branch information
b5 committed Jun 16, 2018
1 parent 633da94 commit 3cae7bd
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 45 deletions.
1 change: 1 addition & 0 deletions lib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ func Receivers(node *p2p.QriNode) []Requests {
NewProfileRequests(r, nil),
NewSearchRequests(r, nil),
NewRenderRequests(r, nil),
NewSelectionRequests(r, nil),
}
}
4 changes: 2 additions & 2 deletions lib/lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func TestReceivers(t *testing.T) {
}

reqs := Receivers(node)
if len(reqs) != 7 {
t.Errorf("unexpected number of receivers returned. expected: %d. got: %d\nhave you added/removed a receiver?", 7, len(reqs))
if len(reqs) != 8 {
t.Errorf("unexpected number of receivers returned. expected: %d. got: %d\nhave you added/removed a receiver?", 8, len(reqs))
return
}
}
Expand Down
55 changes: 55 additions & 0 deletions lib/selection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package lib

import (
"fmt"
"net/rpc"

"github.com/qri-io/qri/repo"
)

// SelectionRequests encapsulates business logic for the qri search
// command
type SelectionRequests struct {
cli *rpc.Client
repo repo.Repo
}

// NewSelectionRequests creates a SelectionRequests pointer from either a repo
// or an rpc.Client
func NewSelectionRequests(r repo.Repo, cli *rpc.Client) *SelectionRequests {
if r != nil && cli != nil {
panic(fmt.Errorf("both repo and client supplied to NewSelectionRequests"))
}
return &SelectionRequests{
cli: cli,
repo: r,
}
}

// CoreRequestsName implements the requests
func (r SelectionRequests) CoreRequestsName() string { return "selection" }

// SetSelectedRefs sets the current set of selected references
func (r *SelectionRequests) SetSelectedRefs(sel *[]repo.DatasetRef, done *bool) error {
if r.cli != nil {
return r.cli.Call("SelectionRequests.SetSelectedRefs", sel, done)
}

if rs, ok := r.repo.(repo.RefSelector); ok {
return rs.SetSelectedRefs(*sel)
}
return fmt.Errorf("selection not supported")
}

// SelectedRefs gets the current set of selected references
func (r *SelectionRequests) SelectedRefs(done *bool, sel *[]repo.DatasetRef) (err error) {
if r.cli != nil {
return r.cli.Call("SelectionRequests.SelectedRefs", done, sel)
}

if rs, ok := r.repo.(repo.RefSelector); ok {
*sel, err = rs.SelectedRefs()
return
}
return fmt.Errorf("selection not supported")
}
36 changes: 36 additions & 0 deletions lib/selection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package lib

import (
"testing"

"github.com/qri-io/qri/repo"
testrepo "github.com/qri-io/qri/repo/test"
)

func TestSelectionRequestsSelectedRefs(t *testing.T) {
mr, err := testrepo.NewTestRepo(nil)
if err != nil {
t.Fatalf("error allocating test repo: %s", err.Error())
}

sr := NewSelectionRequests(mr, nil)
var done bool
a := []repo.DatasetRef{{Peername: "a"}}
if err := sr.SetSelectedRefs(&a, &done); err != nil {
t.Errorf("setting selected refs: %s", err.Error())
}

b := []repo.DatasetRef{}
if err := sr.SelectedRefs(&done, &b); err != nil {
t.Errorf("selected refs: %s", err.Error())
}

if len(a) != len(b) {
t.Errorf("repsonse len mismatch. expected: %d. got: %d", len(a), len(b))
}
for i, ar := range a {
if err := repo.CompareDatasetRef(ar, b[i]); err != nil {
t.Errorf("case selection %d error: %s", i, err)
}
}
}
4 changes: 2 additions & 2 deletions repo/actions/dataset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func init() {
panic(fmt.Errorf("error unmarshaling private key: %s", err.Error()))
return
}
testPeerProfile.PrivKey = privKey
}

func TestDataset(t *testing.T) {
Expand All @@ -53,7 +54,7 @@ func TestDataset(t *testing.T) {
if err != nil {
panic(err)
}
mr.SetPrivateKey(privKey)
// mr.SetPrivateKey(privKey)
return mr
}
DatasetTests(t, rmf)
Expand Down Expand Up @@ -82,7 +83,6 @@ func testCreateDataset(t *testing.T, rmf RepoMakerFunc) {
func createDataset(t *testing.T, rmf RepoMakerFunc) (repo.Repo, repo.DatasetRef) {
r := rmf(t)
r.SetProfile(testPeerProfile)
r.SetPrivateKey(privKey)
act := Dataset{r}

tc, err := dstest.NewTestCaseFromDir(testdataPath("cities"))
Expand Down
3 changes: 3 additions & 0 deletions repo/fs/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const (
FileAnalytics
// FileSearchIndex is the path to a search index
FileSearchIndex
// FileSelectedRefs is the path to the current ref selection
FileSelectedRefs
// FileChangeRequests is a file of change requests
FileChangeRequests
)
Expand All @@ -68,6 +70,7 @@ var paths = map[File]string{
FilePeers: "/peers.json",
FileAnalytics: "/analytics.json",
FileSearchIndex: "/index.bleve",
FileSelectedRefs: "/selected_refs.json",
FileChangeRequests: "/change_requests.json",
}

Expand Down
43 changes: 28 additions & 15 deletions repo/fs/fs.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package fsrepo

import (
"encoding/json"
"fmt"
"github.com/qri-io/registry/regclient"
"os"

golog "github.com/ipfs/go-log"
Expand All @@ -13,6 +13,7 @@ import (
"github.com/qri-io/qri/repo/actions"
"github.com/qri-io/qri/repo/profile"
"github.com/qri-io/qri/repo/search"
"github.com/qri-io/registry/regclient"
)

var log = golog.Logger("fsrepo")
Expand All @@ -25,15 +26,15 @@ func init() {
type Repo struct {
basepath

profile *profile.Profile
pk crypto.PrivKey

store cafs.Filestore
graph map[string]*dsgraph.Node

Refstore
EventLog

profile *profile.Profile

store cafs.Filestore
selectedRefs []repo.DatasetRef
graph map[string]*dsgraph.Node

profiles ProfileStore
index search.Index

Expand All @@ -53,7 +54,6 @@ func NewRepo(store cafs.Filestore, pro *profile.Profile, rc *regclient.Client, b

r := &Repo{
profile: pro,
pk: pro.PrivKey,

store: store,
basepath: bp,
Expand Down Expand Up @@ -110,15 +110,9 @@ func (r *Repo) SetProfile(p *profile.Profile) error {
return r.Profiles().PutProfile(p)
}

// SetPrivateKey sets an internal reference to the private key for this profile
func (r *Repo) SetPrivateKey(pk crypto.PrivKey) error {
r.pk = pk
return nil
}

// PrivateKey returns this repo's private key
func (r *Repo) PrivateKey() crypto.PrivKey {
return r.pk
return r.profile.PrivKey
}

// Search this repo for dataset references
Expand Down Expand Up @@ -152,6 +146,25 @@ func (r *Repo) UpdateSearchIndex(store cafs.Filestore) error {
return search.IndexRepo(r, r.index)
}

// SetSelectedRefs sets the current reference selection
func (r *Repo) SetSelectedRefs(sel []repo.DatasetRef) error {
return r.saveFile(sel, FileSelectedRefs)
}

// SelectedRefs gives the current reference selection
func (r *Repo) SelectedRefs() ([]repo.DatasetRef, error) {
data, err := r.readBytes(FileSelectedRefs)
if err != nil {
return nil, nil
}
res := []repo.DatasetRef{}
if err = json.Unmarshal(data, &res); err != nil {
return nil, nil
}

return res, nil
}

// Profiles returns this repo's Peers implementation
func (r *Repo) Profiles() profile.Store {
return r.profiles
Expand Down
2 changes: 1 addition & 1 deletion repo/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ func makeTestRepo() (Repo, error) {
return nil, err
}

r.SetPrivateKey(privKey)
r.SetProfile(&profile.Profile{
ID: "QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt",
Peername: "peer",
PrivKey: privKey,
})

data1f := cafs.NewMemfileBytes("data1", []byte("dataset_1"))
Expand Down
32 changes: 17 additions & 15 deletions repo/mem_repo.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package repo

import (
"fmt"

"github.com/libp2p/go-libp2p-crypto"
"github.com/qri-io/cafs"
"github.com/qri-io/dataset/dsgraph"
Expand All @@ -12,12 +10,14 @@ import (

// MemRepo is an in-memory implementation of the Repo interface
type MemRepo struct {
pk crypto.PrivKey
store cafs.Filestore
graph map[string]*dsgraph.Node
refCache *MemRefstore
*MemRefstore
*MemEventLog

store cafs.Filestore
graph map[string]*dsgraph.Node
refCache *MemRefstore
selectedRefs []DatasetRef

profile *profile.Profile
profiles profile.Store
registry *regclient.Client
Expand All @@ -41,15 +41,6 @@ func (r *MemRepo) Store() cafs.Filestore {
return r.store
}

// SetPrivateKey sets this repos's internal private key reference
func (r *MemRepo) SetPrivateKey(pk crypto.PrivKey) error {
if r.profile == nil {
return fmt.Errorf("no profile")
}
r.profile.PrivKey = pk
return nil
}

// PrivateKey returns this repo's private key
func (r *MemRepo) PrivateKey() crypto.PrivKey {
if r.profile == nil {
Expand Down Expand Up @@ -79,6 +70,17 @@ func (r *MemRepo) SetProfile(p *profile.Profile) error {
return nil
}

// SetSelectedRefs sets the current reference selection
func (r *MemRepo) SetSelectedRefs(sel []DatasetRef) error {
r.selectedRefs = sel
return nil
}

// SelectedRefs gives the current reference selection
func (r *MemRepo) SelectedRefs() ([]DatasetRef, error) {
return r.selectedRefs, nil
}

// Profiles gives this repo's Peer interface implementation
func (r *MemRepo) Profiles() profile.Store {
return r.profiles
Expand Down
8 changes: 8 additions & 0 deletions repo/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ type Refstore interface {
RefCount() (int, error)
}

// RefSelector is an interface for supporting reference selection
// a reference selection is a slice of references intended for using
// in dataset operations
type RefSelector interface {
SetSelectedRefs([]DatasetRef) error
SelectedRefs() ([]DatasetRef, error)
}

// ProfileRef encapsulates a reference to a peer profile
// It's main job is to connect peernames / profile ID's to profiles
type ProfileRef struct {
Expand Down
6 changes: 1 addition & 5 deletions repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,8 @@ type Repo interface {
// A repository must maintain profile information about the owner of this dataset.
// The value returned by Profile() should represent the peer.
Profile() (*profile.Profile, error)
// It must be possible to alter profile information.
// SetProfile sets this repo's current profile. Profiles must contain a private key
SetProfile(*profile.Profile) error
// SetPrivateKey sets an internal reference to the private key for this profile.
// PrivateKey is used to tie peer actions to this profile. Repo implementations must
// never expose this private key once set.
SetPrivateKey(pk crypto.PrivKey) error
// PrivateKey hands over this repo's private key
// TODO - this is needed to create action structs, any way we can make this
// privately-negotiated or created at init?
Expand Down
26 changes: 26 additions & 0 deletions repo/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func testdataPath(path string) string {
func RunRepoTests(t *testing.T, rmf RepoMakerFunc) {
tests := []repoTestFunc{
testProfile,
testRefSelector,
// testRefstore,
// DatasetActions,
}
Expand All @@ -48,3 +49,28 @@ func testProfile(t *testing.T, rmf RepoMakerFunc) {
}

}

func testRefSelector(t *testing.T, rmf RepoMakerFunc) {
r := rmf(t)
if rs, ok := r.(repo.RefSelector); ok {
sel := []repo.DatasetRef{
{Peername: "foo"},
}

err := rs.SetSelectedRefs(sel)
if err != nil {
t.Errorf("Error setting selection: %s", err)
}

got, err := rs.SelectedRefs()
if len(sel) != len(got) {
t.Errorf("Selected length mismatch. Expected: %d. Got: %d.", len(sel), len(got))
}

for i, a := range sel {
if err := repo.CompareDatasetRef(a, got[i]); err != nil {
t.Errorf("comparing selected reference %d: %s", i, err)
}
}
}
}
1 change: 0 additions & 1 deletion repo/test/test_dataset_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ func testCreateDataset(t *testing.T, rmf RepoMakerFunc) {
func createDataset(t *testing.T, rmf RepoMakerFunc) (repo.Repo, repo.DatasetRef) {
r := rmf(t)
r.SetProfile(testPeerProfile)
r.SetPrivateKey(privKey)
act := actions.Dataset{r}

tc, err := dstest.NewTestCaseFromDir(testdataPath("cities"))
Expand Down
Loading

0 comments on commit 3cae7bd

Please sign in to comment.