mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-18 00:55:48 +08:00
coreapi: path remainders
License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
This commit is contained in:
@ -29,13 +29,14 @@ type Path interface {
|
||||
|
||||
// ResolvedPath is a resolved Path
|
||||
type ResolvedPath interface {
|
||||
// Cid returns cid referred to by path
|
||||
// Cid returns the CID referred to by path
|
||||
Cid() *cid.Cid
|
||||
|
||||
// Root returns cid of root path
|
||||
// Root returns the CID of root path
|
||||
Root() *cid.Cid
|
||||
|
||||
//TODO: Path remainder
|
||||
// Remainder returns unresolved part of the path
|
||||
Remainder() string
|
||||
|
||||
Path
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package coreapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
context "context"
|
||||
gopath "path"
|
||||
strings "strings"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
@ -22,18 +24,29 @@ type path struct {
|
||||
// resolvedPath implements coreiface.resolvedPath
|
||||
type resolvedPath struct {
|
||||
path
|
||||
cid *cid.Cid
|
||||
root *cid.Cid
|
||||
cid *cid.Cid
|
||||
root *cid.Cid
|
||||
remainder string
|
||||
}
|
||||
|
||||
// IpfsPath parses the path from `c`, reruns the parsed path.
|
||||
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.
|
||||
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
|
||||
@ -66,25 +79,40 @@ func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSyste
|
||||
return p.(coreiface.ResolvedPath), nil
|
||||
}
|
||||
|
||||
r := &resolver.Resolver{
|
||||
DAG: ng,
|
||||
ResolveOnce: uio.ResolveUnixfsOnce,
|
||||
}
|
||||
|
||||
p2 := ipfspath.FromString(p.String())
|
||||
node, err := core.Resolve(ctx, nsys, r, p2)
|
||||
ipath := p.(*path).path
|
||||
ipath, err := core.ResolveIPNS(ctx, nsys, ipath)
|
||||
if err == core.ErrNoNamesys {
|
||||
return nil, coreiface.ErrOffline
|
||||
} else if err != nil {
|
||||
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
|
||||
if p2.IsJustAKey() {
|
||||
if ipath.IsJustAKey() {
|
||||
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.
|
||||
@ -120,3 +148,7 @@ func (p *resolvedPath) Cid() *cid.Cid {
|
||||
func (p *resolvedPath) Root() *cid.Cid {
|
||||
return p.root
|
||||
}
|
||||
|
||||
func (p *resolvedPath) Remainder() string {
|
||||
return p.remainder
|
||||
}
|
||||
|
@ -32,3 +32,84 @@ func TestMutablePath(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,8 @@ import (
|
||||
var ErrNoNamesys = errors.New(
|
||||
"core/resolve: no Namesys on IpfsNode - can't resolve ipns entry")
|
||||
|
||||
// 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) {
|
||||
// ResolveIPNS resolves /ipns paths
|
||||
func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (path.Path, error) {
|
||||
if strings.HasPrefix(p.String(), "/ipns/") {
|
||||
evt := log.EventBegin(ctx, "resolveIpnsPath")
|
||||
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
|
||||
if nsys == nil {
|
||||
evt.Append(logging.LoggableMap{"error": ErrNoNamesys.Error()})
|
||||
return nil, ErrNoNamesys
|
||||
return "", ErrNoNamesys
|
||||
}
|
||||
|
||||
seg := p.Segments()
|
||||
|
||||
if len(seg) < 2 || seg[1] == "" { // just "/<protocol/>" without further segments
|
||||
evt.Append(logging.LoggableMap{"error": path.ErrNoComponents.Error()})
|
||||
return nil, path.ErrNoComponents
|
||||
return "", path.ErrNoComponents
|
||||
}
|
||||
|
||||
extensions := seg[2:]
|
||||
resolvable, err := path.FromSegments("/", seg[0], seg[1])
|
||||
if err != nil {
|
||||
evt.Append(logging.LoggableMap{"error": err.Error()})
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
respath, err := nsys.Resolve(ctx, resolvable.String())
|
||||
if err != nil {
|
||||
evt.Append(logging.LoggableMap{"error": err.Error()})
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
segments := append(respath.Segments(), extensions...)
|
||||
p, err = path.FromSegments("/", segments...)
|
||||
if err != nil {
|
||||
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)
|
||||
return r.ResolvePath(ctx, p)
|
||||
|
@ -161,7 +161,7 @@ func SplitList(pth string) []string {
|
||||
// must be a Multihash) and return it separately.
|
||||
func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) {
|
||||
parts := fpath.Segments()
|
||||
if parts[0] == "ipfs" {
|
||||
if parts[0] == "ipfs" || parts[0] == "ipld" {
|
||||
parts = parts[1:]
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user