mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-26 23:53:19 +08:00
Merge pull request #4672 from ipfs/feat/coreapi-paths
Improve paths in CoreApi
This commit is contained in:
@ -20,11 +20,11 @@ import (
|
|||||||
type BlockAPI CoreAPI
|
type BlockAPI CoreAPI
|
||||||
|
|
||||||
type BlockStat struct {
|
type BlockStat struct {
|
||||||
path coreiface.Path
|
path coreiface.ResolvedPath
|
||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.BlockPutOption) (coreiface.Path, error) {
|
func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.BlockPutOption) (coreiface.ResolvedPath, error) {
|
||||||
settings, err := caopts.BlockPutOptions(opts...)
|
settings, err := caopts.BlockPutOptions(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -65,11 +65,16 @@ func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Bloc
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseCid(b.Cid()), nil
|
return coreiface.IpldPath(b.Cid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *BlockAPI) Get(ctx context.Context, p coreiface.Path) (io.Reader, error) {
|
func (api *BlockAPI) Get(ctx context.Context, p coreiface.Path) (io.Reader, error) {
|
||||||
b, err := api.node.Blocks.GetBlock(ctx, p.Cid())
|
rp, err := api.core().ResolvePath(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := api.node.Blocks.GetBlock(ctx, rp.Cid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -78,11 +83,16 @@ func (api *BlockAPI) Get(ctx context.Context, p coreiface.Path) (io.Reader, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *BlockAPI) Rm(ctx context.Context, p coreiface.Path, opts ...caopts.BlockRmOption) error {
|
func (api *BlockAPI) Rm(ctx context.Context, p coreiface.Path, opts ...caopts.BlockRmOption) error {
|
||||||
|
rp, err := api.core().ResolvePath(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
settings, err := caopts.BlockRmOptions(opts...)
|
settings, err := caopts.BlockRmOptions(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cids := []*cid.Cid{p.Cid()}
|
cids := []*cid.Cid{rp.Cid()}
|
||||||
o := util.RmBlocksOpts{Force: settings.Force}
|
o := util.RmBlocksOpts{Force: settings.Force}
|
||||||
|
|
||||||
out, err := util.RmBlocks(api.node.Blockstore, api.node.Pinning, cids, o)
|
out, err := util.RmBlocks(api.node.Blockstore, api.node.Pinning, cids, o)
|
||||||
@ -111,13 +121,18 @@ func (api *BlockAPI) Rm(ctx context.Context, p coreiface.Path, opts ...caopts.Bl
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *BlockAPI) Stat(ctx context.Context, p coreiface.Path) (coreiface.BlockStat, error) {
|
func (api *BlockAPI) Stat(ctx context.Context, p coreiface.Path) (coreiface.BlockStat, error) {
|
||||||
b, err := api.node.Blocks.GetBlock(ctx, p.Cid())
|
rp, err := api.core().ResolvePath(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := api.node.Blocks.GetBlock(ctx, rp.Cid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BlockStat{
|
return &BlockStat{
|
||||||
path: ParseCid(b.Cid()),
|
path: coreiface.IpldPath(b.Cid()),
|
||||||
size: len(b.RawData()),
|
size: len(b.RawData()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -126,6 +141,10 @@ func (bs *BlockStat) Size() int {
|
|||||||
return bs.size
|
return bs.size
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *BlockStat) Path() coreiface.Path {
|
func (bs *BlockStat) Path() coreiface.ResolvedPath {
|
||||||
return bs.path
|
return bs.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *BlockAPI) core() coreiface.CoreAPI {
|
||||||
|
return (*CoreAPI)(api)
|
||||||
|
}
|
||||||
|
@ -14,17 +14,8 @@ Interfaces here aren't yet completely stable.
|
|||||||
package coreapi
|
package coreapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
core "github.com/ipfs/go-ipfs/core"
|
core "github.com/ipfs/go-ipfs/core"
|
||||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||||
namesys "github.com/ipfs/go-ipfs/namesys"
|
|
||||||
ipfspath "github.com/ipfs/go-ipfs/path"
|
|
||||||
resolver "github.com/ipfs/go-ipfs/path/resolver"
|
|
||||||
uio "github.com/ipfs/go-ipfs/unixfs/io"
|
|
||||||
|
|
||||||
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
|
||||||
ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CoreAPI struct {
|
type CoreAPI struct {
|
||||||
@ -71,86 +62,3 @@ func (api *CoreAPI) Object() coreiface.ObjectAPI {
|
|||||||
func (api *CoreAPI) Pin() coreiface.PinAPI {
|
func (api *CoreAPI) Pin() coreiface.PinAPI {
|
||||||
return (*PinAPI)(api)
|
return (*PinAPI)(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the
|
|
||||||
// resolved Node.
|
|
||||||
func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (ipld.Node, error) {
|
|
||||||
return resolveNode(ctx, api.node.DAG, api.node.Namesys, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveNode(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (ipld.Node, error) {
|
|
||||||
p, err := resolvePath(ctx, ng, nsys, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
node, err := ng.Get(ctx, p.Cid())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolvePath resolves the path `p` using Unixfs resolver, returns the
|
|
||||||
// resolved path.
|
|
||||||
// TODO: store all of ipfspath.Resolver.ResolvePathComponents() in Path
|
|
||||||
func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.Path, error) {
|
|
||||||
return resolvePath(ctx, api.node.DAG, api.node.Namesys, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (coreiface.Path, error) {
|
|
||||||
if p.Resolved() {
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &resolver.Resolver{
|
|
||||||
DAG: ng,
|
|
||||||
ResolveOnce: uio.ResolveUnixfsOnce,
|
|
||||||
}
|
|
||||||
|
|
||||||
p2 := ipfspath.FromString(p.String())
|
|
||||||
node, err := core.Resolve(ctx, nsys, r, p2)
|
|
||||||
if err == core.ErrNoNamesys {
|
|
||||||
return nil, coreiface.ErrOffline
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var root *cid.Cid
|
|
||||||
if p2.IsJustAKey() {
|
|
||||||
root = node.Cid()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResolvedPath(p.String(), node.Cid(), root), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements coreiface.Path
|
|
||||||
type path struct {
|
|
||||||
path ipfspath.Path
|
|
||||||
cid *cid.Cid
|
|
||||||
root *cid.Cid
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsePath parses path `p` using ipfspath parser, returns the parsed path.
|
|
||||||
func ParsePath(p string) (coreiface.Path, error) {
|
|
||||||
pp, err := ipfspath.ParsePath(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &path{path: pp}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseCid parses the path from `c`, returns the parsed path.
|
|
||||||
func ParseCid(c *cid.Cid) coreiface.Path {
|
|
||||||
return &path{path: ipfspath.FromCid(c), cid: c, root: c}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolvePath parses path from string `p`, returns parsed path.
|
|
||||||
func ResolvedPath(p string, c *cid.Cid, r *cid.Cid) coreiface.Path {
|
|
||||||
return &path{path: ipfspath.FromString(p), cid: c, root: r}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *path) String() string { return p.path.String() }
|
|
||||||
func (p *path) Cid() *cid.Cid { return p.cid }
|
|
||||||
func (p *path) Root() *cid.Cid { return p.root }
|
|
||||||
func (p *path) Resolved() bool { return p.cid != nil }
|
|
||||||
|
@ -20,7 +20,7 @@ type DagAPI CoreAPI
|
|||||||
// Put inserts data using specified format and input encoding. Unless used with
|
// Put inserts data using specified format and input encoding. Unless used with
|
||||||
// `WithCodes` or `WithHash`, the defaults "dag-cbor" and "sha256" are used.
|
// `WithCodes` or `WithHash`, the defaults "dag-cbor" and "sha256" are used.
|
||||||
// Returns the path of the inserted data.
|
// Returns the path of the inserted data.
|
||||||
func (api *DagAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.DagPutOption) (coreiface.Path, error) {
|
func (api *DagAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.DagPutOption) (coreiface.ResolvedPath, error) {
|
||||||
settings, err := caopts.DagPutOptions(opts...)
|
settings, err := caopts.DagPutOptions(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -44,7 +44,7 @@ func (api *DagAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.DagPut
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseCid(nds[0].Cid()), nil
|
return coreiface.IpldPath(nds[0].Cid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get resolves `path` using Unixfs resolver, returns the resolved Node.
|
// Get resolves `path` using Unixfs resolver, returns the resolved Node.
|
||||||
@ -66,7 +66,7 @@ func (api *DagAPI) Tree(ctx context.Context, p coreiface.Path, opts ...caopts.Da
|
|||||||
paths := n.Tree("", settings.Depth)
|
paths := n.Tree("", settings.Depth)
|
||||||
out := make([]coreiface.Path, len(paths))
|
out := make([]coreiface.Path, len(paths))
|
||||||
for n, p2 := range paths {
|
for n, p2 := range paths {
|
||||||
out[n], err = ParsePath(gopath.Join(p.String(), p2))
|
out[n], err = coreiface.ParsePath(gopath.Join(p.String(), p2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||||
|
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||||
|
|
||||||
mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
|
mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
|
||||||
|
|
||||||
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -74,7 +73,7 @@ func TestPath(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := coreapi.ParsePath(path.Join(res.Cid().String(), "lnk"))
|
p, err := coreiface.ParsePath(path.Join(res.Cid().String(), "lnk"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,13 @@ type BlockStat interface {
|
|||||||
Size() int
|
Size() int
|
||||||
|
|
||||||
// Path returns path to the block
|
// Path returns path to the block
|
||||||
Path() Path
|
Path() ResolvedPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockAPI specifies the interface to the block layer
|
// BlockAPI specifies the interface to the block layer
|
||||||
type BlockAPI interface {
|
type BlockAPI interface {
|
||||||
// Put imports raw block data, hashing it using specified settings.
|
// Put imports raw block data, hashing it using specified settings.
|
||||||
Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error)
|
Put(context.Context, io.Reader, ...options.BlockPutOption) (ResolvedPath, error)
|
||||||
|
|
||||||
// Get attempts to resolve the path and return a reader for data in the block
|
// Get attempts to resolve the path and return a reader for data in the block
|
||||||
Get(context.Context, Path) (io.Reader, error)
|
Get(context.Context, Path) (io.Reader, error)
|
||||||
|
@ -32,7 +32,7 @@ type CoreAPI interface {
|
|||||||
Object() ObjectAPI
|
Object() ObjectAPI
|
||||||
|
|
||||||
// ResolvePath resolves the path using Unixfs resolver
|
// ResolvePath resolves the path using Unixfs resolver
|
||||||
ResolvePath(context.Context, Path) (Path, error)
|
ResolvePath(context.Context, Path) (ResolvedPath, error)
|
||||||
|
|
||||||
// ResolveNode resolves the path (if not resolved already) using Unixfs
|
// ResolveNode resolves the path (if not resolved already) using Unixfs
|
||||||
// resolver, gets and returns the resolved Node
|
// resolver, gets and returns the resolved Node
|
||||||
|
@ -14,7 +14,7 @@ type DagAPI interface {
|
|||||||
// Put inserts data using specified format and input encoding.
|
// Put inserts data using specified format and input encoding.
|
||||||
// Unless used with WithCodec or WithHash, the defaults "dag-cbor" and
|
// Unless used with WithCodec or WithHash, the defaults "dag-cbor" and
|
||||||
// "sha256" are used.
|
// "sha256" are used.
|
||||||
Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (Path, error)
|
Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (ResolvedPath, error)
|
||||||
|
|
||||||
// Get attempts to resolve and get the node specified by the path
|
// Get attempts to resolve and get the node specified by the path
|
||||||
Get(ctx context.Context, path Path) (ipld.Node, error)
|
Get(ctx context.Context, path Path) (ipld.Node, error)
|
||||||
|
@ -38,7 +38,7 @@ type ObjectAPI interface {
|
|||||||
New(context.Context, ...options.ObjectNewOption) (ipld.Node, error)
|
New(context.Context, ...options.ObjectNewOption) (ipld.Node, error)
|
||||||
|
|
||||||
// Put imports the data into merkledag
|
// Put imports the data into merkledag
|
||||||
Put(context.Context, io.Reader, ...options.ObjectPutOption) (Path, error)
|
Put(context.Context, io.Reader, ...options.ObjectPutOption) (ResolvedPath, error)
|
||||||
|
|
||||||
// Get returns the node for the path
|
// Get returns the node for the path
|
||||||
Get(context.Context, Path) (ipld.Node, error)
|
Get(context.Context, Path) (ipld.Node, error)
|
||||||
@ -55,14 +55,14 @@ type ObjectAPI interface {
|
|||||||
// AddLink adds a link under the specified path. child path can point to a
|
// AddLink adds a link under the specified path. child path can point to a
|
||||||
// subdirectory within the patent which must be present (can be overridden
|
// subdirectory within the patent which must be present (can be overridden
|
||||||
// with WithCreate option).
|
// with WithCreate option).
|
||||||
AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Path, error)
|
AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (ResolvedPath, error)
|
||||||
|
|
||||||
// RmLink removes a link from the node
|
// RmLink removes a link from the node
|
||||||
RmLink(ctx context.Context, base Path, link string) (Path, error)
|
RmLink(ctx context.Context, base Path, link string) (ResolvedPath, error)
|
||||||
|
|
||||||
// AppendData appends data to the node
|
// AppendData appends data to the node
|
||||||
AppendData(context.Context, Path, io.Reader) (Path, error)
|
AppendData(context.Context, Path, io.Reader) (ResolvedPath, error)
|
||||||
|
|
||||||
// SetData sets the data contained in the node
|
// SetData sets the data contained in the node
|
||||||
SetData(context.Context, Path, io.Reader) (Path, error)
|
SetData(context.Context, Path, io.Reader) (ResolvedPath, error)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,176 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ipfspath "github.com/ipfs/go-ipfs/path"
|
||||||
|
|
||||||
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//TODO: merge with ipfspath so we don't depend on it
|
||||||
|
|
||||||
// Path is a generic wrapper for paths used in the API. A path can be resolved
|
// Path is a generic wrapper for paths used in the API. A path can be resolved
|
||||||
// to a CID using one of Resolve functions in the API.
|
// to a CID using one of Resolve functions in the API.
|
||||||
|
//
|
||||||
|
// Paths must be prefixed with a valid prefix:
|
||||||
|
//
|
||||||
|
// * /ipfs - Immutable unixfs path (files)
|
||||||
|
// * /ipld - Immutable ipld path (data)
|
||||||
|
// * /ipns - Mutable names. Usually resolves to one of the immutable paths
|
||||||
|
//TODO: /local (MFS)
|
||||||
type Path interface {
|
type Path interface {
|
||||||
// String returns the path as a string.
|
// String returns the path as a string.
|
||||||
String() string
|
String() string
|
||||||
// Cid returns cid referred to by path
|
|
||||||
Cid() *cid.Cid
|
// Namespace returns the first component of the path.
|
||||||
// Root returns cid of root path
|
//
|
||||||
Root() *cid.Cid
|
// For example path "/ipfs/QmHash", calling Namespace() will return "ipfs"
|
||||||
// Resolved returns whether path has been fully resolved
|
Namespace() string
|
||||||
Resolved() bool
|
|
||||||
|
// Mutable returns false if the data pointed to by this path in guaranteed
|
||||||
|
// to not change.
|
||||||
|
//
|
||||||
|
// Note that resolved mutable path can be immutable.
|
||||||
|
Mutable() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvedPath is a path which was resolved to the last resolvable node
|
||||||
|
type ResolvedPath interface {
|
||||||
|
// Cid returns the CID of the node referenced by the path. Remainder of the
|
||||||
|
// path is guaranteed to be within the node.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// If you have 3 linked objects: QmRoot -> A -> B:
|
||||||
|
//
|
||||||
|
// cidB := {"foo": {"bar": 42 }}
|
||||||
|
// cidA := {"B": {"/": cidB }}
|
||||||
|
// cidRoot := {"A": {"/": cidA }}
|
||||||
|
//
|
||||||
|
// And resolve paths:
|
||||||
|
// * "/ipfs/${cidRoot}"
|
||||||
|
// * Calling Cid() will return `cidRoot`
|
||||||
|
// * Calling Root() will return `cidRoot`
|
||||||
|
// * Calling Remainder() will return ``
|
||||||
|
//
|
||||||
|
// * "/ipfs/${cidRoot}/A"
|
||||||
|
// * Calling Cid() will return `cidA`
|
||||||
|
// * Calling Root() will return `cidRoot`
|
||||||
|
// * Calling Remainder() will return ``
|
||||||
|
//
|
||||||
|
// * "/ipfs/${cidRoot}/A/B/foo"
|
||||||
|
// * Calling Cid() will return `cidB`
|
||||||
|
// * Calling Root() will return `cidRoot`
|
||||||
|
// * Calling Remainder() will return `foo`
|
||||||
|
//
|
||||||
|
// * "/ipfs/${cidRoot}/A/B/foo/bar"
|
||||||
|
// * Calling Cid() will return `cidB`
|
||||||
|
// * Calling Root() will return `cidRoot`
|
||||||
|
// * Calling Remainder() will return `foo/bar`
|
||||||
|
Cid() *cid.Cid
|
||||||
|
|
||||||
|
// Root returns the CID of the root object of the path
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// If you have 3 linked objects: QmRoot -> A -> B, and resolve path
|
||||||
|
// "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot
|
||||||
|
//
|
||||||
|
// For more examples see the documentation of Cid() method
|
||||||
|
Root() *cid.Cid
|
||||||
|
|
||||||
|
// Remainder returns unresolved part of the path
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// If you have 2 linked objects: QmRoot -> A, where A is a CBOR node
|
||||||
|
// containing the following data:
|
||||||
|
//
|
||||||
|
// {"foo": {"bar": 42 }}
|
||||||
|
//
|
||||||
|
// When resolving "/ipld/QmRoot/A/foo/bar", Remainder will return "foo/bar"
|
||||||
|
//
|
||||||
|
// For more examples see the documentation of Cid() method
|
||||||
|
Remainder() string
|
||||||
|
|
||||||
|
Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// path implements coreiface.Path
|
||||||
|
type path struct {
|
||||||
|
path ipfspath.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolvedPath implements coreiface.resolvedPath
|
||||||
|
type resolvedPath struct {
|
||||||
|
path
|
||||||
|
cid *cid.Cid
|
||||||
|
root *cid.Cid
|
||||||
|
remainder string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IpfsPath creates new /ipfs path from the provided CID
|
||||||
|
func IpfsPath(c *cid.Cid) ResolvedPath {
|
||||||
|
return &resolvedPath{
|
||||||
|
path: path{ipfspath.Path("/ipfs/" + c.String())},
|
||||||
|
cid: c,
|
||||||
|
root: c,
|
||||||
|
remainder: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IpldPath creates new /ipld path from the provided CID
|
||||||
|
func IpldPath(c *cid.Cid) ResolvedPath {
|
||||||
|
return &resolvedPath{
|
||||||
|
path: path{ipfspath.Path("/ipld/" + c.String())},
|
||||||
|
cid: c,
|
||||||
|
root: c,
|
||||||
|
remainder: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePath parses string path to a Path
|
||||||
|
func ParsePath(p string) (Path, error) {
|
||||||
|
pp, err := ipfspath.ParsePath(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &path{path: pp}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResolvedPath creates new ResolvedPath. This function performs no checks
|
||||||
|
// and is intended to be used by resolver implementations. Incorrect inputs may
|
||||||
|
// cause panics. Handle with care.
|
||||||
|
func NewResolvedPath(ipath ipfspath.Path, c *cid.Cid, root *cid.Cid, remainder string) ResolvedPath {
|
||||||
|
return &resolvedPath{
|
||||||
|
path: path{ipath},
|
||||||
|
cid: c,
|
||||||
|
root: root,
|
||||||
|
remainder: remainder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *path) String() string {
|
||||||
|
return p.path.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *path) Namespace() string {
|
||||||
|
if len(p.path.Segments()) < 1 {
|
||||||
|
panic("path without namespace") //this shouldn't happen under any scenario
|
||||||
|
}
|
||||||
|
return p.path.Segments()[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *path) Mutable() bool {
|
||||||
|
//TODO: MFS: check for /local
|
||||||
|
return p.Namespace() == "ipns"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *resolvedPath) Cid() *cid.Cid {
|
||||||
|
return p.cid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *resolvedPath) Root() *cid.Cid {
|
||||||
|
return p.root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *resolvedPath) Remainder() string {
|
||||||
|
return p.remainder
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
// Pin holds information about pinned resource
|
// Pin holds information about pinned resource
|
||||||
type Pin interface {
|
type Pin interface {
|
||||||
// Path to the pinned object
|
// Path to the pinned object
|
||||||
Path() Path
|
Path() ResolvedPath
|
||||||
|
|
||||||
// Type of the pin
|
// Type of the pin
|
||||||
Type() string
|
Type() string
|
||||||
@ -27,7 +27,7 @@ type PinStatus interface {
|
|||||||
// BadPinNode is a node that has been marked as bad by Pin.Verify
|
// BadPinNode is a node that has been marked as bad by Pin.Verify
|
||||||
type BadPinNode interface {
|
type BadPinNode interface {
|
||||||
// Path is the path of the node
|
// Path is the path of the node
|
||||||
Path() Path
|
Path() ResolvedPath
|
||||||
|
|
||||||
// Err is the reason why the node has been marked as bad
|
// Err is the reason why the node has been marked as bad
|
||||||
Err() error
|
Err() error
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
// UnixfsAPI is the basic interface to immutable files in IPFS
|
// UnixfsAPI is the basic interface to immutable files in IPFS
|
||||||
type UnixfsAPI interface {
|
type UnixfsAPI interface {
|
||||||
// Add imports the data from the reader into merkledag file
|
// Add imports the data from the reader into merkledag file
|
||||||
Add(context.Context, io.Reader) (Path, error)
|
Add(context.Context, io.Reader) (ResolvedPath, error)
|
||||||
|
|
||||||
// Cat returns a reader for the file
|
// Cat returns a reader for the file
|
||||||
Cat(context.Context, Path) (Reader, error)
|
Cat(context.Context, Path) (Reader, error)
|
||||||
|
@ -28,7 +28,12 @@ func (k *key) Name() string {
|
|||||||
|
|
||||||
// Path returns the path of the key.
|
// Path returns the path of the key.
|
||||||
func (k *key) Path() coreiface.Path {
|
func (k *key) Path() coreiface.Path {
|
||||||
return &path{path: ipfspath.FromString(ipfspath.Join([]string{"/ipns", k.peerId}))}
|
path, err := coreiface.ParsePath(ipfspath.Join([]string{"/ipns", k.peerId}))
|
||||||
|
if err != nil {
|
||||||
|
panic("error parsing path: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate generates new key, stores it in the keystore under the specified
|
// Generate generates new key, stores it in the keystore under the specified
|
||||||
|
@ -129,7 +129,7 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &path{path: output}, nil
|
return coreiface.ParsePath(output.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func keylookup(n *core.IpfsNode, k string) (crypto.PrivKey, error) {
|
func keylookup(n *core.IpfsNode, k string) (crypto.PrivKey, error) {
|
||||||
|
@ -56,7 +56,7 @@ func (api *ObjectAPI) New(ctx context.Context, opts ...caopts.ObjectNewOption) (
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.ObjectPutOption) (coreiface.Path, error) {
|
func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.ObjectPutOption) (coreiface.ResolvedPath, error) {
|
||||||
options, err := caopts.ObjectPutOptions(opts...)
|
options, err := caopts.ObjectPutOptions(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -121,7 +121,7 @@ func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Obj
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseCid(dagnode.Cid()), nil
|
return coreiface.IpfsPath(dagnode.Cid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) Get(ctx context.Context, path coreiface.Path) (ipld.Node, error) {
|
func (api *ObjectAPI) Get(ctx context.Context, path coreiface.Path) (ipld.Node, error) {
|
||||||
@ -180,7 +180,7 @@ func (api *ObjectAPI) Stat(ctx context.Context, path coreiface.Path) (*coreiface
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) AddLink(ctx context.Context, base coreiface.Path, name string, child coreiface.Path, opts ...caopts.ObjectAddLinkOption) (coreiface.Path, error) {
|
func (api *ObjectAPI) AddLink(ctx context.Context, base coreiface.Path, name string, child coreiface.Path, opts ...caopts.ObjectAddLinkOption) (coreiface.ResolvedPath, error) {
|
||||||
options, err := caopts.ObjectAddLinkOptions(opts...)
|
options, err := caopts.ObjectAddLinkOptions(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -218,10 +218,10 @@ func (api *ObjectAPI) AddLink(ctx context.Context, base coreiface.Path, name str
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseCid(nnode.Cid()), nil
|
return coreiface.IpfsPath(nnode.Cid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) RmLink(ctx context.Context, base coreiface.Path, link string) (coreiface.Path, error) {
|
func (api *ObjectAPI) RmLink(ctx context.Context, base coreiface.Path, link string) (coreiface.ResolvedPath, error) {
|
||||||
baseNd, err := api.core().ResolveNode(ctx, base)
|
baseNd, err := api.core().ResolveNode(ctx, base)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -244,18 +244,18 @@ func (api *ObjectAPI) RmLink(ctx context.Context, base coreiface.Path, link stri
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseCid(nnode.Cid()), nil
|
return coreiface.IpfsPath(nnode.Cid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) AppendData(ctx context.Context, path coreiface.Path, r io.Reader) (coreiface.Path, error) {
|
func (api *ObjectAPI) AppendData(ctx context.Context, path coreiface.Path, r io.Reader) (coreiface.ResolvedPath, error) {
|
||||||
return api.patchData(ctx, path, r, true)
|
return api.patchData(ctx, path, r, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) SetData(ctx context.Context, path coreiface.Path, r io.Reader) (coreiface.Path, error) {
|
func (api *ObjectAPI) SetData(ctx context.Context, path coreiface.Path, r io.Reader) (coreiface.ResolvedPath, error) {
|
||||||
return api.patchData(ctx, path, r, false)
|
return api.patchData(ctx, path, r, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) patchData(ctx context.Context, path coreiface.Path, r io.Reader, appendData bool) (coreiface.Path, error) {
|
func (api *ObjectAPI) patchData(ctx context.Context, path coreiface.Path, r io.Reader, appendData bool) (coreiface.ResolvedPath, error) {
|
||||||
nd, err := api.core().ResolveNode(ctx, path)
|
nd, err := api.core().ResolveNode(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -281,7 +281,7 @@ func (api *ObjectAPI) patchData(ctx context.Context, path coreiface.Path, r io.R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseCid(pbnd.Cid()), nil
|
return coreiface.IpfsPath(pbnd.Cid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ObjectAPI) core() coreiface.CoreAPI {
|
func (api *ObjectAPI) core() coreiface.CoreAPI {
|
||||||
|
84
core/coreapi/path.go
Normal file
84
core/coreapi/path.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package coreapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
fmt "fmt"
|
||||||
|
gopath "path"
|
||||||
|
|
||||||
|
core "github.com/ipfs/go-ipfs/core"
|
||||||
|
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||||
|
namesys "github.com/ipfs/go-ipfs/namesys"
|
||||||
|
ipfspath "github.com/ipfs/go-ipfs/path"
|
||||||
|
resolver "github.com/ipfs/go-ipfs/path/resolver"
|
||||||
|
uio "github.com/ipfs/go-ipfs/unixfs/io"
|
||||||
|
|
||||||
|
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||||
|
ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the
|
||||||
|
// resolved Node.
|
||||||
|
func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (ipld.Node, error) {
|
||||||
|
return resolveNode(ctx, api.node.DAG, api.node.Namesys, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvePath resolves the path `p` using Unixfs resolver, returns the
|
||||||
|
// resolved path.
|
||||||
|
func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.ResolvedPath, error) {
|
||||||
|
return resolvePath(ctx, api.node.DAG, api.node.Namesys, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveNode(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (ipld.Node, error) {
|
||||||
|
rp, err := resolvePath(ctx, ng, nsys, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := ng.Get(ctx, rp.Cid())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (coreiface.ResolvedPath, error) {
|
||||||
|
if _, ok := p.(coreiface.ResolvedPath); ok {
|
||||||
|
return p.(coreiface.ResolvedPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ipath := ipfspath.Path(p.String())
|
||||||
|
ipath, err := core.ResolveIPNS(ctx, nsys, ipath)
|
||||||
|
if err == core.ErrNoNamesys {
|
||||||
|
return nil, coreiface.ErrOffline
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolveOnce resolver.ResolveOnce
|
||||||
|
|
||||||
|
switch ipath.Segments()[0] {
|
||||||
|
case "ipfs":
|
||||||
|
resolveOnce = uio.ResolveUnixfsOnce
|
||||||
|
case "ipld":
|
||||||
|
resolveOnce = resolver.ResolveSingle
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported path namespace: %s", p.Namespace())
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &resolver.Resolver{
|
||||||
|
DAG: ng,
|
||||||
|
ResolveOnce: resolveOnce,
|
||||||
|
}
|
||||||
|
|
||||||
|
node, rest, err := r.ResolveToLastNode(ctx, ipath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := cid.Parse(ipath.Segments()[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return coreiface.NewResolvedPath(ipath, node.Cid(), root, gopath.Join(rest...)), nil
|
||||||
|
}
|
154
core/coreapi/path_test.go
Normal file
154
core/coreapi/path_test.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package coreapi_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||||
|
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMutablePath(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_, api, err := makeAPI(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get self /ipns path
|
||||||
|
keys, err := api.Key().List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !keys[0].Path().Mutable() {
|
||||||
|
t.Error("expected self /ipns path to be mutable")
|
||||||
|
}
|
||||||
|
|
||||||
|
blk, err := api.Block().Put(ctx, strings.NewReader(`foo`))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if blk.Mutable() {
|
||||||
|
t.Error("expected /ipld path to be immutable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathRemainder(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_, api, err := makeAPI(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, err := coreiface.ParsePath(obj.String() + "/foo/bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rp1, err := api.ResolvePath(ctx, p1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rp1.Remainder() != "foo/bar" {
|
||||||
|
t.Error("expected to get path remainder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyPathRemainder(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_, api, err := makeAPI(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.Remainder() != "" {
|
||||||
|
t.Error("expected the resolved path to not have a remainder")
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, err := coreiface.ParsePath(obj.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rp1, err := api.ResolvePath(ctx, p1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rp1.Remainder() != "" {
|
||||||
|
t.Error("expected the resolved path to not have a remainder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidPathRemainder(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_, api, err := makeAPI(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, err := coreiface.ParsePath(obj.String() + "/bar/baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = api.ResolvePath(ctx, p1)
|
||||||
|
if err == nil || err.Error() != "no such link found" {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathRoot(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_, api, err := makeAPI(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw"))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"/": "`+blk.Cid().String()+`"}}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, err := coreiface.ParsePath(obj.String() + "/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rp, err := api.ResolvePath(ctx, p1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rp.Root().String() != obj.Cid().String() {
|
||||||
|
t.Error("unexpected path root")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rp.Cid().String() != blk.Cid().String() {
|
||||||
|
t.Error("unexpected path cid")
|
||||||
|
}
|
||||||
|
}
|
@ -9,11 +9,9 @@ import (
|
|||||||
caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||||
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
|
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
|
||||||
merkledag "github.com/ipfs/go-ipfs/merkledag"
|
merkledag "github.com/ipfs/go-ipfs/merkledag"
|
||||||
pin "github.com/ipfs/go-ipfs/pin"
|
|
||||||
|
|
||||||
offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline"
|
offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline"
|
||||||
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
|
||||||
ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PinAPI CoreAPI
|
type PinAPI CoreAPI
|
||||||
@ -26,7 +24,12 @@ func (api *PinAPI) Add(ctx context.Context, p coreiface.Path, opts ...caopts.Pin
|
|||||||
|
|
||||||
defer api.node.Blockstore.PinLock().Unlock()
|
defer api.node.Blockstore.PinLock().Unlock()
|
||||||
|
|
||||||
_, err = corerepo.Pin(api.node, ctx, []string{p.String()}, settings.Recursive)
|
rp, err := api.core().ResolvePath(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = corerepo.Pin(api.node, ctx, []string{rp.Cid().String()}, settings.Recursive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -46,7 +49,7 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) ([]coreif
|
|||||||
return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
|
return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pinLsAll(settings.Type, ctx, api.node.Pinning, api.node.DAG)
|
return api.pinLsAll(settings.Type, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *PinAPI) Rm(ctx context.Context, p coreiface.Path) error {
|
func (api *PinAPI) Rm(ctx context.Context, p coreiface.Path) error {
|
||||||
@ -64,7 +67,17 @@ func (api *PinAPI) Update(ctx context.Context, from coreiface.Path, to coreiface
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.node.Pinning.Update(ctx, from.Cid(), to.Cid(), settings.Unpin)
|
fp, err := api.core().ResolvePath(ctx, from)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tp, err := api.core().ResolvePath(ctx, to)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.node.Pinning.Update(ctx, fp.Cid(), tp.Cid(), settings.Unpin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type pinStatus struct {
|
type pinStatus struct {
|
||||||
@ -75,8 +88,8 @@ type pinStatus struct {
|
|||||||
|
|
||||||
// BadNode is used in PinVerifyRes
|
// BadNode is used in PinVerifyRes
|
||||||
type badNode struct {
|
type badNode struct {
|
||||||
cid *cid.Cid
|
path coreiface.ResolvedPath
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *pinStatus) Ok() bool {
|
func (s *pinStatus) Ok() bool {
|
||||||
@ -87,8 +100,8 @@ func (s *pinStatus) BadNodes() []coreiface.BadPinNode {
|
|||||||
return s.badNodes
|
return s.badNodes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *badNode) Path() coreiface.Path {
|
func (n *badNode) Path() coreiface.ResolvedPath {
|
||||||
return ParseCid(n.cid)
|
return n.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *badNode) Err() error {
|
func (n *badNode) Err() error {
|
||||||
@ -112,7 +125,7 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
|
|||||||
links, err := getLinks(ctx, root)
|
links, err := getLinks(ctx, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status := &pinStatus{ok: false, cid: root}
|
status := &pinStatus{ok: false, cid: root}
|
||||||
status.badNodes = []coreiface.BadPinNode{&badNode{cid: root, err: err}}
|
status.badNodes = []coreiface.BadPinNode{&badNode{path: coreiface.IpldPath(root), err: err}}
|
||||||
visited[key] = status
|
visited[key] = status
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
@ -143,18 +156,18 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
|
|||||||
|
|
||||||
type pinInfo struct {
|
type pinInfo struct {
|
||||||
pinType string
|
pinType string
|
||||||
object *cid.Cid
|
path coreiface.ResolvedPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pinInfo) Path() coreiface.Path {
|
func (p *pinInfo) Path() coreiface.ResolvedPath {
|
||||||
return ParseCid(p.object)
|
return p.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pinInfo) Type() string {
|
func (p *pinInfo) Type() string {
|
||||||
return p.pinType
|
return p.pinType
|
||||||
}
|
}
|
||||||
|
|
||||||
func pinLsAll(typeStr string, ctx context.Context, pinning pin.Pinner, dag ipld.DAGService) ([]coreiface.Pin, error) {
|
func (api *PinAPI) pinLsAll(typeStr string, ctx context.Context) ([]coreiface.Pin, error) {
|
||||||
|
|
||||||
keys := make(map[string]*pinInfo)
|
keys := make(map[string]*pinInfo)
|
||||||
|
|
||||||
@ -162,18 +175,18 @@ func pinLsAll(typeStr string, ctx context.Context, pinning pin.Pinner, dag ipld.
|
|||||||
for _, c := range keyList {
|
for _, c := range keyList {
|
||||||
keys[c.String()] = &pinInfo{
|
keys[c.String()] = &pinInfo{
|
||||||
pinType: typeStr,
|
pinType: typeStr,
|
||||||
object: c,
|
path: coreiface.IpldPath(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if typeStr == "direct" || typeStr == "all" {
|
if typeStr == "direct" || typeStr == "all" {
|
||||||
AddToResultKeys(pinning.DirectKeys(), "direct")
|
AddToResultKeys(api.node.Pinning.DirectKeys(), "direct")
|
||||||
}
|
}
|
||||||
if typeStr == "indirect" || typeStr == "all" {
|
if typeStr == "indirect" || typeStr == "all" {
|
||||||
set := cid.NewSet()
|
set := cid.NewSet()
|
||||||
for _, k := range pinning.RecursiveKeys() {
|
for _, k := range api.node.Pinning.RecursiveKeys() {
|
||||||
err := merkledag.EnumerateChildren(ctx, merkledag.GetLinksWithDAG(dag), k, set.Visit)
|
err := merkledag.EnumerateChildren(ctx, merkledag.GetLinksWithDAG(api.node.DAG), k, set.Visit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -181,7 +194,7 @@ func pinLsAll(typeStr string, ctx context.Context, pinning pin.Pinner, dag ipld.
|
|||||||
AddToResultKeys(set.Keys(), "indirect")
|
AddToResultKeys(set.Keys(), "indirect")
|
||||||
}
|
}
|
||||||
if typeStr == "recursive" || typeStr == "all" {
|
if typeStr == "recursive" || typeStr == "all" {
|
||||||
AddToResultKeys(pinning.RecursiveKeys(), "recursive")
|
AddToResultKeys(api.node.Pinning.RecursiveKeys(), "recursive")
|
||||||
}
|
}
|
||||||
|
|
||||||
out := make([]coreiface.Pin, 0, len(keys))
|
out := make([]coreiface.Pin, 0, len(keys))
|
||||||
@ -191,3 +204,7 @@ func pinLsAll(typeStr string, ctx context.Context, pinning pin.Pinner, dag ipld.
|
|||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *PinAPI) core() coreiface.CoreAPI {
|
||||||
|
return (*CoreAPI)(api)
|
||||||
|
}
|
||||||
|
@ -52,7 +52,7 @@ func TestPinSimple(t *testing.T) {
|
|||||||
t.Errorf("unexpected pin list len: %d", len(list))
|
t.Errorf("unexpected pin list len: %d", len(list))
|
||||||
}
|
}
|
||||||
|
|
||||||
if list[0].Path().String() != p.String() {
|
if list[0].Path().Cid().String() != p.Cid().String() {
|
||||||
t.Error("paths don't match")
|
t.Error("paths don't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ func TestPinRecursive(t *testing.T) {
|
|||||||
t.Errorf("unexpected pin list len: %d", len(list))
|
t.Errorf("unexpected pin list len: %d", len(list))
|
||||||
}
|
}
|
||||||
|
|
||||||
if list[0].Path().String() != p0.String() {
|
if list[0].Path().Cid().String() != p0.Cid().String() {
|
||||||
t.Error("unexpected path")
|
t.Error("unexpected path")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ func TestPinRecursive(t *testing.T) {
|
|||||||
t.Fatalf("unexpected badNodes len")
|
t.Fatalf("unexpected badNodes len")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.BadNodes()[0].Path().String() != p0.String() {
|
if r.BadNodes()[0].Path().Cid().String() != p0.Cid().String() {
|
||||||
t.Error("unexpected badNode path")
|
t.Error("unexpected badNode path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ type UnixfsAPI CoreAPI
|
|||||||
|
|
||||||
// Add builds a merkledag node from a reader, adds it to the blockstore,
|
// Add builds a merkledag node from a reader, adds it to the blockstore,
|
||||||
// and returns the key representing that node.
|
// and returns the key representing that node.
|
||||||
func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.Path, error) {
|
func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.ResolvedPath, error) {
|
||||||
k, err := coreunix.AddWithContext(ctx, api.node, r)
|
k, err := coreunix.AddWithContext(ctx, api.node, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -25,7 +25,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.Path, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ParseCid(c), nil
|
return coreiface.IpfsPath(c), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cat returns the data contained by an IPFS or IPNS object(s) at path `p`.
|
// Cat returns the data contained by an IPFS or IPNS object(s) at path `p`.
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
core "github.com/ipfs/go-ipfs/core"
|
core "github.com/ipfs/go-ipfs/core"
|
||||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
||||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||||
|
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||||
coreunix "github.com/ipfs/go-ipfs/core/coreunix"
|
coreunix "github.com/ipfs/go-ipfs/core/coreunix"
|
||||||
keystore "github.com/ipfs/go-ipfs/keystore"
|
keystore "github.com/ipfs/go-ipfs/keystore"
|
||||||
mdag "github.com/ipfs/go-ipfs/merkledag"
|
mdag "github.com/ipfs/go-ipfs/merkledag"
|
||||||
@ -29,14 +30,11 @@ import (
|
|||||||
const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe"
|
const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe"
|
||||||
|
|
||||||
// `echo -n 'hello, world!' | ipfs add`
|
// `echo -n 'hello, world!' | ipfs add`
|
||||||
var hello = coreapi.ResolvedPath("/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk", nil, nil)
|
var hello = "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk"
|
||||||
var helloStr = "hello, world!"
|
var helloStr = "hello, world!"
|
||||||
|
|
||||||
// `ipfs object new unixfs-dir`
|
|
||||||
var emptyDir = coreapi.ResolvedPath("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", nil, nil)
|
|
||||||
|
|
||||||
// `echo -n | ipfs add`
|
// `echo -n | ipfs add`
|
||||||
var emptyFile = coreapi.ResolvedPath("/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH", nil, nil)
|
var emptyFile = "/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
|
||||||
|
|
||||||
func makeAPIIdent(ctx context.Context, fullIdentity bool) (*core.IpfsNode, coreiface.CoreAPI, error) {
|
func makeAPIIdent(ctx context.Context, fullIdentity bool) (*core.IpfsNode, coreiface.CoreAPI, error) {
|
||||||
var ident config.Identity
|
var ident config.Identity
|
||||||
@ -98,11 +96,11 @@ func TestAdd(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.String() != hello.String() {
|
if p.String() != hello {
|
||||||
t.Fatalf("expected path %s, got: %s", hello, p)
|
t.Fatalf("expected path %s, got: %s", hello, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := api.Unixfs().Cat(ctx, hello)
|
r, err := api.Unixfs().Cat(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -130,7 +128,7 @@ func TestAddEmptyFile(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.String() != emptyFile.String() {
|
if p.String() != emptyFile {
|
||||||
t.Fatalf("expected path %s, got: %s", hello, p)
|
t.Fatalf("expected path %s, got: %s", hello, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,11 +147,16 @@ func TestCatBasic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
p = "/ipfs/" + p
|
p = "/ipfs/" + p
|
||||||
|
|
||||||
if p != hello.String() {
|
if p != hello {
|
||||||
t.Fatalf("expected CID %s, got: %s", hello, p)
|
t.Fatalf("expected CID %s, got: %s", hello, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := api.Unixfs().Cat(ctx, hello)
|
helloPath, err := coreiface.ParsePath(hello)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := api.Unixfs().Cat(ctx, helloPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -180,7 +183,12 @@ func TestCatEmptyFile(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := api.Unixfs().Cat(ctx, emptyFile)
|
emptyFilePath, err := coreiface.ParsePath(emptyFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := api.Unixfs().Cat(ctx, emptyFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -206,13 +214,18 @@ func TestCatDir(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
p := coreapi.ParseCid(edir.Cid())
|
p := coreiface.IpfsPath(edir.Cid())
|
||||||
|
|
||||||
if p.String() != emptyDir.String() {
|
emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir"))
|
||||||
t.Fatalf("expected path %s, got: %s", emptyDir, p)
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.Unixfs().Cat(ctx, emptyDir)
|
if p.String() != coreiface.IpfsPath(emptyDir.Cid()).String() {
|
||||||
|
t.Fatalf("expected path %s, got: %s", emptyDir.Cid(), p.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = api.Unixfs().Cat(ctx, coreiface.IpfsPath(emptyDir.Cid()))
|
||||||
if err != coreiface.ErrIsDir {
|
if err != coreiface.ErrIsDir {
|
||||||
t.Fatalf("expected ErrIsDir, got: %s", err)
|
t.Fatalf("expected ErrIsDir, got: %s", err)
|
||||||
}
|
}
|
||||||
@ -231,7 +244,7 @@ func TestCatNonUnixfs(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.Unixfs().Cat(ctx, coreapi.ParseCid(nd.Cid()))
|
_, err = api.Unixfs().Cat(ctx, coreiface.IpfsPath(nd.Cid()))
|
||||||
if !strings.Contains(err.Error(), "proto: required field") {
|
if !strings.Contains(err.Error(), "proto: required field") {
|
||||||
t.Fatalf("expected protobuf error, got: %s", err)
|
t.Fatalf("expected protobuf error, got: %s", err)
|
||||||
}
|
}
|
||||||
@ -244,7 +257,11 @@ func TestCatOffline(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.Unixfs().Cat(ctx, coreapi.ResolvedPath("/ipns/Qmfoobar", nil, nil))
|
p, err := coreiface.ParsePath("/ipns/Qmfoobar")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
_, err = api.Unixfs().Cat(ctx, p)
|
||||||
if err != coreiface.ErrOffline {
|
if err != coreiface.ErrOffline {
|
||||||
t.Fatalf("expected ErrOffline, got: %s", err)
|
t.Fatalf("expected ErrOffline, got: %s", err)
|
||||||
}
|
}
|
||||||
@ -266,7 +283,10 @@ func TestLs(t *testing.T) {
|
|||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
t.Errorf("unexpected path: %s", k)
|
t.Errorf("unexpected path: %s", k)
|
||||||
}
|
}
|
||||||
p := coreapi.ResolvedPath("/ipfs/"+parts[0], nil, nil)
|
p, err := coreiface.ParsePath("/ipfs/" + parts[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
links, err := api.Unixfs().Ls(ctx, p)
|
links, err := api.Unixfs().Ls(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -299,7 +319,12 @@ func TestLsEmptyDir(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
links, err := api.Unixfs().Ls(ctx, emptyDir)
|
emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir"))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(emptyDir.Cid()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -327,7 +352,7 @@ func TestLsNonUnixfs(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
links, err := api.Unixfs().Ls(ctx, coreapi.ParseCid(nd.Cid()))
|
links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(nd.Cid()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "github.com/ipfs/go-ipfs/core"
|
core "github.com/ipfs/go-ipfs/core"
|
||||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
|
||||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||||
"github.com/ipfs/go-ipfs/importer"
|
"github.com/ipfs/go-ipfs/importer"
|
||||||
dag "github.com/ipfs/go-ipfs/merkledag"
|
dag "github.com/ipfs/go-ipfs/merkledag"
|
||||||
@ -160,7 +159,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
|
|||||||
ipnsHostname = true
|
ipnsHostname = true
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedPath, err := coreapi.ParsePath(urlPath)
|
parsedPath, err := coreiface.ParsePath(urlPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
webError(w, "invalid ipfs path", err, http.StatusBadRequest)
|
webError(w, "invalid ipfs path", err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@ -288,7 +287,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(ixnd.Cid()))
|
dr, err := i.api.Unixfs().Cat(ctx, coreiface.IpfsPath(ixnd.Cid()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
internalWebError(w, err)
|
internalWebError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -171,7 +171,7 @@ func TestGatewayGet(t *testing.T) {
|
|||||||
{"working.example.com", "/", http.StatusOK, "fnord"},
|
{"working.example.com", "/", http.StatusOK, "fnord"},
|
||||||
{"double.example.com", "/", http.StatusOK, "fnord"},
|
{"double.example.com", "/", http.StatusOK, "fnord"},
|
||||||
{"triple.example.com", "/", http.StatusOK, "fnord"},
|
{"triple.example.com", "/", http.StatusOK, "fnord"},
|
||||||
{"working.example.com", "/ipfs/" + k, http.StatusNotFound, "ipfs resolve -r /ipns/working.example.com/ipfs/" + k + ": no link named \"ipfs\" under " + k + "\n"},
|
{"working.example.com", "/ipfs/" + k, http.StatusNotFound, "ipfs resolve -r /ipns/working.example.com/ipfs/" + k + ": no link by that name\n"},
|
||||||
{"broken.example.com", "/", http.StatusNotFound, "ipfs resolve -r /ipns/broken.example.com/: " + namesys.ErrResolveFailed.Error() + "\n"},
|
{"broken.example.com", "/", http.StatusNotFound, "ipfs resolve -r /ipns/broken.example.com/: " + namesys.ErrResolveFailed.Error() + "\n"},
|
||||||
{"broken.example.com", "/ipfs/" + k, http.StatusNotFound, "ipfs resolve -r /ipns/broken.example.com/ipfs/" + k + ": " + namesys.ErrResolveFailed.Error() + "\n"},
|
{"broken.example.com", "/ipfs/" + k, http.StatusNotFound, "ipfs resolve -r /ipns/broken.example.com/ipfs/" + k + ": " + namesys.ErrResolveFailed.Error() + "\n"},
|
||||||
} {
|
} {
|
||||||
|
@ -19,10 +19,8 @@ import (
|
|||||||
var ErrNoNamesys = errors.New(
|
var ErrNoNamesys = errors.New(
|
||||||
"core/resolve: no Namesys on IpfsNode - can't resolve ipns entry")
|
"core/resolve: no Namesys on IpfsNode - can't resolve ipns entry")
|
||||||
|
|
||||||
// Resolve resolves the given path by parsing out protocol-specific
|
// ResolveIPNS resolves /ipns paths
|
||||||
// entries (e.g. /ipns/<node-key>) and then going through the /ipfs/
|
func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (path.Path, error) {
|
||||||
// entries and returning the final node.
|
|
||||||
func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (ipld.Node, error) {
|
|
||||||
if strings.HasPrefix(p.String(), "/ipns/") {
|
if strings.HasPrefix(p.String(), "/ipns/") {
|
||||||
evt := log.EventBegin(ctx, "resolveIpnsPath")
|
evt := log.EventBegin(ctx, "resolveIpnsPath")
|
||||||
defer evt.Done()
|
defer evt.Done()
|
||||||
@ -31,36 +29,47 @@ func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver,
|
|||||||
// TODO(cryptix): we should be able to query the local cache for the path
|
// TODO(cryptix): we should be able to query the local cache for the path
|
||||||
if nsys == nil {
|
if nsys == nil {
|
||||||
evt.Append(logging.LoggableMap{"error": ErrNoNamesys.Error()})
|
evt.Append(logging.LoggableMap{"error": ErrNoNamesys.Error()})
|
||||||
return nil, ErrNoNamesys
|
return "", ErrNoNamesys
|
||||||
}
|
}
|
||||||
|
|
||||||
seg := p.Segments()
|
seg := p.Segments()
|
||||||
|
|
||||||
if len(seg) < 2 || seg[1] == "" { // just "/<protocol/>" without further segments
|
if len(seg) < 2 || seg[1] == "" { // just "/<protocol/>" without further segments
|
||||||
evt.Append(logging.LoggableMap{"error": path.ErrNoComponents.Error()})
|
evt.Append(logging.LoggableMap{"error": path.ErrNoComponents.Error()})
|
||||||
return nil, path.ErrNoComponents
|
return "", path.ErrNoComponents
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions := seg[2:]
|
extensions := seg[2:]
|
||||||
resolvable, err := path.FromSegments("/", seg[0], seg[1])
|
resolvable, err := path.FromSegments("/", seg[0], seg[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evt.Append(logging.LoggableMap{"error": err.Error()})
|
evt.Append(logging.LoggableMap{"error": err.Error()})
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
respath, err := nsys.Resolve(ctx, resolvable.String())
|
respath, err := nsys.Resolve(ctx, resolvable.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evt.Append(logging.LoggableMap{"error": err.Error()})
|
evt.Append(logging.LoggableMap{"error": err.Error()})
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
segments := append(respath.Segments(), extensions...)
|
segments := append(respath.Segments(), extensions...)
|
||||||
p, err = path.FromSegments("/", segments...)
|
p, err = path.FromSegments("/", segments...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evt.Append(logging.LoggableMap{"error": err.Error()})
|
evt.Append(logging.LoggableMap{"error": err.Error()})
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve resolves the given path by parsing out protocol-specific
|
||||||
|
// entries (e.g. /ipns/<node-key>) and then going through the /ipfs/
|
||||||
|
// entries and returning the final node.
|
||||||
|
func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (ipld.Node, error) {
|
||||||
|
p, err := ResolveIPNS(ctx, nsys, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// ok, we have an IPFS path now (or what we'll treat as one)
|
// ok, we have an IPFS path now (or what we'll treat as one)
|
||||||
return r.ResolvePath(ctx, p)
|
return r.ResolvePath(ctx, p)
|
||||||
|
@ -59,10 +59,11 @@ func (p Path) String() string {
|
|||||||
return string(p)
|
return string(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsJustAKey returns true if the path is of the form <key> or /ipfs/<key>.
|
// IsJustAKey returns true if the path is of the form <key> or /ipfs/<key>, or
|
||||||
|
// /ipld/<key>
|
||||||
func (p Path) IsJustAKey() bool {
|
func (p Path) IsJustAKey() bool {
|
||||||
parts := p.Segments()
|
parts := p.Segments()
|
||||||
return len(parts) == 2 && parts[0] == "ipfs"
|
return len(parts) == 2 && (parts[0] == "ipfs" || parts[0] == "ipld")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopLastSegment returns a new Path without its final segment, and the final
|
// PopLastSegment returns a new Path without its final segment, and the final
|
||||||
@ -120,7 +121,7 @@ func ParsePath(txt string) (Path, error) {
|
|||||||
if _, err := ParseCidToPath(parts[2]); err != nil {
|
if _, err := ParseCidToPath(parts[2]); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
} else if parts[1] != "ipns" {
|
} else if parts[1] != "ipns" && parts[1] != "ipld" { //TODO: make this smarter
|
||||||
return "", ErrBadPath
|
return "", ErrBadPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ func SplitList(pth string) []string {
|
|||||||
// must be a Multihash) and return it separately.
|
// must be a Multihash) and return it separately.
|
||||||
func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) {
|
func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) {
|
||||||
parts := fpath.Segments()
|
parts := fpath.Segments()
|
||||||
if parts[0] == "ipfs" {
|
if parts[0] == "ipfs" || parts[0] == "ipld" {
|
||||||
parts = parts[1:]
|
parts = parts[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ func TestPathParsing(t *testing.T) {
|
|||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true,
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true,
|
||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": true,
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": true,
|
||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
||||||
|
"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true,
|
||||||
|
"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": true,
|
||||||
|
"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
||||||
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
||||||
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true,
|
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true,
|
||||||
"QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
"QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true,
|
||||||
@ -36,6 +39,8 @@ func TestIsJustAKey(t *testing.T) {
|
|||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false,
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false,
|
||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false,
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false,
|
||||||
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false,
|
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false,
|
||||||
|
"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false,
|
||||||
|
"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
for p, expected := range cases {
|
for p, expected := range cases {
|
||||||
@ -57,6 +62,7 @@ func TestPopLastSegment(t *testing.T) {
|
|||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", "a"},
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", "a"},
|
||||||
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a", "b"},
|
"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a", "b"},
|
||||||
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"},
|
"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"},
|
||||||
|
"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for p, expected := range cases {
|
for p, expected := range cases {
|
||||||
|
@ -34,6 +34,9 @@ func (e ErrNoLink) Error() string {
|
|||||||
return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String())
|
return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveOnce resolves path through a single node
|
||||||
|
type ResolveOnce func(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error)
|
||||||
|
|
||||||
// Resolver provides path resolution to IPFS
|
// Resolver provides path resolution to IPFS
|
||||||
// It has a pointer to a DAGService, which is uses to resolve nodes.
|
// It has a pointer to a DAGService, which is uses to resolve nodes.
|
||||||
// TODO: now that this is more modular, try to unify this code with the
|
// TODO: now that this is more modular, try to unify this code with the
|
||||||
@ -41,7 +44,7 @@ func (e ErrNoLink) Error() string {
|
|||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
DAG ipld.NodeGetter
|
DAG ipld.NodeGetter
|
||||||
|
|
||||||
ResolveOnce func(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error)
|
ResolveOnce ResolveOnce
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBasicResolver constructs a new basic resolver.
|
// NewBasicResolver constructs a new basic resolver.
|
||||||
|
Reference in New Issue
Block a user