diff --git a/api.go b/api.go index 63ea3aa..1768e84 100644 --- a/api.go +++ b/api.go @@ -118,7 +118,7 @@ func (api *HttpApi) Dag() iface.DagAPI { } func (api *HttpApi) Name() iface.NameAPI { - return (*NameAPI)(api) + return nil } func (api *HttpApi) Key() iface.KeyAPI { diff --git a/api_test.go b/api_test.go index b925b5c..e32c456 100644 --- a/api_test.go +++ b/api_test.go @@ -3,7 +3,6 @@ package httpapi import ( "context" "fmt" - "github.com/ipfs/iptb/testbed/interfaces" "io/ioutil" "os" "path" @@ -16,6 +15,7 @@ import ( local "github.com/ipfs/iptb-plugins/local" "github.com/ipfs/iptb/cli" "github.com/ipfs/iptb/testbed" + "github.com/ipfs/iptb/testbed/interfaces" ) type NodeProvider struct{} @@ -39,7 +39,14 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) } c := cli.NewCli() - if err := c.Run([]string{"iptb", "--IPTB_ROOT", dir, "auto", "-type", "localipfs", "-count", strconv.FormatInt(int64(n), 10), "--start"}); err != nil { + + initArgs := []string{"iptb", "--IPTB_ROOT", dir, "auto", "-type", "localipfs", "-count", strconv.FormatInt(int64(n), 10)} + if err := c.Run(initArgs); err != nil { + return nil, err + } + + startArgs := []string{"iptb", "--IPTB_ROOT", dir, "start", "-wait", "--", "--offline=" + strconv.FormatBool(n == 1)} + if err := c.Run(startArgs); err != nil { return nil, err } diff --git a/block.go b/block.go index 8bdb4c5..185aa0a 100644 --- a/block.go +++ b/block.go @@ -1,17 +1,69 @@ package httpapi import ( + "bytes" "context" + "errors" + "fmt" "io" + "github.com/ipfs/go-cid" "github.com/ipfs/go-ipfs/core/coreapi/interface" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + mh "github.com/multiformats/go-multihash" ) type BlockAPI HttpApi -func (api *BlockAPI) Put(ctx context.Context, r io.Reader, opts ...options.BlockPutOption) (iface.BlockStat, error) { - return nil, ErrNotImplemented +type blockStat struct { + Key string + BSize int `json:"Size"` +} + +func (s *blockStat) Size() int { + return s.BSize +} + +func (s *blockStat) valid() (iface.ResolvedPath, error) { + c, err := cid.Parse(s.Key) + if err != nil { + return nil, err + } + + return iface.IpldPath(c), nil +} + +func (s *blockStat) Path() iface.ResolvedPath { + p, _ := s.valid() + return p +} + +func (api *BlockAPI) Put(ctx context.Context, r io.Reader, opts ...caopts.BlockPutOption) (iface.BlockStat, error) { + options, _, err := caopts.BlockPutOptions(opts...) + if err != nil { + return nil, err + } + + mht, ok := mh.Codes[options.MhType] + if !ok { + return nil, fmt.Errorf("unknowm mhType %d", options.MhType) + } + + req := api.core().request("block/put"). + Option("mhtype", mht). + Option("mhlen", options.MhLength). + Option("format", options.Codec). + FileBody(r) + + var out blockStat + if err := req.Exec(ctx, &out); err != nil { + return nil, err + } + if _, err := out.valid(); err != nil { + return nil, err + } + + return &out, nil } func (api *BlockAPI) Get(ctx context.Context, p iface.Path) (io.Reader, error) { @@ -19,20 +71,57 @@ func (api *BlockAPI) Get(ctx context.Context, p iface.Path) (io.Reader, error) { if err != nil { return nil, err } + if resp.Error != nil { + return nil, resp.Error + } - //TODO: is close on the reader enough? - //defer resp.Close() + //TODO: make get return ReadCloser to avoid copying + defer resp.Close() + b := new(bytes.Buffer) + if _, err := io.Copy(b, resp.Output); err != nil { + return nil, err + } - //TODO: make blockApi return ReadCloser - return resp.Output, resp.Error + return b, nil } -func (api *BlockAPI) Rm(ctx context.Context, p iface.Path, opts ...options.BlockRmOption) error { - return ErrNotImplemented +func (api *BlockAPI) Rm(ctx context.Context, p iface.Path, opts ...caopts.BlockRmOption) error { + options, err := caopts.BlockRmOptions(opts...) + if err != nil { + return err + } + + removedBlock := struct { + Hash string `json:",omitempty"` + Error string `json:",omitempty"` + }{} + + req := api.core().request("block/rm"). + Option("force", options.Force). + Arguments(p.String()) + + if err := req.Exec(ctx, &removedBlock); err != nil { + return err + } + + if removedBlock.Error != "" { + return errors.New(removedBlock.Error) + } + + return nil } func (api *BlockAPI) Stat(ctx context.Context, p iface.Path) (iface.BlockStat, error) { - return nil, ErrNotImplemented + var out blockStat + err := api.core().request("block/stat", p.String()).Exec(ctx, &out) + if err != nil { + return nil, err + } + if _, err := out.valid(); err != nil { + return nil, err + } + + return &out, nil } func (api *BlockAPI) core() *HttpApi { diff --git a/requestbuilder.go b/requestbuilder.go index 9ccc8cf..6e5a89e 100644 --- a/requestbuilder.go +++ b/requestbuilder.go @@ -5,8 +5,11 @@ import ( "context" "fmt" "io" + "io/ioutil" "strconv" "strings" + + "github.com/ipfs/go-ipfs-files" ) // RequestBuilder is an IPFS commands request builder. @@ -42,6 +45,15 @@ func (r *RequestBuilder) Body(body io.Reader) *RequestBuilder { return r } +// FileBody sets the request body to the given reader wrapped into multipartreader. +func (r *RequestBuilder) FileBody(body io.Reader) *RequestBuilder { + pr, _ := files.NewReaderPathFile("/dev/stdin", ioutil.NopCloser(body), nil) + d := files.NewMapDirectory(map[string]files.Node{"": pr}) + r.body = files.NewMultiFileReader(d, false) + + return r +} + // Option sets the given option. func (r *RequestBuilder) Option(key string, value interface{}) *RequestBuilder { var s string