mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-20 02:21:48 +08:00
implement 'editor' abstraction
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
@ -587,13 +587,17 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path := strings.Split(req.Arguments()[2], "/")
|
||||
path := req.Arguments()[2]
|
||||
|
||||
nnode, err := dagutils.RmLink(req.Context(), nd.DAG, root, path)
|
||||
e := dagutils.NewDagEditor(nd.DAG, root)
|
||||
|
||||
err = e.RmLink(req.Context(), path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nnode := e.GetNode()
|
||||
|
||||
return nnode.Key()
|
||||
}
|
||||
|
||||
@ -610,17 +614,27 @@ func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
|
||||
path := req.Arguments()[2]
|
||||
childk := key.B58KeyDecode(req.Arguments()[3])
|
||||
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
create, _, err := req.Option("create").Bool()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nnode, err := dagutils.InsertNodeAtPath(req.Context(), nd.DAG, root, parts, childk, create)
|
||||
var createfunc func() *dag.Node
|
||||
if create {
|
||||
createfunc = func() *dag.Node {
|
||||
return &dag.Node{Data: ft.FolderPBData()}
|
||||
}
|
||||
}
|
||||
|
||||
e := dagutils.NewDagEditor(nd.DAG, root)
|
||||
|
||||
err = e.InsertNodeAtPath(req.Context(), path, childk, createfunc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nnode := e.GetNode()
|
||||
|
||||
return nnode.Key()
|
||||
}
|
||||
|
||||
|
@ -129,13 +129,23 @@ func (n *Node) AddRawLink(name string, l *Link) error {
|
||||
// Remove a link on this node by the given name
|
||||
func (n *Node) RemoveNodeLink(name string) error {
|
||||
n.encoded = nil
|
||||
for i, l := range n.Links {
|
||||
if l.Name == name {
|
||||
n.Links = append(n.Links[:i], n.Links[i+1:]...)
|
||||
return nil
|
||||
good := make([]*Link, 0, len(n.Links))
|
||||
var found bool
|
||||
|
||||
for _, l := range n.Links {
|
||||
if l.Name != name {
|
||||
good = append(good, l)
|
||||
} else {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return ErrNotFound
|
||||
n.Links = good
|
||||
|
||||
if !found {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return a copy of the link with given name
|
||||
|
@ -2,22 +2,44 @@ package dagutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
dag "github.com/ipfs/go-ipfs/merkledag"
|
||||
ft "github.com/ipfs/go-ipfs/unixfs"
|
||||
)
|
||||
|
||||
func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
|
||||
type Editor struct {
|
||||
root *dag.Node
|
||||
ds dag.DAGService
|
||||
}
|
||||
|
||||
func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor {
|
||||
return &Editor{
|
||||
root: root,
|
||||
ds: ds,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Editor) GetNode() *dag.Node {
|
||||
return e.root.Copy()
|
||||
}
|
||||
|
||||
func (e *Editor) AddLink(ctx context.Context, childname string, childk key.Key) error {
|
||||
nd, err := addLink(ctx, e.ds, e.root, childname, childk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.root = nd
|
||||
return nil
|
||||
}
|
||||
|
||||
func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
|
||||
if childname == "" {
|
||||
return nil, errors.New("cannot create link with no name!")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||
defer cancel()
|
||||
childnd, err := ds.Get(ctx, childk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -38,22 +60,32 @@ func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) {
|
||||
func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key.Key, create func() *dag.Node) error {
|
||||
splpath := strings.Split(path, "/")
|
||||
nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.root = nd
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create func() *dag.Node) (*dag.Node, error) {
|
||||
if len(path) == 1 {
|
||||
return AddLink(ctx, ds, root, path[0], toinsert)
|
||||
return addLink(ctx, ds, root, path[0], toinsert)
|
||||
}
|
||||
|
||||
nd, err := root.GetLinkedNode(ctx, ds, path[0])
|
||||
if err != nil {
|
||||
// if 'create' is true, we create directories on the way down as needed
|
||||
if err == dag.ErrNotFound && create {
|
||||
nd = &dag.Node{Data: ft.FolderPBData()}
|
||||
if err == dag.ErrNotFound && create != nil {
|
||||
nd = create()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
|
||||
ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -72,7 +104,17 @@ func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
|
||||
func (e *Editor) RmLink(ctx context.Context, path string) error {
|
||||
splpath := strings.Split(path, "/")
|
||||
nd, err := rmLink(ctx, e.ds, e.root, splpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.root = nd
|
||||
return nil
|
||||
}
|
||||
|
||||
func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
|
||||
if len(path) == 1 {
|
||||
// base case, remove node in question
|
||||
err := root.RemoveNodeLink(path[0])
|
||||
@ -93,7 +135,7 @@ func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nnode, err := RmLink(ctx, ds, nd, path[1:])
|
||||
nnode, err := rmLink(ctx, ds, nd, path[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package dagutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
@ -22,7 +23,7 @@ func TestAddLink(t *testing.T) {
|
||||
}
|
||||
|
||||
nd := new(dag.Node)
|
||||
nnode, err := AddLink(context.Background(), ds, nd, "fish", fk)
|
||||
nnode, err := addLink(context.Background(), ds, nd, "fish", fk)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -42,9 +43,10 @@ func TestAddLink(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) {
|
||||
func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path string, exp key.Key) {
|
||||
parts := strings.Split(path, "/")
|
||||
cur := root
|
||||
for _, e := range path {
|
||||
for _, e := range parts {
|
||||
nxt, err := cur.GetLinkedNode(context.Background(), ds, e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -66,53 +68,57 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []st
|
||||
func TestInsertNode(t *testing.T) {
|
||||
ds := mdtest.Mock(t)
|
||||
root := new(dag.Node)
|
||||
e := NewDagEditor(ds, root)
|
||||
|
||||
childa := &dag.Node{
|
||||
Data: []byte("This is child A"),
|
||||
}
|
||||
ak, err := ds.Add(childa)
|
||||
testInsert(t, e, "a", "anodefortesting", false, "")
|
||||
testInsert(t, e, "a/b", "data", false, "")
|
||||
testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found")
|
||||
testInsert(t, e, "a/b/c/d/e", "foo", true, "")
|
||||
testInsert(t, e, "a/b/c/d/f", "baz", true, "")
|
||||
testInsert(t, e, "a/b/c/d/f", "bar", true, "")
|
||||
|
||||
testInsert(t, e, "", "bar", true, "cannot create link with no name!")
|
||||
testInsert(t, e, "////", "slashes", true, "cannot create link with no name!")
|
||||
|
||||
k, err := e.GetNode().Key()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := []string{"a", "b", "c", "d"}
|
||||
root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" {
|
||||
t.Fatal("output was different than expected")
|
||||
}
|
||||
assertNodeAtPath(t, ds, root_a, path, ak)
|
||||
|
||||
childb := &dag.Node{Data: []byte("this is the second child")}
|
||||
bk, err := ds.Add(childb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// this one should fail, we are specifying a non-existant path
|
||||
// with create == false
|
||||
path2 := []string{"a", "b", "e", "f"}
|
||||
_, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false)
|
||||
if err == nil {
|
||||
t.Fatal("that shouldnt have worked")
|
||||
}
|
||||
if err != dag.ErrNotFound {
|
||||
t.Fatal("expected this to fail with 'not found'")
|
||||
}
|
||||
|
||||
// inserting a path of length one should work with create == false
|
||||
path3 := []string{"x"}
|
||||
root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertNodeAtPath(t, ds, root_b, path3, bk)
|
||||
|
||||
// now try overwriting a path
|
||||
root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertNodeAtPath(t, ds, root_c, path, bk)
|
||||
}
|
||||
|
||||
func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) {
|
||||
child := &dag.Node{Data: []byte(data)}
|
||||
ck, err := e.ds.Add(child)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var c func() *dag.Node
|
||||
if create {
|
||||
c = func() *dag.Node {
|
||||
return &dag.Node{}
|
||||
}
|
||||
}
|
||||
|
||||
err = e.InsertNodeAtPath(context.TODO(), path, ck, c)
|
||||
if experr != "" {
|
||||
var got string
|
||||
if err != nil {
|
||||
got = err.Error()
|
||||
}
|
||||
if got != experr {
|
||||
t.Fatalf("expected '%s' but got '%s'", experr, got)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertNodeAtPath(t, e.ds, e.root, path, ck)
|
||||
}
|
||||
|
Reference in New Issue
Block a user