1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-08-06 19:44:01 +08:00

coreapi: path remainders

License: MIT
Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
This commit is contained in:
Łukasz Magiera
2018-04-03 14:49:33 +02:00
parent 7ee6194352
commit 9b288560a4
5 changed files with 150 additions and 27 deletions

View File

@ -29,13 +29,14 @@ type Path interface {
// ResolvedPath is a resolved Path // ResolvedPath is a resolved Path
type ResolvedPath interface { type ResolvedPath interface {
// Cid returns cid referred to by path // Cid returns the CID referred to by path
Cid() *cid.Cid Cid() *cid.Cid
// Root returns cid of root path // Root returns the CID of root path
Root() *cid.Cid Root() *cid.Cid
//TODO: Path remainder // Remainder returns unresolved part of the path
Remainder() string
Path Path
} }

View File

@ -1,7 +1,9 @@
package coreapi package coreapi
import ( import (
"context" context "context"
gopath "path"
strings "strings"
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"
@ -24,16 +26,27 @@ type resolvedPath struct {
path path
cid *cid.Cid cid *cid.Cid
root *cid.Cid root *cid.Cid
remainder string
} }
// IpfsPath parses the path from `c`, reruns the parsed path. // IpfsPath parses the path from `c`, reruns the parsed path.
func (api *CoreAPI) IpfsPath(c *cid.Cid) coreiface.ResolvedPath { func (api *CoreAPI) IpfsPath(c *cid.Cid) coreiface.ResolvedPath {
return &resolvedPath{path: path{ipfspath.Path("/ipfs/" + c.String())}, cid: c, root: c} return &resolvedPath{
path: path{ipfspath.Path("/ipfs/" + c.String())},
cid: c,
root: c,
remainder: "",
}
} }
// IpldPath parses the path from `c`, reruns the parsed path. // IpldPath parses the path from `c`, reruns the parsed path.
func (api *CoreAPI) IpldPath(c *cid.Cid) coreiface.ResolvedPath { func (api *CoreAPI) IpldPath(c *cid.Cid) coreiface.ResolvedPath {
return &resolvedPath{path: path{ipfspath.Path("/ipld/" + c.String())}, cid: c, root: c} return &resolvedPath{
path: path{ipfspath.Path("/ipld/" + c.String())},
cid: c,
root: c,
remainder: "",
}
} }
// ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the // ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the
@ -66,25 +79,40 @@ func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSyste
return p.(coreiface.ResolvedPath), nil return p.(coreiface.ResolvedPath), nil
} }
r := &resolver.Resolver{ ipath := p.(*path).path
DAG: ng, ipath, err := core.ResolveIPNS(ctx, nsys, ipath)
ResolveOnce: uio.ResolveUnixfsOnce,
}
p2 := ipfspath.FromString(p.String())
node, err := core.Resolve(ctx, nsys, r, p2)
if err == core.ErrNoNamesys { if err == core.ErrNoNamesys {
return nil, coreiface.ErrOffline return nil, coreiface.ErrOffline
} else if err != nil { } else if err != nil {
return nil, err return nil, err
} }
resolveOnce := uio.ResolveUnixfsOnce
if strings.HasPrefix(ipath.String(), "/ipld") {
resolveOnce = resolver.ResolveSingle
}
r := &resolver.Resolver{
DAG: ng,
ResolveOnce: resolveOnce,
}
node, rest, err := r.ResolveToLastNode(ctx, ipath)
if err != nil {
return nil, err
}
var root *cid.Cid var root *cid.Cid
if p2.IsJustAKey() { if ipath.IsJustAKey() {
root = node.Cid() root = node.Cid()
} }
return &resolvedPath{path: path{p2}, cid: node.Cid(), root: root}, nil return &resolvedPath{
path: path{ipath},
cid: node.Cid(),
root: root,
remainder: gopath.Join(rest...),
}, nil
} }
// ParsePath parses path `p` using ipfspath parser, returns the parsed path. // ParsePath parses path `p` using ipfspath parser, returns the parsed path.
@ -120,3 +148,7 @@ func (p *resolvedPath) Cid() *cid.Cid {
func (p *resolvedPath) Root() *cid.Cid { func (p *resolvedPath) Root() *cid.Cid {
return p.root return p.root
} }
func (p *resolvedPath) Remainder() string {
return p.remainder
}

View File

@ -32,3 +32,84 @@ func TestMutablePath(t *testing.T) {
t.Error("expected /ipld path to be immutable") 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 := api.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 := api.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 := api.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)
}
}

View File

@ -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)

View File

@ -161,7 +161,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:]
} }