From 9315e6768a8d61ecb0b4942ddc83f905190274ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 2 Feb 2018 21:50:22 +0100 Subject: [PATCH] coreapi: implement Object.Diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Ɓukasz Magiera --- core/coreapi/interface/object.go | 37 +++++++++++++++++++++++++++++ core/coreapi/object.go | 29 +++++++++++++++++++++++ core/coreapi/object_test.go | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/core/coreapi/interface/object.go b/core/coreapi/interface/object.go index ea9aa59480b..0a716dc97e7 100644 --- a/core/coreapi/interface/object.go +++ b/core/coreapi/interface/object.go @@ -31,6 +31,39 @@ type ObjectStat struct { CumulativeSize int } + +const ( + // DiffAdd is a Type of ObjectChange where a link was added to the graph + DiffAdd = iota + + // DiffRemove is a Type of ObjectChange where a link was removed from the graph + DiffRemove + + // DiffMod is a Type of ObjectChange where a link was changed in the graph + DiffMod +) + +// ObjectChange represents a change ia a graph +// TODO: do we want this to be an interface? +type ObjectChange struct { + // Type of the change, either: + // * DiffAdd - Added a link + // * DiffRemove - Removed a link + // * DiffMod - Modified a link + Type int + + // Path to the changed link + Path string + + // Before holds the link path before the change. Note that when a link is + // added, this will be nil. + Before Path + + // After holds the link path after the change. Note that when a link is + // removed, this will be nil. + After Path +} + // ObjectAPI specifies the interface to MerkleDAG and contains useful utilities // for manipulating MerkleDAG data structures. type ObjectAPI interface { @@ -65,4 +98,8 @@ type ObjectAPI interface { // SetData sets the data contained in the node SetData(context.Context, Path, io.Reader) (ResolvedPath, error) + + // Diff returns a set of changes needed to transform the first object into the + // second. + Diff(context.Context, Path, Path) ([]ObjectChange, error) } diff --git a/core/coreapi/object.go b/core/coreapi/object.go index 4da99110f1d..d89f47bdfb7 100644 --- a/core/coreapi/object.go +++ b/core/coreapi/object.go @@ -297,6 +297,35 @@ func (api *ObjectAPI) patchData(ctx context.Context, path coreiface.Path, r io.R return coreiface.IpfsPath(pbnd.Cid()), nil } +func (api *ObjectAPI) Diff(ctx context.Context, before coreiface.Path, after coreiface.Path) ([]coreiface.ObjectChange, error) { + beforeNd, err := api.core().ResolveNode(ctx, before) + if err != nil { + return nil, err + } + + afterNd, err := api.core().ResolveNode(ctx, after) + if err != nil { + return nil, err + } + + changes, err := dagutils.Diff(ctx, api.node.DAG, beforeNd, afterNd) + if err != nil { + return nil, err + } + + out := make([]coreiface.ObjectChange, len(changes)) + for i, change := range changes { + out[i] = coreiface.ObjectChange{ + Type: change.Type, + Path: change.Path, + Before: coreiface.IpfsPath(change.Before), + After: coreiface.IpfsPath(change.After), + } + } + + return out, nil +} + func (api *ObjectAPI) core() coreiface.CoreAPI { return (*CoreAPI)(api) } diff --git a/core/coreapi/object_test.go b/core/coreapi/object_test.go index 3d0a5434931..8df9c04d253 100644 --- a/core/coreapi/object_test.go +++ b/core/coreapi/object_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/ipfs/go-ipfs/core/coreapi/interface" opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) @@ -385,3 +386,42 @@ func TestObjectSetData(t *testing.T) { t.Error("unexpected data") } } + +func TestDiffTest(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bar"}`)) + if err != nil { + t.Fatal(err) + } + + changes, err := api.Object().Diff(ctx, p1, p2) + if err != nil { + t.Fatal(err) + } + + if len(changes) != 1 { + t.Fatal("unexpected changes len") + } + + if changes[0].Type != iface.DiffMod { + t.Fatal("unexpected change type") + } + + if changes[0].Before.String() != p1.String() { + t.Fatal("unexpected before path") + } + + if changes[0].After.String() != p2.String() { + t.Fatal("unexpected before path") + } +}