From cdefb64eb12a21e676af8c2db69a083ac1fce9f1 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 2 Feb 2022 12:37:54 -0300 Subject: [PATCH] feat(cmds): add dag diff --- core/commands/dag/dag.go | 35 ++++++++++++++++++++++++ core/commands/dag/diff.go | 56 +++++++++++++++++++++++++++++++++++++++ core/commands/dag/get.go | 32 +++++++++++++++------- 3 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 core/commands/dag/diff.go diff --git a/core/commands/dag/dag.go b/core/commands/dag/dag.go index d5d7479f268..b584ad8cf59 100644 --- a/core/commands/dag/dag.go +++ b/core/commands/dag/dag.go @@ -41,6 +41,7 @@ the existing 'ipfs object' command moving forward. "import": DagImportCmd, "export": DagExportCmd, "stat": DagStatCmd, + "diff": DagDiffCmd, }, } @@ -322,3 +323,37 @@ Note: This command skips duplicate blocks in reporting both size and the number }), }, } + +type DagDiff struct { + s string +} + +// DagDiffCmd is a command for comparing DAG nodes. +// FIXME: Right now only supports DAG-PB encoding (for the roadmap of this +// command see https://github.com/ipfs/go-ipfs/issues/4801). +var DagDiffCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Diffs two DAG nodes.", + ShortDescription: ` +'ipfs dag diff' fetches two DAG nodes and returns their differences. +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("node_before", true, false, "First node, \"before\", in the diff."), + cmds.StringArg("node_after", true, false, "First node, \"after\", in the diff."), + }, + Options: []cmds.Option{ // FIXME: Remove if unused. + }, + Run: dagDiff, + Type: DagDiff{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, d *DagDiff) error { + _, err := fmt.Fprintf( + w, + "%s\n", + d.s, + ) + return err + }), + }, +} diff --git a/core/commands/dag/diff.go b/core/commands/dag/diff.go new file mode 100644 index 00000000000..bf6ff6c455f --- /dev/null +++ b/core/commands/dag/diff.go @@ -0,0 +1,56 @@ +package dagcmd + +import ( + "encoding/json" + cmds "github.com/ipfs/go-ipfs-cmds" + "github.com/ipfs/go-ipfs/core/commands/cmdenv" + "io/ioutil" + + "github.com/wI2L/jsondiff" +) + +func dagDiff(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + api, err := cmdenv.GetApi(env, req) + if err != nil { + return err + } + + getArgNodeAsJson := func(argNumber int) ([]byte, error) { + r, err := getNodeWithCodec(req.Context, req.Arguments[argNumber], "dag-json", api) + if err != nil { + return nil, err + } + + jsonOutput, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return jsonOutput, nil + } + + nodeBefore, err := getArgNodeAsJson(0) + if err != nil { + return err + } + nodeAfter, err := getArgNodeAsJson(1) + if err != nil { + return err + } + + patch, err := jsondiff.CompareJSONOpts(nodeBefore, nodeAfter, jsondiff.Invertible()) + if err != nil { + return err + } + + indented, err := json.MarshalIndent(patch, "", " ") + if err != nil { + return err + } + + if err := res.Emit(DagDiff{string(indented)}); err != nil { + return err + } + + return nil +} diff --git a/core/commands/dag/get.go b/core/commands/dag/get.go index 546ba4e5d19..8c550f93229 100644 --- a/core/commands/dag/get.go +++ b/core/commands/dag/get.go @@ -1,7 +1,9 @@ package dagcmd import ( + "context" "fmt" + coreiface "github.com/ipfs/interface-go-ipfs-core" "io" "github.com/ipfs/go-ipfs/core/commands/cmdenv" @@ -23,24 +25,34 @@ func dagGet(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) e } codecStr, _ := req.Options["output-codec"].(string) - var codec mc.Code - if err := codec.Set(codecStr); err != nil { + + r, err := getNodeWithCodec(req.Context, codecStr, req.Arguments[0], api) + if err != nil { return err } - rp, err := api.ResolvePath(req.Context, path.New(req.Arguments[0])) + return res.Emit(r) +} + +func getNodeWithCodec(ctx context.Context, nodePath string, codecName string, api coreiface.CoreAPI) (io.Reader, error) { + var codec mc.Code + if err := codec.Set(codecName); err != nil { + return nil, err + } + + rp, err := api.ResolvePath(ctx, path.New(nodePath)) if err != nil { - return err + return nil, err } - obj, err := api.Dag().Get(req.Context, rp.Cid()) + obj, err := api.Dag().Get(ctx, rp.Cid()) if err != nil { - return err + return nil, err } universal, ok := obj.(ipldlegacy.UniversalNode) if !ok { - return fmt.Errorf("%T is not a valid IPLD node", obj) + return nil, fmt.Errorf("%T is not a valid IPLD node", obj) } finalNode := universal.(ipld.Node) @@ -50,13 +62,13 @@ func dagGet(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) e finalNode, err = traversal.Get(finalNode, remainderPath) if err != nil { - return err + return nil, err } } encoder, err := multicodec.LookupEncoder(uint64(codec)) if err != nil { - return fmt.Errorf("invalid encoding: %s - %s", codec, err) + return nil, fmt.Errorf("invalid encoding: %s - %s", codec, err) } r, w := io.Pipe() @@ -67,5 +79,5 @@ func dagGet(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) e } }() - return res.Emit(r) + return r, nil }