From 45de4b32f9c60503ac7c22a97406924939a44b57 Mon Sep 17 00:00:00 2001 From: Rohitha <73472983+PolamarasettyRohitha@users.noreply.github.com> Date: Fri, 23 Jun 2023 17:06:47 +0530 Subject: [PATCH] feat: add dag stat method (#297) Co-authored-by: Rohitha --- dag.go | 83 +++++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- options/dag_stat.go | 33 ++++++++++++++++++ shell_test.go | 30 ++++++++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 options/dag_stat.go diff --git a/dag.go b/dag.go index 37c8aa3df..f5c7bf010 100644 --- a/dag.go +++ b/dag.go @@ -9,6 +9,7 @@ import ( "strings" files "github.com/ipfs/boxo/files" + "github.com/ipfs/go-cid" "github.com/ipfs/go-ipfs-api/options" ) @@ -37,6 +38,46 @@ type DagImportOutput struct { Stats *DagImportStats } +type DagStat struct { + Cid cid.Cid `json:",omitempty"` + Size uint64 `json:",omitempty"` + NumBlocks int64 `json:",omitempty"` +} + +type DagStatOutput struct { + redundantSize uint64 `json:"-"` + UniqueBlocks int `json:",omitempty"` + TotalSize uint64 `json:",omitempty"` + SharedSize uint64 `json:",omitempty"` + Ratio float32 `json:",omitempty"` + DagStatsArray []*DagStat `json:"DagStats,omitempty"` +} + +func (s *DagStat) UnmarshalJSON(data []byte) error { + /* + We can't rely on cid.Cid.UnmarshalJSON since it uses the {"/": "..."} + format. To make the output consistent and follow the Kubo API patterns + we use the Cid.Parse method + */ + + type Alias DagStat + aux := struct { + Cid string `json:"Cid"` + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + Cid, err := cid.Parse(aux.Cid) + if err != nil { + return err + } + s.Cid = Cid + return nil +} + func (s *Shell) DagGet(ref string, out interface{}) error { return s.Request("dag/get", ref).Exec(context.Background(), out) } @@ -151,3 +192,45 @@ func dagToFilesReader(data interface{}) (*files.MultiFileReader, error) { return fileReader, nil } + +// DagStat gets stats for dag with default options +func (s *Shell) DagStat(data string) (DagStatOutput, error) { + return s.DagStatWithOpts(data) +} + +// DagStatWithOpts gets stats for dag +func (s *Shell) DagStatWithOpts(data string, opts ...options.DagStatOption) (DagStatOutput, error) { + var out DagStatOutput + cfg, err := options.DagStatOptions(opts...) + if err != nil { + return out, err + } + + resp, err := s. + Request("dag/stat", data). + Option("progress", cfg.Progress). + Send(context.Background()) + + if err != nil { + return out, err + } + + defer resp.Close() + + if resp.Error != nil { + return out, resp.Error + } + + dec := json.NewDecoder(resp.Output) + for { + var v DagStatOutput + if err := dec.Decode(&v); err == io.EOF { + break + } else if err != nil { + return out, err + } + out = v + } + + return out, nil +} diff --git a/go.mod b/go.mod index b04f9b0da..6041bcf34 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ module github.com/ipfs/go-ipfs-api require ( github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 github.com/ipfs/boxo v0.8.0 + github.com/ipfs/go-cid v0.4.0 github.com/libp2p/go-libp2p v0.26.3 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-multiaddr v0.8.0 @@ -16,7 +17,6 @@ require ( github.com/benbjohnson/clock v1.3.0 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/ipfs/go-cid v0.4.0 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect diff --git a/options/dag_stat.go b/options/dag_stat.go new file mode 100644 index 000000000..ddace6acb --- /dev/null +++ b/options/dag_stat.go @@ -0,0 +1,33 @@ +package options + +// DagStatSettings is a set of Dag stat options. +type DagStatSettings struct { + Progress bool +} + +// DagStatOption is a single Dag option. +type DagStatOption func(opts *DagStatSettings) error + +// DagStatOptions applies the given option to a DagStatSettings instance. +func DagStatOptions(opts ...DagStatOption) (*DagStatSettings, error) { + options := &DagStatSettings{ + Progress: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +// Progress is an option for Dag.Stat which returns progressive data while reading through the DAG +func (dagOpts) Progress(progress bool) DagStatOption { + return func(opts *DagStatSettings) error { + opts.Progress = progress + return nil + } +} diff --git a/shell_test.go b/shell_test.go index 440edcbbc..be5a6c660 100644 --- a/shell_test.go +++ b/shell_test.go @@ -630,3 +630,33 @@ func TestSwarmPeeringAdd(t *testing.T) { _, err := s.SwarmPeeringAdd(context.Background(), addr) is.Nil(err) } + +func TestDagStat(t *testing.T) { + is := is.New(t) + s := NewShell(shellUrl) + + result, err := s.DagStat("QmUwp4xYq4pt1xavfCnpJ2aoVETf83AsvK3W8KvUGtyzFB") + is.Nil(err) + is.Equal(result.TotalSize, 3383728) + + is.Equal(result.DagStatsArray[0].Cid.String(), "QmUwp4xYq4pt1xavfCnpJ2aoVETf83AsvK3W8KvUGtyzFB") + is.Equal(result.UniqueBlocks, 15) + is.Equal(result.redundantSize, 0) + is.Equal(result.SharedSize, 0) + is.Equal(result.Ratio, 1) +} + +func TestDagStatWithOpts(t *testing.T) { + is := is.New(t) + s := NewShell(shellUrl) + + result, err := s.DagStatWithOpts("QmUwp4xYq4pt1xavfCnpJ2aoVETf83AsvK3W8KvUGtyzFB", options.Dag.Progress(true)) + is.Nil(err) + is.Equal(result.TotalSize, 3383728) + + is.Equal(result.DagStatsArray[0].Cid.String(), "QmUwp4xYq4pt1xavfCnpJ2aoVETf83AsvK3W8KvUGtyzFB") + is.Equal(result.UniqueBlocks, 15) + is.Equal(result.redundantSize, 0) + is.Equal(result.SharedSize, 0) + is.Equal(result.Ratio, 1) +}