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