mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-25 20:37:53 +08:00
Merge pull request #4643 from ipfs/feat/coreapi-commands
commands: switch object to CoreAPI
This commit is contained in:
commit
83f22d5d81
@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/repo/config"
|
||||
|
||||
"gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
|
||||
@ -22,6 +24,7 @@ type Context struct {
|
||||
config *config.Config
|
||||
LoadConfig func(path string) (*config.Config, error)
|
||||
|
||||
api coreiface.CoreAPI
|
||||
node *core.IpfsNode
|
||||
ConstructNode func() (*core.IpfsNode, error)
|
||||
}
|
||||
@ -52,6 +55,19 @@ func (c *Context) GetNode() (*core.IpfsNode, error) {
|
||||
return c.node, err
|
||||
}
|
||||
|
||||
// GetApi returns CoreAPI instance backed by ipfs node.
|
||||
// It may construct the node with the provided function
|
||||
func (c *Context) GetApi() (coreiface.CoreAPI, error) {
|
||||
if c.api == nil {
|
||||
n, err := c.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.api = coreapi.NewCoreAPI(n)
|
||||
}
|
||||
return c.api, nil
|
||||
}
|
||||
|
||||
// Context returns the node's context.
|
||||
func (c *Context) Context() context.Context {
|
||||
n, err := c.GetNode()
|
||||
|
||||
@ -5,7 +5,10 @@ import (
|
||||
|
||||
"github.com/ipfs/go-ipfs/commands"
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/repo/config"
|
||||
|
||||
cmds "gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
|
||||
)
|
||||
|
||||
// GetNode extracts the node from the environment.
|
||||
@ -18,8 +21,18 @@ func GetNode(env interface{}) (*core.IpfsNode, error) {
|
||||
return ctx.GetNode()
|
||||
}
|
||||
|
||||
// GetApi extracts CoreAPI instance from the environment.
|
||||
func GetApi(env cmds.Environment) (coreiface.CoreAPI, error) {
|
||||
ctx, ok := env.(*commands.Context)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected env to be of type %T, got %T", ctx, env)
|
||||
}
|
||||
|
||||
return ctx.GetApi()
|
||||
}
|
||||
|
||||
// GetConfig extracts the config from the environment.
|
||||
func GetConfig(env interface{}) (*config.Config, error) {
|
||||
func GetConfig(env cmds.Environment) (*config.Config, error) {
|
||||
ctx, ok := env.(*commands.Context)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected env to be of type %T, got %T", ctx, env)
|
||||
|
||||
@ -6,10 +6,10 @@ import (
|
||||
"io"
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs/commands"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/dagutils"
|
||||
path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path"
|
||||
|
||||
cmdkit "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
|
||||
)
|
||||
|
||||
@ -52,7 +52,7 @@ Example:
|
||||
cmdkit.BoolOption("verbose", "v", "Print extra information."),
|
||||
},
|
||||
Run: func(req cmds.Request, res cmds.Response) {
|
||||
node, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
@ -61,39 +61,37 @@ Example:
|
||||
a := req.Arguments()[0]
|
||||
b := req.Arguments()[1]
|
||||
|
||||
pa, err := path.ParsePath(a)
|
||||
pa, err := coreiface.ParsePath(a)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
pb, err := path.ParsePath(b)
|
||||
pb, err := coreiface.ParsePath(b)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
changes, err := api.Object().Diff(req.Context(), pa, pb)
|
||||
|
||||
obj_a, err := core.Resolve(ctx, node.Namesys, node.Resolver, pa)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
out := make([]*dagutils.Change, len(changes))
|
||||
for i, change := range changes {
|
||||
out[i] = &dagutils.Change{
|
||||
Type: change.Type,
|
||||
Path: change.Path,
|
||||
}
|
||||
|
||||
if change.Before != nil {
|
||||
out[i].Before = change.Before.Cid()
|
||||
}
|
||||
|
||||
if change.After != nil {
|
||||
out[i].After = change.After.Cid()
|
||||
}
|
||||
}
|
||||
|
||||
obj_b, err := core.Resolve(ctx, node.Namesys, node.Resolver, pb)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
changes, err := dagutils.Diff(ctx, node.DAG, obj_a, obj_b)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
res.SetOutput(&Changes{changes})
|
||||
res.SetOutput(&Changes{out})
|
||||
},
|
||||
Type: Changes{},
|
||||
Marshalers: cmds.MarshalerMap{
|
||||
|
||||
@ -2,10 +2,7 @@ package objectcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -15,14 +12,12 @@ import (
|
||||
|
||||
oldcmds "github.com/ipfs/go-ipfs/commands"
|
||||
lgc "github.com/ipfs/go-ipfs/commands/legacy"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag"
|
||||
ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs"
|
||||
path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
cmds "gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
|
||||
dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag"
|
||||
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||
ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
|
||||
cmdkit "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
|
||||
@ -88,31 +83,25 @@ is the raw data of the object.
|
||||
cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
n, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
fpath, err := path.ParsePath(req.Arguments()[0])
|
||||
path, err := coreiface.ParsePath(req.Arguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath)
|
||||
data, err := api.Object().Data(req.Context(), path)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
pbnode, ok := node.(*dag.ProtoNode)
|
||||
if !ok {
|
||||
res.SetError(dag.ErrNotProtobuf, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
res.SetOutput(bytes.NewReader(pbnode.Data()))
|
||||
res.SetOutput(data)
|
||||
},
|
||||
}
|
||||
|
||||
@ -133,7 +122,7 @@ multihash.
|
||||
cmdkit.BoolOption("headers", "v", "Print table headers (Hash, Size, Name)."),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
n, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
@ -145,19 +134,39 @@ multihash.
|
||||
return
|
||||
}
|
||||
|
||||
fpath := path.Path(req.Arguments()[0])
|
||||
node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath)
|
||||
path, err := coreiface.ParsePath(req.Arguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
output, err := getOutput(node)
|
||||
rp, err := api.ResolvePath(req.Context(), path)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
res.SetOutput(output)
|
||||
|
||||
links, err := api.Object().Links(req.Context(), rp)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
outLinks := make([]Link, len(links))
|
||||
for i, link := range links {
|
||||
outLinks[i] = Link{
|
||||
Hash: link.Cid.String(),
|
||||
Name: link.Name,
|
||||
Size: link.Size,
|
||||
}
|
||||
}
|
||||
|
||||
out := Object{
|
||||
Hash: rp.Cid().String(),
|
||||
Links: outLinks,
|
||||
}
|
||||
|
||||
res.SetOutput(out)
|
||||
},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
|
||||
@ -222,13 +231,17 @@ Supported values are:
|
||||
cmdkit.StringOption("data-encoding", "Encoding type of the data field, either \"text\" or \"base64\".").WithDefault("text"),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
n, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
fpath := path.Path(req.Arguments()[0])
|
||||
path, err := coreiface.ParsePath(req.Arguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
datafieldenc, _, err := req.Option("data-encoding").String()
|
||||
if err != nil {
|
||||
@ -236,30 +249,36 @@ Supported values are:
|
||||
return
|
||||
}
|
||||
|
||||
object, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath)
|
||||
nd, err := api.Object().Get(req.Context(), path)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
pbo, ok := object.(*dag.ProtoNode)
|
||||
if !ok {
|
||||
res.SetError(dag.ErrNotProtobuf, cmdkit.ErrNormal)
|
||||
r, err := api.Object().Data(req.Context(), path)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := encodeData(pbo.Data(), datafieldenc)
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
out, err := encodeData(data, datafieldenc)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
node := &Node{
|
||||
Links: make([]Link, len(object.Links())),
|
||||
Data: data,
|
||||
Links: make([]Link, len(nd.Links())),
|
||||
Data: out,
|
||||
}
|
||||
|
||||
for i, link := range object.Links() {
|
||||
for i, link := range nd.Links() {
|
||||
node.Links[i] = Link{
|
||||
Hash: link.Cid.String(),
|
||||
Name: link.Name,
|
||||
@ -316,27 +335,34 @@ var ObjectStatCmd = &oldcmds.Command{
|
||||
cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
n, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
fpath := path.Path(req.Arguments()[0])
|
||||
|
||||
object, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath)
|
||||
path, err := coreiface.ParsePath(req.Arguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
ns, err := object.Stat()
|
||||
ns, err := api.Object().Stat(req.Context(), path)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
res.SetOutput(ns)
|
||||
oldStat := &ipld.NodeStat{
|
||||
Hash: ns.Cid.String(),
|
||||
NumLinks: ns.NumLinks,
|
||||
BlockSize: ns.BlockSize,
|
||||
LinksSize: ns.LinksSize,
|
||||
DataSize: ns.DataSize,
|
||||
CumulativeSize: ns.CumulativeSize,
|
||||
}
|
||||
|
||||
res.SetOutput(oldStat)
|
||||
},
|
||||
Type: ipld.NodeStat{},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
@ -414,7 +440,7 @@ And then run:
|
||||
cmdkit.BoolOption("quiet", "q", "Write minimal output."),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
n, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
@ -444,30 +470,16 @@ And then run:
|
||||
return
|
||||
}
|
||||
|
||||
if dopin {
|
||||
defer n.Blockstore.PinLock().Unlock()
|
||||
}
|
||||
|
||||
objectCid, err := objectPut(req.Context(), n, input, inputenc, datafieldenc)
|
||||
p, err := api.Object().Put(req.Context(), input,
|
||||
options.Object.DataType(datafieldenc),
|
||||
options.Object.InputEnc(inputenc),
|
||||
options.Object.Pin(dopin))
|
||||
if err != nil {
|
||||
errType := cmdkit.ErrNormal
|
||||
if err == ErrUnknownObjectEnc {
|
||||
errType = cmdkit.ErrClient
|
||||
}
|
||||
res.SetError(err, errType)
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
if dopin {
|
||||
n.Pinning.PinWithMode(objectCid, pin.Recursive)
|
||||
err = n.Pinning.Flush()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.SetOutput(&Object{Hash: objectCid.String()})
|
||||
res.SetOutput(&Object{Hash: p.Cid().String()})
|
||||
},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
|
||||
@ -513,29 +525,24 @@ Available templates:
|
||||
cmdkit.StringArg("template", false, false, "Template to use. Optional."),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
n, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
node := new(dag.ProtoNode)
|
||||
template := "empty"
|
||||
if len(req.Arguments()) == 1 {
|
||||
template := req.Arguments()[0]
|
||||
var err error
|
||||
node, err = nodeFromTemplate(template)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
template = req.Arguments()[0]
|
||||
}
|
||||
|
||||
err = n.DAG.Add(req.Context(), node)
|
||||
if err != nil {
|
||||
nd, err := api.Object().New(req.Context(), options.Object.Type(template))
|
||||
if err != nil && err != io.EOF {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
res.SetOutput(&Object{Hash: node.Cid().String()})
|
||||
|
||||
res.SetOutput(&Object{Hash: nd.Cid().String()})
|
||||
},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
|
||||
@ -555,126 +562,6 @@ Available templates:
|
||||
Type: Object{},
|
||||
}
|
||||
|
||||
func nodeFromTemplate(template string) (*dag.ProtoNode, error) {
|
||||
switch template {
|
||||
case "unixfs-dir":
|
||||
return ft.EmptyDirNode(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("template '%s' not found", template)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrEmptyNode is returned when the input to 'ipfs object put' contains no data
|
||||
var ErrEmptyNode = errors.New("no data or links in this node")
|
||||
|
||||
// objectPut takes a format option, serializes bytes from stdin and updates the dag with that data
|
||||
func objectPut(ctx context.Context, n *core.IpfsNode, input io.Reader, encoding string, dataFieldEncoding string) (*cid.Cid, error) {
|
||||
|
||||
data, err := ioutil.ReadAll(io.LimitReader(input, inputLimit+10))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) >= inputLimit {
|
||||
return nil, ErrObjectTooLarge
|
||||
}
|
||||
|
||||
var dagnode *dag.ProtoNode
|
||||
switch getObjectEnc(encoding) {
|
||||
case objectEncodingJSON:
|
||||
node := new(Node)
|
||||
err = json.Unmarshal(data, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check that we have data in the Node to add
|
||||
// otherwise we will add the empty object without raising an error
|
||||
if NodeEmpty(node) {
|
||||
return nil, ErrEmptyNode
|
||||
}
|
||||
|
||||
dagnode, err = deserializeNode(node, dataFieldEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case objectEncodingProtobuf:
|
||||
dagnode, err = dag.DecodeProtobuf(data)
|
||||
|
||||
case objectEncodingXML:
|
||||
node := new(Node)
|
||||
err = xml.Unmarshal(data, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check that we have data in the Node to add
|
||||
// otherwise we will add the empty object without raising an error
|
||||
if NodeEmpty(node) {
|
||||
return nil, ErrEmptyNode
|
||||
}
|
||||
|
||||
dagnode, err = deserializeNode(node, dataFieldEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, ErrUnknownObjectEnc
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = n.DAG.Add(ctx, dagnode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dagnode.Cid(), nil
|
||||
}
|
||||
|
||||
// ErrUnknownObjectEnc is returned if a invalid encoding is supplied
|
||||
var ErrUnknownObjectEnc = errors.New("unknown object encoding")
|
||||
|
||||
type objectEncoding string
|
||||
|
||||
const (
|
||||
objectEncodingJSON objectEncoding = "json"
|
||||
objectEncodingProtobuf = "protobuf"
|
||||
objectEncodingXML = "xml"
|
||||
)
|
||||
|
||||
func getObjectEnc(o interface{}) objectEncoding {
|
||||
v, ok := o.(string)
|
||||
if !ok {
|
||||
// chosen as default because it's human readable
|
||||
return objectEncodingJSON
|
||||
}
|
||||
|
||||
return objectEncoding(v)
|
||||
}
|
||||
|
||||
func getOutput(dagnode ipld.Node) (*Object, error) {
|
||||
c := dagnode.Cid()
|
||||
output := &Object{
|
||||
Hash: c.String(),
|
||||
Links: make([]Link, len(dagnode.Links())),
|
||||
}
|
||||
|
||||
for i, link := range dagnode.Links() {
|
||||
output.Links[i] = Link{
|
||||
Name: link.Name,
|
||||
Hash: link.Cid.String(),
|
||||
Size: link.Size,
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// converts the Node object into a real dag.ProtoNode
|
||||
func deserializeNode(nd *Node, dataFieldEncoding string) (*dag.ProtoNode, error) {
|
||||
dagnode := new(dag.ProtoNode)
|
||||
@ -708,10 +595,6 @@ func deserializeNode(nd *Node, dataFieldEncoding string) (*dag.ProtoNode, error)
|
||||
return dagnode, nil
|
||||
}
|
||||
|
||||
func NodeEmpty(node *Node) bool {
|
||||
return node.Data == "" && len(node.Links) == 0
|
||||
}
|
||||
|
||||
// copy+pasted from ../commands.go
|
||||
func unwrapOutput(i interface{}) (interface{}, error) {
|
||||
var (
|
||||
|
||||
@ -3,25 +3,18 @@ package objectcmd
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
oldcmds "github.com/ipfs/go-ipfs/commands"
|
||||
lgc "github.com/ipfs/go-ipfs/commands/legacy"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
"github.com/ipfs/go-ipfs/dagutils"
|
||||
dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag"
|
||||
ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs"
|
||||
path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
cmds "gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
|
||||
logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
|
||||
cmdkit "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
|
||||
)
|
||||
|
||||
var log = logging.Logger("core/commands/object")
|
||||
|
||||
var ObjectPatchCmd = &cmds.Command{
|
||||
Helptext: cmdkit.HelpText{
|
||||
Tagline: "Create a new merkledag object based on an existing one.",
|
||||
@ -74,51 +67,31 @@ the limit will not be respected by the network.
|
||||
cmdkit.FileArg("data", true, false, "Data to append.").EnableStdin(),
|
||||
},
|
||||
Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) {
|
||||
nd, err := GetNode(env)
|
||||
api, err := GetApi(env)
|
||||
if err != nil {
|
||||
re.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
root, err := path.ParsePath(req.Arguments[0])
|
||||
root, err := coreiface.ParsePath(req.Arguments[0])
|
||||
if err != nil {
|
||||
re.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rootnd, err := core.Resolve(req.Context, nd.Namesys, nd.Resolver, root)
|
||||
data, err := req.Files.NextFile()
|
||||
if err != nil {
|
||||
re.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rtpb, ok := rootnd.(*dag.ProtoNode)
|
||||
if !ok {
|
||||
re.SetError(dag.ErrNotProtobuf, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
fi, err := req.Files.NextFile()
|
||||
p, err := api.Object().AppendData(req.Context, root, data)
|
||||
if err != nil {
|
||||
re.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(fi)
|
||||
if err != nil {
|
||||
re.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rtpb.SetData(append(rtpb.Data(), data...))
|
||||
|
||||
err = nd.DAG.Add(req.Context, rtpb)
|
||||
if err != nil {
|
||||
re.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
cmds.EmitOnce(re, &Object{Hash: rtpb.Cid().String()})
|
||||
cmds.EmitOnce(re, &Object{Hash: p.Cid().String()})
|
||||
},
|
||||
Type: Object{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
@ -145,51 +118,27 @@ Example:
|
||||
cmdkit.FileArg("data", true, false, "The data to set the object to.").EnableStdin(),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
nd, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
|
||||
root, err := coreiface.ParsePath(req.StringArguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rp, err := path.ParsePath(req.StringArguments()[0])
|
||||
data, err := req.Files().NextFile()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, rp)
|
||||
p, err := api.Object().SetData(req.Context(), root, data)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rtpb, ok := root.(*dag.ProtoNode)
|
||||
if !ok {
|
||||
res.SetError(dag.ErrNotProtobuf, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
fi, err := req.Files().NextFile()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(fi)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rtpb.SetData(data)
|
||||
|
||||
err = nd.DAG.Add(req.Context(), rtpb)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
res.SetOutput(&Object{Hash: rtpb.Cid().String()})
|
||||
res.SetOutput(&Object{Hash: p.Cid().String()})
|
||||
},
|
||||
Type: Object{},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
@ -209,49 +158,26 @@ Removes a link by the given name from root.
|
||||
cmdkit.StringArg("link", true, false, "Name of the link to remove."),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
nd, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rootp, err := path.ParsePath(req.Arguments()[0])
|
||||
root, err := coreiface.ParsePath(req.Arguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, rootp)
|
||||
link := req.Arguments()[1]
|
||||
p, err := api.Object().RmLink(req.Context(), root, link)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rtpb, ok := root.(*dag.ProtoNode)
|
||||
if !ok {
|
||||
res.SetError(dag.ErrNotProtobuf, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
path := req.Arguments()[1]
|
||||
|
||||
e := dagutils.NewDagEditor(rtpb, nd.DAG)
|
||||
|
||||
err = e.RmLink(req.Context(), path)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
nnode, err := e.Finalize(req.Context(), nd.DAG)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
nc := nnode.Cid()
|
||||
|
||||
res.SetOutput(&Object{Hash: nc.String()})
|
||||
res.SetOutput(&Object{Hash: p.Cid().String()})
|
||||
},
|
||||
Type: Object{},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
@ -284,32 +210,21 @@ to a file containing 'bar', and returns the hash of the new object.
|
||||
cmdkit.BoolOption("create", "p", "Create intermediary nodes."),
|
||||
},
|
||||
Run: func(req oldcmds.Request, res oldcmds.Response) {
|
||||
nd, err := req.InvocContext().GetNode()
|
||||
api, err := req.InvocContext().GetApi()
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
rootp, err := path.ParsePath(req.Arguments()[0])
|
||||
root, err := coreiface.ParsePath(req.Arguments()[0])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, rootp)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
name := req.Arguments()[1]
|
||||
|
||||
rtpb, ok := root.(*dag.ProtoNode)
|
||||
if !ok {
|
||||
res.SetError(dag.ErrNotProtobuf, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
npath := req.Arguments()[1]
|
||||
childp, err := path.ParsePath(req.Arguments()[2])
|
||||
child, err := coreiface.ParsePath(req.Arguments()[2])
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
@ -321,34 +236,14 @@ to a file containing 'bar', and returns the hash of the new object.
|
||||
return
|
||||
}
|
||||
|
||||
var createfunc func() *dag.ProtoNode
|
||||
if create {
|
||||
createfunc = ft.EmptyDirNode
|
||||
}
|
||||
|
||||
e := dagutils.NewDagEditor(rtpb, nd.DAG)
|
||||
|
||||
childnd, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, childp)
|
||||
p, err := api.Object().AddLink(req.Context(), root, name, child,
|
||||
options.Object.Create(create))
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
err = e.InsertNodeAtPath(req.Context(), npath, childnd, createfunc)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
nnode, err := e.Finalize(req.Context(), nd.DAG)
|
||||
if err != nil {
|
||||
res.SetError(err, cmdkit.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
nc := nnode.Cid()
|
||||
|
||||
res.SetOutput(&Object{Hash: nc.String()})
|
||||
res.SetOutput(&Object{Hash: p.Cid().String()})
|
||||
},
|
||||
Type: Object{},
|
||||
Marshalers: oldcmds.MarshalerMap{
|
||||
@ -356,13 +251,14 @@ to a file containing 'bar', and returns the hash of the new object.
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: fix import loop with core/commands so we don't need that
|
||||
// COPIED FROM ONE LEVEL UP
|
||||
// GetNode extracts the node from the environment.
|
||||
func GetNode(env interface{}) (*core.IpfsNode, error) {
|
||||
// GetApi extracts CoreAPI instance from the environment.
|
||||
func GetApi(env cmds.Environment) (coreiface.CoreAPI, error) {
|
||||
ctx, ok := env.(*oldcmds.Context)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected env to be of type %T, got %T", ctx, env)
|
||||
}
|
||||
|
||||
return ctx.GetNode()
|
||||
return ctx.GetApi()
|
||||
}
|
||||
|
||||
@ -31,6 +31,40 @@ type ObjectStat struct {
|
||||
CumulativeSize int
|
||||
}
|
||||
|
||||
// ChangeType denotes type of change in ObjectChange
|
||||
type ChangeType int
|
||||
|
||||
const (
|
||||
// DiffAdd is set when a link was added to the graph
|
||||
DiffAdd ChangeType = iota
|
||||
|
||||
// DiffRemove is set when a link was removed from the graph
|
||||
DiffRemove
|
||||
|
||||
// DiffMod is set when a link was changed in the graph
|
||||
DiffMod
|
||||
)
|
||||
|
||||
// ObjectChange represents a change ia a graph
|
||||
type ObjectChange struct {
|
||||
// Type of the change, either:
|
||||
// * DiffAdd - Added a link
|
||||
// * DiffRemove - Removed a link
|
||||
// * DiffMod - Modified a link
|
||||
Type ChangeType
|
||||
|
||||
// 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 ResolvedPath
|
||||
|
||||
// After holds the link path after the change. Note that when a link is
|
||||
// removed, this will be nil.
|
||||
After ResolvedPath
|
||||
}
|
||||
|
||||
// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities
|
||||
// for manipulating MerkleDAG data structures.
|
||||
type ObjectAPI interface {
|
||||
@ -65,4 +99,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)
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ type ObjectNewSettings struct {
|
||||
type ObjectPutSettings struct {
|
||||
InputEnc string
|
||||
DataType string
|
||||
Pin bool
|
||||
}
|
||||
|
||||
type ObjectAddLinkSettings struct {
|
||||
@ -35,6 +36,7 @@ func ObjectPutOptions(opts ...ObjectPutOption) (*ObjectPutSettings, error) {
|
||||
options := &ObjectPutSettings{
|
||||
InputEnc: "json",
|
||||
DataType: "text",
|
||||
Pin: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@ -103,6 +105,15 @@ func (objectOpts) DataType(t string) ObjectPutOption {
|
||||
}
|
||||
}
|
||||
|
||||
// Pin is an option for Object.Put which specifies whether to pin the added
|
||||
// objects, default is false
|
||||
func (objectOpts) Pin(pin bool) ObjectPutOption {
|
||||
return func(settings *ObjectPutSettings) error {
|
||||
settings.Pin = pin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create is an option for Object.AddLink which specifies whether create required
|
||||
// directories for the child
|
||||
func (objectOpts) Create(create bool) ObjectAddLinkOption {
|
||||
|
||||
@ -14,9 +14,10 @@ import (
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
"github.com/ipfs/go-ipfs/dagutils"
|
||||
"github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag"
|
||||
ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs"
|
||||
|
||||
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||
ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
|
||||
)
|
||||
@ -116,11 +117,23 @@ func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Obj
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.Pin {
|
||||
defer api.node.Blockstore.PinLock().Unlock()
|
||||
}
|
||||
|
||||
err = api.node.DAG.Add(ctx, dagnode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.Pin {
|
||||
api.node.Pinning.PinWithMode(dagnode.Cid(), pin.Recursive)
|
||||
err = api.node.Pinning.Flush()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return coreiface.IpfsPath(dagnode.Cid()), nil
|
||||
}
|
||||
|
||||
@ -284,6 +297,41 @@ 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,
|
||||
}
|
||||
|
||||
if change.Before != nil {
|
||||
out[i].Before = coreiface.IpfsPath(change.Before)
|
||||
}
|
||||
|
||||
if change.After != nil {
|
||||
out[i].After = coreiface.IpfsPath(change.After)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (api *ObjectAPI) core() coreiface.CoreAPI {
|
||||
return (*CoreAPI)(api)
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,9 +5,10 @@ import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
|
||||
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||
dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag"
|
||||
"gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||
ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
|
||||
)
|
||||
|
||||
@ -21,7 +22,7 @@ const (
|
||||
// Change represents a change to a DAG and contains a reference to the old and
|
||||
// new CIDs.
|
||||
type Change struct {
|
||||
Type int
|
||||
Type coreiface.ChangeType
|
||||
Path string
|
||||
Before *cid.Cid
|
||||
After *cid.Cid
|
||||
|
||||
Loading…
Reference in New Issue
Block a user