@cryptotaxi247 / kubo / commits / 9b288560a

coreapi: path remainders

License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>

Łukasz Magiera committed Apr 3, 2018 at 14:49 UTC 9b288560a44ad98ae9c8fdbcd13a7c8de60ed27c
5 files changed +149 -26
core/coreapi/interface/path.go
+4 -3
@@ -29,13 +29,14 @@ type Path interface {
29
30 // ResolvedPath is a resolved Path
31 type ResolvedPath interface {
32 - // Cid returns cid referred to by path
32 + // Cid returns the CID referred to by path
33 Cid() *cid.Cid
34
35 - // Root returns cid of root path
35 + // Root returns the CID of root path
36 Root() *cid.Cid
37
38 - //TODO: Path remainder
38 + // Remainder returns unresolved part of the path
39 + Remainder() string
40
41 Path
42 }
core/coreapi/path.go
+45 -13
@@ -1,7 +1,9 @@
1 package coreapi
2
3 import (
4 - "context"
4 + context "context"
5 + gopath "path"
6 + strings "strings"
7
8 core "github.com/ipfs/go-ipfs/core"
9 coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
@@ -22,18 +24,29 @@ type path struct {
24 // resolvedPath implements coreiface.resolvedPath
25 type resolvedPath struct {
26 path
25 - cid *cid.Cid
26 - root *cid.Cid
27 + cid *cid.Cid
28 + root *cid.Cid
29 + remainder string
30 }
31
32 // IpfsPath parses the path from `c`, reruns the parsed path.
33 func (api *CoreAPI) IpfsPath(c *cid.Cid) coreiface.ResolvedPath {
31 - return &resolvedPath{path: path{ipfspath.Path("/ipfs/" + c.String())}, cid: c, root: c}
34 + return &resolvedPath{
35 + path: path{ipfspath.Path("/ipfs/" + c.String())},
36 + cid: c,
37 + root: c,
38 + remainder: "",
39 + }
40 }
41
42 // IpldPath parses the path from `c`, reruns the parsed path.
43 func (api *CoreAPI) IpldPath(c *cid.Cid) coreiface.ResolvedPath {
36 - return &resolvedPath{path: path{ipfspath.Path("/ipld/" + c.String())}, cid: c, root: c}
44 + return &resolvedPath{
45 + path: path{ipfspath.Path("/ipld/" + c.String())},
46 + cid: c,
47 + root: c,
48 + remainder: "",
49 + }
50 }
51
52 // 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
79 return p.(coreiface.ResolvedPath), nil
80 }
81
82 + ipath := p.(*path).path
83 + ipath, err := core.ResolveIPNS(ctx, nsys, ipath)
84 + if err == core.ErrNoNamesys {
85 + return nil, coreiface.ErrOffline
86 + } else if err != nil {
87 + return nil, err
88 + }
89 +
90 + resolveOnce := uio.ResolveUnixfsOnce
91 + if strings.HasPrefix(ipath.String(), "/ipld") {
92 + resolveOnce = resolver.ResolveSingle
93 + }
94 +
95 r := &resolver.Resolver{
96 DAG: ng,
71 - ResolveOnce: uio.ResolveUnixfsOnce,
97 + ResolveOnce: resolveOnce,
98 }
99
74 - p2 := ipfspath.FromString(p.String())
75 - node, err := core.Resolve(ctx, nsys, r, p2)
76 - if err == core.ErrNoNamesys {
77 - return nil, coreiface.ErrOffline
78 - } else if err != nil {
100 + node, rest, err := r.ResolveToLastNode(ctx, ipath)
101 + if err != nil {
102 return nil, err
103 }
104
105 var root *cid.Cid
83 - if p2.IsJustAKey() {
106 + if ipath.IsJustAKey() {
107 root = node.Cid()
108 }
109
87 - return &resolvedPath{path: path{p2}, cid: node.Cid(), root: root}, nil
110 + return &resolvedPath{
111 + path: path{ipath},
112 + cid: node.Cid(),
113 + root: root,
114 + remainder: gopath.Join(rest...),
115 + }, nil
116 }
117
118 // ParsePath parses path `p` using ipfspath parser, returns the parsed path.
@@ -120,3 +148,7 @@ func (p *resolvedPath) Cid() *cid.Cid {
148 func (p *resolvedPath) Root() *cid.Cid {
149 return p.root
150 }
151 +
152 +func (p *resolvedPath) Remainder() string {
153 + return p.remainder
154 +}
core/coreapi/path_test.go
+81
@@ -32,3 +32,84 @@ func TestMutablePath(t *testing.T) {
32 t.Error("expected /ipld path to be immutable")
33 }
34 }
35 +
36 +func TestPathRemainder(t *testing.T) {
37 + ctx := context.Background()
38 + _, api, err := makeAPI(ctx)
39 + if err != nil {
40 + t.Fatal(err)
41 + }
42 +
43 + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`))
44 + if err != nil {
45 + t.Fatal(err)
46 + }
47 +
48 + p1, err := api.ParsePath(obj.String() + "/foo/bar")
49 + if err != nil {
50 + t.Error(err)
51 + }
52 +
53 + rp1, err := api.ResolvePath(ctx, p1)
54 + if err != nil {
55 + t.Fatal(err)
56 + }
57 +
58 + if rp1.Remainder() != "foo/bar" {
59 + t.Error("expected to get path remainder")
60 + }
61 +}
62 +
63 +func TestEmptyPathRemainder(t *testing.T) {
64 + ctx := context.Background()
65 + _, api, err := makeAPI(ctx)
66 + if err != nil {
67 + t.Fatal(err)
68 + }
69 +
70 + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`))
71 + if err != nil {
72 + t.Fatal(err)
73 + }
74 +
75 + if obj.Remainder() != "" {
76 + t.Error("expected the resolved path to not have a remainder")
77 + }
78 +
79 + p1, err := api.ParsePath(obj.String())
80 + if err != nil {
81 + t.Error(err)
82 + }
83 +
84 + rp1, err := api.ResolvePath(ctx, p1)
85 + if err != nil {
86 + t.Fatal(err)
87 + }
88 +
89 + if rp1.Remainder() != "" {
90 + t.Error("expected the resolved path to not have a remainder")
91 + }
92 +}
93 +
94 +func TestInvalidPathRemainder(t *testing.T) {
95 + ctx := context.Background()
96 + _, api, err := makeAPI(ctx)
97 + if err != nil {
98 + t.Fatal(err)
99 + }
100 +
101 + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`))
102 + if err != nil {
103 + t.Fatal(err)
104 + }
105 +
106 + p1, err := api.ParsePath(obj.String() + "/bar/baz")
107 + if err != nil {
108 + t.Error(err)
109 + }
110 +
111 + _, err = api.ResolvePath(ctx, p1)
112 + if err == nil || err.Error() != "no such link found" {
113 + t.Fatalf("unexpected error: %s", err)
114 + }
115 +}
core/pathresolver.go
+18 -9
@@ -19,10 +19,8 @@ import (
19 var ErrNoNamesys = errors.New(
20 "core/resolve: no Namesys on IpfsNode - can't resolve ipns entry")
21
22 -// Resolve resolves the given path by parsing out protocol-specific
23 -// entries (e.g. /ipns/<node-key>) and then going through the /ipfs/
24 -// entries and returning the final node.
25 -func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (ipld.Node, error) {
22 +// ResolveIPNS resolves /ipns paths
23 +func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (path.Path, error) {
24 if strings.HasPrefix(p.String(), "/ipns/") {
25 evt := log.EventBegin(ctx, "resolveIpnsPath")
26 defer evt.Done()
@@ -31,36 +29,47 @@ func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver,
29 // TODO(cryptix): we should be able to query the local cache for the path
30 if nsys == nil {
31 evt.Append(logging.LoggableMap{"error": ErrNoNamesys.Error()})
34 - return nil, ErrNoNamesys
32 + return "", ErrNoNamesys
33 }
34
35 seg := p.Segments()
36
37 if len(seg) < 2 || seg[1] == "" { // just "/<protocol/>" without further segments
38 evt.Append(logging.LoggableMap{"error": path.ErrNoComponents.Error()})
41 - return nil, path.ErrNoComponents
39 + return "", path.ErrNoComponents
40 }
41
42 extensions := seg[2:]
43 resolvable, err := path.FromSegments("/", seg[0], seg[1])
44 if err != nil {
45 evt.Append(logging.LoggableMap{"error": err.Error()})
48 - return nil, err
46 + return "", err
47 }
48
49 respath, err := nsys.Resolve(ctx, resolvable.String())
50 if err != nil {
51 evt.Append(logging.LoggableMap{"error": err.Error()})
54 - return nil, err
52 + return "", err
53 }
54
55 segments := append(respath.Segments(), extensions...)
56 p, err = path.FromSegments("/", segments...)
57 if err != nil {
58 evt.Append(logging.LoggableMap{"error": err.Error()})
61 - return nil, err
59 + return "", err
60 }
61 }
62 + return p, nil
63 +}
64 +
65 +// Resolve resolves the given path by parsing out protocol-specific
66 +// entries (e.g. /ipns/<node-key>) and then going through the /ipfs/
67 +// entries and returning the final node.
68 +func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (ipld.Node, error) {
69 + p, err := ResolveIPNS(ctx, nsys, p)
70 + if err != nil {
71 + return nil, err
72 + }
73
74 // ok, we have an IPFS path now (or what we'll treat as one)
75 return r.ResolvePath(ctx, p)
path/path.go
+1 -1
@@ -161,7 +161,7 @@ func SplitList(pth string) []string {
161 // must be a Multihash) and return it separately.
162 func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) {
163 parts := fpath.Segments()
164 - if parts[0] == "ipfs" {
164 + if parts[0] == "ipfs" || parts[0] == "ipld" {
165 parts = parts[1:]
166 }
167