From c533b87b075cee7fc9819f959c79f3049f9f7939 Mon Sep 17 00:00:00 2001 From: Rohitha Date: Tue, 20 Jun 2023 19:37:18 +0530 Subject: [PATCH 1/3] implement dag stat --- dag.go | 84 +++++++++++++++++++++++++++++++++++++++++++++ options/dag_stat.go | 33 ++++++++++++++++++ shell_test.go | 24 +++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 options/dag_stat.go diff --git a/dag.go b/dag.go index 37c8aa3df..a50590220 100644 --- a/dag.go +++ b/dag.go @@ -6,9 +6,11 @@ import ( "encoding/json" "fmt" "io" + "log" "strings" files "github.com/ipfs/boxo/files" + "github.com/ipfs/go-cid" "github.com/ipfs/go-ipfs-api/options" ) @@ -37,6 +39,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 +193,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 { + log.Fatal(err) + } + out = v + } + + return out, nil +} 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..1a36ee68a 100644 --- a/shell_test.go +++ b/shell_test.go @@ -630,3 +630,27 @@ 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) +} + +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) +} From 3c04e27848c73a8decab5b7716e9fe775004503b Mon Sep 17 00:00:00 2001 From: Rohitha Date: Tue, 20 Jun 2023 20:45:21 +0530 Subject: [PATCH 2/3] import go-cid --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From bfa65ee423f9094008536a414358aef8095fc1e0 Mon Sep 17 00:00:00 2001 From: Rohitha Date: Tue, 20 Jun 2023 22:36:56 +0530 Subject: [PATCH 3/3] fix staticcheck errors --- dag.go | 3 +-- shell_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dag.go b/dag.go index a50590220..f5c7bf010 100644 --- a/dag.go +++ b/dag.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "log" "strings" files "github.com/ipfs/boxo/files" @@ -228,7 +227,7 @@ func (s *Shell) DagStatWithOpts(data string, opts ...options.DagStatOption) (Dag if err := dec.Decode(&v); err == io.EOF { break } else if err != nil { - log.Fatal(err) + return out, err } out = v } diff --git a/shell_test.go b/shell_test.go index 1a36ee68a..be5a6c660 100644 --- a/shell_test.go +++ b/shell_test.go @@ -641,6 +641,9 @@ func TestDagStat(t *testing.T) { 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) { @@ -653,4 +656,7 @@ func TestDagStatWithOpts(t *testing.T) { 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) }