mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-28 08:47:42 +08:00
coreapi: make the interfaces path centric
The new coreiface.Path maps a path to the cid.Cid resulting from a full path resolution. The path is internally represented as a go-ipfs/path.Path, but that doesn't matter to the outside. Apart from the path-to-CID mapping, it also aims to hold all resolved segment CIDs of the path. Right now it only exposes Root(), and only for flat paths a la /ipfs/Qmfoo. In other cases, the root is nil. In the future, resolution will internally use go-ipfs/path.Resolver.ResolvePathComponents and thus always return the proper resolved segments, via Root(), or a future Segments() func. - Add coreiface.Path with Cid() and Root(). - Add CoreAPI.ResolvePath() for getting a coreiface.Path. - All functions now expect and return coreiface.Path. - Add ParsePath() and ParseCid() for constructing a coreiface.Path. - Add coreiface.Node and Link which are simply go-ipld-node.Node and Link. - Add CoreAPI.ResolveNode() for getting a Node from a Path. License: MIT Signed-off-by: Lars Gierth <larsg@systemli.org>
This commit is contained in:
@ -5,9 +5,9 @@ import (
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
ipfspath "github.com/ipfs/go-ipfs/path"
|
||||
|
||||
ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
|
||||
cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
|
||||
)
|
||||
|
||||
type CoreAPI struct {
|
||||
@ -23,17 +23,65 @@ func (api *CoreAPI) Unixfs() coreiface.UnixfsAPI {
|
||||
return (*UnixfsAPI)(api)
|
||||
}
|
||||
|
||||
func resolve(ctx context.Context, n *core.IpfsNode, p string) (ipld.Node, error) {
|
||||
pp, err := path.ParsePath(p)
|
||||
func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) {
|
||||
p, err := api.ResolvePath(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dagnode, err := core.Resolve(ctx, n.Namesys, n.Resolver, pp)
|
||||
node, err := api.node.DAG.Get(ctx, p.Cid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// TODO: store all of ipfspath.Resolver.ResolvePathComponents() in Path
|
||||
func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.Path, error) {
|
||||
if p.Resolved() {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
p2 := ipfspath.FromString(p.String())
|
||||
node, err := core.Resolve(ctx, api.node.Namesys, api.node.Resolver, p2)
|
||||
if err == core.ErrNoNamesys {
|
||||
return nil, coreiface.ErrOffline
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dagnode, nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func ParsePath(p string) (coreiface.Path, error) {
|
||||
pp, err := ipfspath.ParsePath(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &path{path: pp}, nil
|
||||
}
|
||||
|
||||
func ParseCid(c *cid.Cid) coreiface.Path {
|
||||
return &path{path: ipfspath.FromCid(c), cid: c, root: c}
|
||||
}
|
||||
|
||||
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 }
|
||||
|
@ -9,6 +9,16 @@ import (
|
||||
ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
|
||||
)
|
||||
|
||||
type Path interface {
|
||||
String() string
|
||||
Cid() *cid.Cid
|
||||
Root() *cid.Cid
|
||||
Resolved() bool
|
||||
}
|
||||
|
||||
// TODO: should we really copy these?
|
||||
// if we didn't, godoc would generate nice links straight to go-ipld-node
|
||||
type Node ipld.Node
|
||||
type Link ipld.Link
|
||||
|
||||
type Reader interface {
|
||||
@ -18,12 +28,14 @@ type Reader interface {
|
||||
|
||||
type CoreAPI interface {
|
||||
Unixfs() UnixfsAPI
|
||||
ResolvePath(context.Context, Path) (Path, error)
|
||||
ResolveNode(context.Context, Path) (Node, error)
|
||||
}
|
||||
|
||||
type UnixfsAPI interface {
|
||||
Add(context.Context, io.Reader) (*cid.Cid, error)
|
||||
Cat(context.Context, string) (Reader, error)
|
||||
Ls(context.Context, string) ([]*Link, error)
|
||||
Add(context.Context, io.Reader) (Path, error)
|
||||
Cat(context.Context, Path) (Reader, error)
|
||||
Ls(context.Context, Path) ([]*Link, error)
|
||||
}
|
||||
|
||||
// type ObjectAPI interface {
|
||||
@ -49,5 +61,4 @@ type UnixfsAPI interface {
|
||||
// }
|
||||
|
||||
var ErrIsDir = errors.New("object is a directory")
|
||||
var ErrIsNonDag = errors.New("not a merkledag object")
|
||||
var ErrOffline = errors.New("can't resolve, ipfs node is offline")
|
||||
|
@ -13,16 +13,20 @@ import (
|
||||
|
||||
type UnixfsAPI CoreAPI
|
||||
|
||||
func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (*cid.Cid, error) {
|
||||
func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.Path, error) {
|
||||
k, err := coreunix.AddWithContext(ctx, api.node, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cid.Decode(k)
|
||||
c, err := cid.Decode(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseCid(c), nil
|
||||
}
|
||||
|
||||
func (api *UnixfsAPI) Cat(ctx context.Context, p string) (coreiface.Reader, error) {
|
||||
dagnode, err := resolve(ctx, api.node, p)
|
||||
func (api *UnixfsAPI) Cat(ctx context.Context, p coreiface.Path) (coreiface.Reader, error) {
|
||||
dagnode, err := api.core().ResolveNode(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -36,8 +40,8 @@ func (api *UnixfsAPI) Cat(ctx context.Context, p string) (coreiface.Reader, erro
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (api *UnixfsAPI) Ls(ctx context.Context, p string) ([]*coreiface.Link, error) {
|
||||
dagnode, err := resolve(ctx, api.node, p)
|
||||
func (api *UnixfsAPI) Ls(ctx context.Context, p coreiface.Path) ([]*coreiface.Link, error) {
|
||||
dagnode, err := api.core().ResolveNode(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -49,3 +53,7 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p string) ([]*coreiface.Link, erro
|
||||
}
|
||||
return links, nil
|
||||
}
|
||||
|
||||
func (api *UnixfsAPI) core() coreiface.CoreAPI {
|
||||
return (*CoreAPI)(api)
|
||||
}
|
||||
|
@ -19,14 +19,14 @@ import (
|
||||
)
|
||||
|
||||
// `echo -n 'hello, world!' | ipfs add`
|
||||
var hello = "QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk"
|
||||
var hello = coreapi.ResolvedPath("/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk", nil, nil)
|
||||
var helloStr = "hello, world!"
|
||||
|
||||
// `ipfs object new unixfs-dir`
|
||||
var emptyUnixfsDir = "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"
|
||||
var emptyDir = coreapi.ResolvedPath("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", nil, nil)
|
||||
|
||||
// `echo -n | ipfs add`
|
||||
var emptyUnixfsFile = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
|
||||
var emptyFile = coreapi.ResolvedPath("/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH", nil, nil)
|
||||
|
||||
func makeAPI(ctx context.Context) (*core.IpfsNode, coreiface.UnixfsAPI, error) {
|
||||
r := &repo.Mock{
|
||||
@ -53,13 +53,13 @@ func TestAdd(t *testing.T) {
|
||||
}
|
||||
|
||||
str := strings.NewReader(helloStr)
|
||||
c, err := api.Add(ctx, str)
|
||||
p, err := api.Add(ctx, str)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if c.String() != hello {
|
||||
t.Fatalf("expected CID %s, got: %s", hello, c)
|
||||
if p.String() != hello.String() {
|
||||
t.Fatalf("expected path %s, got: %s", hello, p)
|
||||
}
|
||||
|
||||
r, err := api.Cat(ctx, hello)
|
||||
@ -85,13 +85,13 @@ func TestAddEmptyFile(t *testing.T) {
|
||||
}
|
||||
|
||||
str := strings.NewReader("")
|
||||
c, err := api.Add(ctx, str)
|
||||
p, err := api.Add(ctx, str)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if c.String() != emptyUnixfsFile {
|
||||
t.Fatalf("expected CID %s, got: %s", hello, c)
|
||||
if p.String() != emptyFile.String() {
|
||||
t.Fatalf("expected path %s, got: %s", hello, p)
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,16 +103,17 @@ func TestCatBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
hr := strings.NewReader(helloStr)
|
||||
k, err := coreunix.Add(node, hr)
|
||||
p, err := coreunix.Add(node, hr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p = "/ipfs/" + p
|
||||
|
||||
if k != hello {
|
||||
t.Fatalf("expected CID %s, got: %s", hello, k)
|
||||
if p != hello.String() {
|
||||
t.Fatalf("expected CID %s, got: %s", hello, p)
|
||||
}
|
||||
|
||||
r, err := api.Cat(ctx, k)
|
||||
r, err := api.Cat(ctx, hello)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -139,7 +140,7 @@ func TestCatEmptyFile(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := api.Cat(ctx, emptyUnixfsFile)
|
||||
r, err := api.Cat(ctx, emptyFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -165,8 +166,13 @@ func TestCatDir(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
p := coreapi.ParseCid(c)
|
||||
|
||||
_, err = api.Cat(ctx, c.String())
|
||||
if p.String() != emptyDir.String() {
|
||||
t.Fatalf("expected path %s, got: %s", emptyDir, p)
|
||||
}
|
||||
|
||||
_, err = api.Cat(ctx, emptyDir)
|
||||
if err != coreiface.ErrIsDir {
|
||||
t.Fatalf("expected ErrIsDir, got: %s", err)
|
||||
}
|
||||
@ -184,7 +190,7 @@ func TestCatNonUnixfs(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Cat(ctx, c.String())
|
||||
_, err = api.Cat(ctx, coreapi.ParseCid(c))
|
||||
if !strings.Contains(err.Error(), "proto: required field") {
|
||||
t.Fatalf("expected protobuf error, got: %s", err)
|
||||
}
|
||||
@ -197,7 +203,7 @@ func TestCatOffline(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Cat(ctx, "/ipns/Qmfoobar")
|
||||
_, err = api.Cat(ctx, coreapi.ResolvedPath("/ipns/Qmfoobar", nil, nil))
|
||||
if err != coreiface.ErrOffline {
|
||||
t.Fatalf("expected ErrOffline, got: %", err)
|
||||
}
|
||||
@ -211,17 +217,17 @@ func TestLs(t *testing.T) {
|
||||
}
|
||||
|
||||
r := strings.NewReader("content-of-file")
|
||||
p, _, err := coreunix.AddWrapped(node, r, "name-of-file")
|
||||
k, _, err := coreunix.AddWrapped(node, r, "name-of-file")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
parts := strings.Split(p, "/")
|
||||
parts := strings.Split(k, "/")
|
||||
if len(parts) != 2 {
|
||||
t.Errorf("unexpected path:", p)
|
||||
t.Errorf("unexpected path:", k)
|
||||
}
|
||||
k := parts[0]
|
||||
p := coreapi.ResolvedPath("/ipfs/"+parts[0], nil, nil)
|
||||
|
||||
links, err := api.Ls(ctx, k)
|
||||
links, err := api.Ls(ctx, p)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -236,7 +242,7 @@ func TestLs(t *testing.T) {
|
||||
t.Fatalf("expected name = name-of-file, got %s", links[0].Name)
|
||||
}
|
||||
if links[0].Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" {
|
||||
t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", links[0].Cid.String())
|
||||
t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", links[0].Cid)
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,12 +253,12 @@ func TestLsEmptyDir(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
c, err := node.DAG.Add(unixfs.EmptyDirNode())
|
||||
_, err = node.DAG.Add(unixfs.EmptyDirNode())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
links, err := api.Ls(ctx, c.String())
|
||||
links, err := api.Ls(ctx, emptyDir)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -275,7 +281,7 @@ func TestLsNonUnixfs(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
links, err := api.Ls(ctx, c.String())
|
||||
links, err := api.Ls(ctx, coreapi.ParseCid(c))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/importer"
|
||||
chunk "github.com/ipfs/go-ipfs/importer/chunk"
|
||||
@ -158,7 +159,13 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
|
||||
ipnsHostname = true
|
||||
}
|
||||
|
||||
dr, err := i.api.Unixfs().Cat(ctx, urlPath)
|
||||
parsedPath, err := coreapi.ParsePath(urlPath)
|
||||
if err != nil {
|
||||
webError(w, "invalid ipfs path", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
dr, err := i.api.Unixfs().Cat(ctx, parsedPath)
|
||||
dir := false
|
||||
switch err {
|
||||
case nil:
|
||||
@ -218,7 +225,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
|
||||
return
|
||||
}
|
||||
|
||||
links, err := i.api.Unixfs().Ls(ctx, urlPath)
|
||||
links, err := i.api.Unixfs().Ls(ctx, parsedPath)
|
||||
if err != nil {
|
||||
internalWebError(w, err)
|
||||
return
|
||||
@ -240,14 +247,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
|
||||
return
|
||||
}
|
||||
|
||||
p, err := path.ParsePath(urlPath + "/index.html")
|
||||
if err != nil {
|
||||
internalWebError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// return index page instead.
|
||||
dr, err := i.api.Unixfs().Cat(ctx, p.String())
|
||||
dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(link.Cid))
|
||||
if err != nil {
|
||||
internalWebError(w, err)
|
||||
return
|
||||
@ -314,15 +314,15 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
|
||||
}
|
||||
|
||||
func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
k, err := i.api.Unixfs().Add(ctx, r.Body)
|
||||
p, err := i.api.Unixfs().Add(ctx, r.Body)
|
||||
if err != nil {
|
||||
internalWebError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
i.addUserHeaders(w) // ok, _now_ write user's headers.
|
||||
w.Header().Set("IPFS-Hash", k.String())
|
||||
http.Redirect(w, r, ipfsPathPrefix+k.String(), http.StatusCreated)
|
||||
w.Header().Set("IPFS-Hash", p.Cid().String())
|
||||
http.Redirect(w, r, p.String(), http.StatusCreated)
|
||||
}
|
||||
|
||||
func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
Reference in New Issue
Block a user