feat(path)!: consolidated path libraries (#334)

This commit was moved from ipfs/boxo@85c180e266
This commit is contained in:
Henrique Dias 2023-10-06 16:04:23 +02:00 committed by GitHub
parent 4e0f9aa922
commit 5bac37c735
17 changed files with 206 additions and 447 deletions

View File

@ -4,9 +4,8 @@ import (
"context"
"io"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/path"
)
// BlockStat contains information about a block
@ -15,7 +14,7 @@ type BlockStat interface {
Size() int
// Path returns path to the block
Path() path.Resolved
Path() path.ImmutablePath
}
// BlockAPI specifies the interface to the block layer

View File

@ -5,9 +5,8 @@ package iface
import (
"context"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/path"
ipld "github.com/ipfs/go-ipld-format"
)
@ -47,8 +46,10 @@ type CoreAPI interface {
// Routing returns an implementation of Routing API
Routing() RoutingAPI
// ResolvePath resolves the path using Unixfs resolver
ResolvePath(context.Context, path.Path) (path.Resolved, error)
// ResolvePath resolves the path using UnixFS resolver, and returns the resolved
// immutable path, and the remainder of the path segments that cannot be resolved
// within UnixFS.
ResolvePath(context.Context, path.Path) (path.ImmutablePath, []string, error)
// ResolveNode resolves the path (if not resolved already) using Unixfs
// resolver, gets and returns the resolved Node

View File

@ -3,7 +3,7 @@ package iface
import (
"context"
"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/path"
"github.com/ipfs/boxo/coreiface/options"

View File

@ -3,7 +3,7 @@ package iface
import (
"context"
"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/path"
"github.com/ipfs/boxo/coreiface/options"

View File

@ -4,10 +4,9 @@ import (
"context"
"errors"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/path"
)
var ErrResolveFailed = errors.New("could not resolve name")

View File

@ -4,9 +4,8 @@ import (
"context"
"io"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/path"
"github.com/ipfs/go-cid"
ipld "github.com/ipfs/go-ipld-format"
@ -60,11 +59,11 @@ type ObjectChange struct {
// Before holds the link path before the change. Note that when a link is
// added, this will be nil.
Before path.Resolved
Before path.ImmutablePath
// After holds the link path after the change. Note that when a link is
// removed, this will be nil.
After path.Resolved
After path.ImmutablePath
}
// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities
@ -74,7 +73,7 @@ type ObjectAPI interface {
New(context.Context, ...options.ObjectNewOption) (ipld.Node, error)
// Put imports the data into merkledag
Put(context.Context, io.Reader, ...options.ObjectPutOption) (path.Resolved, error)
Put(context.Context, io.Reader, ...options.ObjectPutOption) (path.ImmutablePath, error)
// Get returns the node for the path
Get(context.Context, path.Path) (ipld.Node, error)
@ -91,16 +90,16 @@ type ObjectAPI interface {
// AddLink adds a link under the specified path. child path can point to a
// subdirectory within the patent which must be present (can be overridden
// with WithCreate option).
AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.Resolved, error)
AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.ImmutablePath, error)
// RmLink removes a link from the node
RmLink(ctx context.Context, base path.Path, link string) (path.Resolved, error)
RmLink(ctx context.Context, base path.Path, link string) (path.ImmutablePath, error)
// AppendData appends data to the node
AppendData(context.Context, path.Path, io.Reader) (path.Resolved, error)
AppendData(context.Context, path.Path, io.Reader) (path.ImmutablePath, error)
// SetData sets the data contained in the node
SetData(context.Context, path.Path, io.Reader) (path.Resolved, error)
SetData(context.Context, path.Path, io.Reader) (path.ImmutablePath, error)
// Diff returns a set of changes needed to transform the first object into the
// second.

View File

@ -1,199 +0,0 @@
package path
import (
"strings"
ipfspath "github.com/ipfs/boxo/path"
cid "github.com/ipfs/go-cid"
)
// Path is a generic wrapper for paths used in the API. A path can be resolved
// to a CID using one of Resolve functions in the API.
//
// Paths must be prefixed with a valid prefix:
//
// * /ipfs - Immutable unixfs path (files)
// * /ipld - Immutable ipld path (data)
// * /ipns - Mutable names. Usually resolves to one of the immutable paths
// TODO: /local (MFS)
type Path interface {
// String returns the path as a string.
String() string
// Namespace returns the first component of the path.
//
// For example path "/ipfs/QmHash", calling Namespace() will return "ipfs"
//
// Calling this method on invalid paths (IsValid() != nil) will result in
// empty string
Namespace() string
// Mutable returns false if the data pointed to by this path in guaranteed
// to not change.
//
// Note that resolved mutable path can be immutable.
Mutable() bool
// IsValid checks if this path is a valid ipfs Path, returning nil iff it is
// valid
IsValid() error
}
// Resolved is a path which was resolved to the last resolvable node.
// ResolvedPaths are guaranteed to return nil from `IsValid`
type Resolved interface {
// Cid returns the CID of the node referenced by the path. Remainder of the
// path is guaranteed to be within the node.
//
// Examples:
// If you have 3 linked objects: QmRoot -> A -> B:
//
// cidB := {"foo": {"bar": 42 }}
// cidA := {"B": {"/": cidB }}
// cidRoot := {"A": {"/": cidA }}
//
// And resolve paths:
//
// * "/ipfs/${cidRoot}"
// * Calling Cid() will return `cidRoot`
// * Calling Root() will return `cidRoot`
// * Calling Remainder() will return ``
//
// * "/ipfs/${cidRoot}/A"
// * Calling Cid() will return `cidA`
// * Calling Root() will return `cidRoot`
// * Calling Remainder() will return ``
//
// * "/ipfs/${cidRoot}/A/B/foo"
// * Calling Cid() will return `cidB`
// * Calling Root() will return `cidRoot`
// * Calling Remainder() will return `foo`
//
// * "/ipfs/${cidRoot}/A/B/foo/bar"
// * Calling Cid() will return `cidB`
// * Calling Root() will return `cidRoot`
// * Calling Remainder() will return `foo/bar`
Cid() cid.Cid
// Root returns the CID of the root object of the path
//
// Example:
// If you have 3 linked objects: QmRoot -> A -> B, and resolve path
// "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot
//
// For more examples see the documentation of Cid() method
Root() cid.Cid
// Remainder returns unresolved part of the path
//
// Example:
// If you have 2 linked objects: QmRoot -> A, where A is a CBOR node
// containing the following data:
//
// {"foo": {"bar": 42 }}
//
// When resolving "/ipld/QmRoot/A/foo/bar", Remainder will return "foo/bar"
//
// For more examples see the documentation of Cid() method
Remainder() string
Path
}
// path implements coreiface.Path
type path struct {
path string
}
// resolvedPath implements coreiface.resolvedPath
type resolvedPath struct {
path
cid cid.Cid
root cid.Cid
remainder string
}
// Join appends provided segments to the base path
func Join(base Path, a ...string) Path {
s := strings.Join(append([]string{base.String()}, a...), "/")
return &path{path: s}
}
// IpfsPath creates new /ipfs path from the provided CID
func IpfsPath(c cid.Cid) Resolved {
return &resolvedPath{
path: path{"/ipfs/" + c.String()},
cid: c,
root: c,
remainder: "",
}
}
// IpldPath creates new /ipld path from the provided CID
func IpldPath(c cid.Cid) Resolved {
return &resolvedPath{
path: path{"/ipld/" + c.String()},
cid: c,
root: c,
remainder: "",
}
}
// New parses string path to a Path
func New(p string) Path {
if pp, err := ipfspath.ParsePath(p); err == nil {
p = pp.String()
}
return &path{path: p}
}
// NewResolvedPath creates new Resolved path. This function performs no checks
// and is intended to be used by resolver implementations. Incorrect inputs may
// cause panics. Handle with care.
func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) Resolved {
return &resolvedPath{
path: path{ipath.String()},
cid: c,
root: root,
remainder: remainder,
}
}
func (p *path) String() string {
return p.path
}
func (p *path) Namespace() string {
ip, err := ipfspath.ParsePath(p.path)
if err != nil {
return ""
}
if len(ip.Segments()) < 1 {
panic("path without namespace") // this shouldn't happen under any scenario
}
return ip.Segments()[0]
}
func (p *path) Mutable() bool {
// TODO: MFS: check for /local
return p.Namespace() == "ipns"
}
func (p *path) IsValid() error {
_, err := ipfspath.ParsePath(p.path)
return err
}
func (p *resolvedPath) Cid() cid.Cid {
return p.cid
}
func (p *resolvedPath) Root() cid.Cid {
return p.root
}
func (p *resolvedPath) Remainder() string {
return p.remainder
}

View File

@ -3,7 +3,7 @@ package iface
import (
"context"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/path"
"github.com/ipfs/boxo/coreiface/options"
)
@ -11,7 +11,7 @@ import (
// Pin holds information about pinned resource
type Pin interface {
// Path to the pinned object
Path() path.Resolved
Path() path.ImmutablePath
// Type of the pin
Type() string
@ -35,7 +35,7 @@ type PinStatus interface {
// BadPinNode is a node that has been marked as bad by Pin.Verify
type BadPinNode interface {
// Path is the path of the node
Path() path.Resolved
Path() path.ImmutablePath
// Err is the reason why the node has been marked as bad
Err() error

View File

@ -9,9 +9,8 @@ import (
coreiface "github.com/ipfs/boxo/coreiface"
opt "github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/path"
ipld "github.com/ipfs/go-ipld-format"
mh "github.com/multiformats/go-multihash"
)
@ -68,8 +67,8 @@ func (tp *TestSuite) TestBlockPut(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != rawCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != rawCid {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -88,8 +87,8 @@ func (tp *TestSuite) TestBlockPutFormatDagCbor(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != cborCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != cborCid {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -108,8 +107,8 @@ func (tp *TestSuite) TestBlockPutFormatDagPb(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != pbCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != pbCid {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -128,8 +127,8 @@ func (tp *TestSuite) TestBlockPutFormatV0(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != pbCidV0 {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != pbCidV0 {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -146,8 +145,8 @@ func (tp *TestSuite) TestBlockPutCidCodecDagCbor(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != cborCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != cborCid {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -164,8 +163,8 @@ func (tp *TestSuite) TestBlockPutCidCodecDagPb(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != pbCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != pbCid {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -187,8 +186,8 @@ func (tp *TestSuite) TestBlockPutHash(t *testing.T) {
t.Fatal(err)
}
if res.Path().Cid().String() != cborKCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
if res.Path().RootCid().String() != cborKCid {
t.Errorf("got wrong cid: %s", res.Path().RootCid().String())
}
}
@ -219,13 +218,13 @@ func (tp *TestSuite) TestBlockGet(t *testing.T) {
t.Error("didn't get correct data back")
}
p := path.New("/ipfs/" + res.Path().Cid().String())
p := path.FromCid(res.Path().RootCid())
rp, err := api.ResolvePath(ctx, p)
rp, _, err := api.ResolvePath(ctx, p)
if err != nil {
t.Fatal(err)
}
if rp.Cid().String() != res.Path().Cid().String() {
if rp.RootCid().String() != res.Path().RootCid().String() {
t.Error("paths didn't match")
}
}

View File

@ -3,13 +3,11 @@ package tests
import (
"context"
"math"
gopath "path"
"strings"
"testing"
path "github.com/ipfs/boxo/coreiface/path"
coreiface "github.com/ipfs/boxo/coreiface"
"github.com/ipfs/boxo/path"
ipldcbor "github.com/ipfs/go-ipld-cbor"
ipld "github.com/ipfs/go-ipld-format"
@ -113,14 +111,17 @@ func (tp *TestSuite) TestDagPath(t *testing.T) {
t.Fatal(err)
}
p := path.New(gopath.Join(nd.Cid().String(), "lnk"))
rp, err := api.ResolvePath(ctx, p)
p, err := path.Join(path.FromCid(nd.Cid()), "lnk")
if err != nil {
t.Fatal(err)
}
ndd, err := api.Dag().Get(ctx, rp.Cid())
rp, _, err := api.ResolvePath(ctx, p)
if err != nil {
t.Fatal(err)
}
ndd, err := api.Dag().Get(ctx, rp.RootCid())
if err != nil {
t.Fatal(err)
}

View File

@ -4,15 +4,14 @@ import (
"context"
"io"
"math/rand"
gopath "path"
"testing"
"time"
coreiface "github.com/ipfs/boxo/coreiface"
opt "github.com/ipfs/boxo/coreiface/options"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/files"
"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/path"
"github.com/stretchr/testify/require"
)
@ -35,10 +34,6 @@ func addTestObject(ctx context.Context, api coreiface.CoreAPI) (path.Path, error
return api.Unixfs().Add(ctx, files.NewReaderFile(&io.LimitedReader{R: rnd, N: 4092}))
}
func appendPath(p path.Path, sub string) path.Path {
return path.New(gopath.Join(p.String(), sub))
}
func (tp *TestSuite) TestPublishResolve(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -68,7 +63,10 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) {
t.Run("publishPath", func(t *testing.T) {
api, p := init()
name, err := api.Name().Publish(ctx, appendPath(p, "/test"))
p, err := path.Join(p, "/test")
require.NoError(t, err)
name, err := api.Name().Publish(ctx, p)
require.NoError(t, err)
self, err := api.Key().Self(ctx)
@ -77,7 +75,7 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) {
resPath, err := api.Name().Resolve(ctx, name.String(), ropts...)
require.NoError(t, err)
require.Equal(t, p.String()+"/test", resPath.String())
require.Equal(t, p.String(), resPath.String())
})
t.Run("revolvePath", func(t *testing.T) {
@ -96,7 +94,10 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) {
t.Run("publishRevolvePath", func(t *testing.T) {
api, p := init()
name, err := api.Name().Publish(ctx, appendPath(p, "/a"))
p, err := path.Join(p, "/a")
require.NoError(t, err)
name, err := api.Name().Publish(ctx, p)
require.NoError(t, err)
self, err := api.Key().Self(ctx)
@ -105,7 +106,7 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) {
resPath, err := api.Name().Resolve(ctx, name.String()+"/b", ropts...)
require.NoError(t, err)
require.Equal(t, p.String()+"/a/b", resPath.String())
require.Equal(t, p.String()+"/b", resPath.String())
})
}

View File

@ -166,7 +166,7 @@ func (tp *TestSuite) TestObjectLinks(t *testing.T) {
t.Fatal(err)
}
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`"}]}`))
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`"}]}`))
if err != nil {
t.Fatal(err)
}
@ -180,7 +180,7 @@ func (tp *TestSuite) TestObjectLinks(t *testing.T) {
t.Errorf("unexpected number of links: %d", len(links))
}
if links[0].Cid.String() != p1.Cid().String() {
if links[0].Cid.String() != p1.RootCid().String() {
t.Fatal("cids didn't batch")
}
@ -202,7 +202,7 @@ func (tp *TestSuite) TestObjectStat(t *testing.T) {
t.Fatal(err)
}
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`))
if err != nil {
t.Fatal(err)
}
@ -212,7 +212,7 @@ func (tp *TestSuite) TestObjectStat(t *testing.T) {
t.Fatal(err)
}
if stat.Cid.String() != p2.Cid().String() {
if stat.Cid.String() != p2.RootCid().String() {
t.Error("unexpected stat.Cid")
}
@ -250,7 +250,7 @@ func (tp *TestSuite) TestObjectAddLink(t *testing.T) {
t.Fatal(err)
}
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`))
if err != nil {
t.Fatal(err)
}
@ -291,7 +291,7 @@ func (tp *TestSuite) TestObjectAddLinkCreate(t *testing.T) {
t.Fatal(err)
}
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`))
if err != nil {
t.Fatal(err)
}
@ -340,7 +340,7 @@ func (tp *TestSuite) TestObjectRmLink(t *testing.T) {
t.Fatal(err)
}
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`))
if err != nil {
t.Fatal(err)
}

View File

@ -2,17 +2,26 @@ package tests
import (
"context"
"fmt"
"math"
"strings"
"testing"
"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/path"
"github.com/ipfs/go-cid"
ipldcbor "github.com/ipfs/go-ipld-cbor"
"github.com/stretchr/testify/require"
)
func newIPLDPath(t *testing.T, cid cid.Cid) path.ImmutablePath {
p, err := path.NewPath(fmt.Sprintf("/%s/%s", path.IPLDNamespace, cid.String()))
require.NoError(t, err)
im, err := path.NewImmutablePath(p)
require.NoError(t, err)
return im
}
func (tp *TestSuite) TestPath(t *testing.T) {
t.Run("TestMutablePath", tp.TestMutablePath)
t.Run("TestPathRemainder", tp.TestPathRemainder)
@ -25,173 +34,115 @@ func (tp *TestSuite) TestPath(t *testing.T) {
func (tp *TestSuite) TestMutablePath(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(t, ctx)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
blk, err := api.Block().Put(ctx, strings.NewReader(`foo`))
if err != nil {
t.Fatal(err)
}
if blk.Path().Mutable() {
t.Error("expected /ipld path to be immutable")
}
// get self /ipns path
if api.Key() == nil {
t.Fatal(".Key not implemented")
}
require.NoError(t, err)
require.False(t, blk.Path().Mutable())
require.NotNil(t, api.Key())
keys, err := api.Key().List(ctx)
if err != nil {
t.Fatal(err)
}
if !keys[0].Path().Mutable() {
t.Error("expected self /ipns path to be mutable")
}
require.NoError(t, err)
require.True(t, keys[0].Path().Mutable())
}
func (tp *TestSuite) TestPathRemainder(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(t, ctx)
if err != nil {
t.Fatal(err)
}
if api.Dag() == nil {
t.Fatal(".Dag not implemented")
}
api, err := tp.makeAPI(t, ctx)
require.NoError(t, err)
require.NotNil(t, api.Dag())
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if err := api.Dag().Add(ctx, nd); err != nil {
t.Fatal(err)
}
err = api.Dag().Add(ctx, nd)
require.NoError(t, err)
rp1, err := api.ResolvePath(ctx, path.New(nd.String()+"/foo/bar"))
if err != nil {
t.Fatal(err)
}
p, err := path.Join(path.FromCid(nd.Cid()), "foo", "bar")
require.NoError(t, err)
if rp1.Remainder() != "foo/bar" {
t.Error("expected to get path remainder")
}
_, remainder, err := api.ResolvePath(ctx, p)
require.NoError(t, err)
require.Equal(t, "/foo/bar", path.SegmentsToString(remainder...))
}
func (tp *TestSuite) TestEmptyPathRemainder(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(t, ctx)
if err != nil {
t.Fatal(err)
}
if api.Dag() == nil {
t.Fatal(".Dag not implemented")
}
api, err := tp.makeAPI(t, ctx)
require.NoError(t, err)
require.NotNil(t, api.Dag())
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if err := api.Dag().Add(ctx, nd); err != nil {
t.Fatal(err)
}
err = api.Dag().Add(ctx, nd)
require.NoError(t, err)
rp1, err := api.ResolvePath(ctx, path.New(nd.Cid().String()))
if err != nil {
t.Fatal(err)
}
if rp1.Remainder() != "" {
t.Error("expected the resolved path to not have a remainder")
}
_, remainder, err := api.ResolvePath(ctx, path.FromCid(nd.Cid()))
require.NoError(t, err)
require.Empty(t, remainder)
}
func (tp *TestSuite) TestInvalidPathRemainder(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(t, ctx)
if err != nil {
t.Fatal(err)
}
if api.Dag() == nil {
t.Fatal(".Dag not implemented")
}
api, err := tp.makeAPI(t, ctx)
require.NoError(t, err)
require.NotNil(t, api.Dag())
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if err := api.Dag().Add(ctx, nd); err != nil {
t.Fatal(err)
}
err = api.Dag().Add(ctx, nd)
require.NoError(t, err)
_, err = api.ResolvePath(ctx, path.New("/ipld/"+nd.Cid().String()+"/bar/baz"))
if err == nil || !strings.Contains(err.Error(), `no link named "bar"`) {
t.Fatalf("unexpected error: %s", err)
}
p, err := path.Join(newIPLDPath(t, nd.Cid()), "/bar/baz")
require.NoError(t, err)
_, _, err = api.ResolvePath(ctx, p)
require.NotNil(t, err)
require.ErrorContains(t, err, `no link named "bar"`)
}
func (tp *TestSuite) TestPathRoot(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(t, ctx)
if err != nil {
t.Fatal(err)
}
if api.Block() == nil {
t.Fatal(".Block not implemented")
}
api, err := tp.makeAPI(t, ctx)
require.NoError(t, err)
require.NotNil(t, api.Block())
blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw"))
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
require.NotNil(t, api.Dag())
if api.Dag() == nil {
t.Fatal(".Dag not implemented")
}
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().RootCid().String()+`"}}`), math.MaxUint64, -1)
require.NoError(t, err)
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
err = api.Dag().Add(ctx, nd)
require.NoError(t, err)
if err := api.Dag().Add(ctx, nd); err != nil {
t.Fatal(err)
}
p, err := path.Join(newIPLDPath(t, nd.Cid()), "/foo")
require.NoError(t, err)
rp, err := api.ResolvePath(ctx, path.New("/ipld/"+nd.Cid().String()+"/foo"))
if err != nil {
t.Fatal(err)
}
if rp.Root().String() != nd.Cid().String() {
t.Error("unexpected path root")
}
if rp.Cid().String() != blk.Path().Cid().String() {
t.Error("unexpected path cid")
}
rp, _, err := api.ResolvePath(ctx, p)
require.NoError(t, err)
require.Equal(t, rp.RootCid().String(), blk.Path().RootCid().String())
}
func (tp *TestSuite) TestPathJoin(t *testing.T) {
p1 := path.New("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz")
p1, err := path.NewPath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz")
require.NoError(t, err)
if path.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" {
t.Error("unexpected path")
}
p2, err := path.Join(p1, "foo")
require.NoError(t, err)
require.Equal(t, "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo", p2.String())
}

View File

@ -8,8 +8,7 @@ import (
iface "github.com/ipfs/boxo/coreiface"
opt "github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/path"
"github.com/ipfs/go-cid"
ipldcbor "github.com/ipfs/go-ipld-cbor"
ipld "github.com/ipfs/go-ipld-format"
@ -77,7 +76,7 @@ func (tp *TestSuite) TestPinSimple(t *testing.T) {
t.Errorf("unexpected pin list len: %d", len(list))
}
if list[0].Path().Cid().String() != p.Cid().String() {
if list[0].Path().RootCid().String() != p.RootCid().String() {
t.Error("paths don't match")
}
@ -120,12 +119,12 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
t.Fatal(err)
}
nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`), math.MaxUint64, -1)
nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.RootCid().String()+`"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`), math.MaxUint64, -1)
nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.RootCid().String()+`"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
@ -134,12 +133,12 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(nd2.Cid()))
err = api.Pin().Add(ctx, path.FromCid(nd2.Cid()))
if err != nil {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(nd3.Cid()), opt.Pin.Recursive(false))
err = api.Pin().Add(ctx, path.FromCid(nd3.Cid()), opt.Pin.Recursive(false))
if err != nil {
t.Fatal(err)
}
@ -162,8 +161,8 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
t.Errorf("unexpected pin list len: %d", len(list))
}
if list[0].Path().String() != path.IpldPath(nd3.Cid()).String() {
t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpfsPath(nd3.Cid()).String())
if list[0].Path().String() != path.FromCid(nd3.Cid()).String() {
t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd3.Cid()).String())
}
list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive()))
@ -175,8 +174,8 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
t.Errorf("unexpected pin list len: %d", len(list))
}
if list[0].Path().String() != path.IpldPath(nd2.Cid()).String() {
t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpldPath(nd2.Cid()).String())
if list[0].Path().String() != path.FromCid(nd2.Cid()).String() {
t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd2.Cid()).String())
}
list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect()))
@ -188,8 +187,8 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
t.Errorf("unexpected pin list len: %d", len(list))
}
if list[0].Path().Cid().String() != p0.Cid().String() {
t.Errorf("unexpected path, %s != %s", list[0].Path().Cid().String(), p0.Cid().String())
if list[0].Path().RootCid().String() != p0.RootCid().String() {
t.Errorf("unexpected path, %s != %s", list[0].Path().RootCid().String(), p0.RootCid().String())
}
res, err := api.Pin().Verify(ctx)
@ -259,12 +258,12 @@ func (tp *TestSuite) TestPinLsIndirect(t *testing.T) {
leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foo")
err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()))
if err != nil {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false))
err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false))
if err != nil {
t.Fatal(err)
}
@ -293,12 +292,12 @@ func (tp *TestSuite) TestPinLsPredenceRecursiveIndirect(t *testing.T) {
// Test recursive > indirect
leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive > indirect")
err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()))
if err != nil {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()))
err = api.Pin().Add(ctx, path.FromCid(parent.Cid()))
if err != nil {
t.Fatal(err)
}
@ -317,12 +316,12 @@ func (tp *TestSuite) TestPinLsPrecedenceDirectIndirect(t *testing.T) {
// Test direct > indirect
leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "direct > indirect")
err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()))
if err != nil {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false))
err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false))
if err != nil {
t.Fatal(err)
}
@ -341,24 +340,24 @@ func (tp *TestSuite) TestPinLsPrecedenceRecursiveDirect(t *testing.T) {
// Test recursive > direct
leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive + direct = error")
err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()))
err = api.Pin().Add(ctx, path.FromCid(parent.Cid()))
if err != nil {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false))
err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false))
if err == nil {
t.Fatal("expected error directly pinning a recursively pinned node")
}
assertPinTypes(t, ctx, api, []cidContainer{parent}, []cidContainer{}, []cidContainer{leaf})
err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()), opt.Pin.Recursive(false))
err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()), opt.Pin.Recursive(false))
if err != nil {
t.Fatal(err)
}
err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()))
if err != nil {
t.Fatal(err)
}
@ -376,40 +375,48 @@ func (tp *TestSuite) TestPinIsPinned(t *testing.T) {
leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foofoo")
assertNotPinned(t, ctx, api, path.IpldPath(grandparent.Cid()))
assertNotPinned(t, ctx, api, path.IpldPath(parent.Cid()))
assertNotPinned(t, ctx, api, path.IpldPath(leaf.Cid()))
assertNotPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid()))
assertNotPinned(t, ctx, api, newIPLDPath(t, parent.Cid()))
assertNotPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()))
err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(true))
err = api.Pin().Add(ctx, newIPLDPath(t, parent.Cid()), opt.Pin.Recursive(true))
if err != nil {
t.Fatal(err)
}
assertNotPinned(t, ctx, api, path.IpldPath(grandparent.Cid()))
assertIsPinned(t, ctx, api, path.IpldPath(parent.Cid()), "recursive")
assertIsPinned(t, ctx, api, path.IpldPath(leaf.Cid()), "indirect")
assertNotPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid()))
assertIsPinned(t, ctx, api, newIPLDPath(t, parent.Cid()), "recursive")
assertIsPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()), "indirect")
err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()), opt.Pin.Recursive(false))
err = api.Pin().Add(ctx, newIPLDPath(t, grandparent.Cid()), opt.Pin.Recursive(false))
if err != nil {
t.Fatal(err)
}
assertIsPinned(t, ctx, api, path.IpldPath(grandparent.Cid()), "direct")
assertIsPinned(t, ctx, api, path.IpldPath(parent.Cid()), "recursive")
assertIsPinned(t, ctx, api, path.IpldPath(leaf.Cid()), "indirect")
assertIsPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid()), "direct")
assertIsPinned(t, ctx, api, newIPLDPath(t, parent.Cid()), "recursive")
assertIsPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()), "indirect")
}
type cidContainer interface {
Cid() cid.Cid
}
type immutablePathCidContainer struct {
path.ImmutablePath
}
func (i immutablePathCidContainer) Cid() cid.Cid {
return i.RootCid()
}
func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, leafData string) (cidContainer, cidContainer, cidContainer) {
leaf, err := api.Unixfs().Add(ctx, strFile(leafData)())
if err != nil {
t.Fatal(err)
}
parent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+leaf.Cid().String()+`"}}`), math.MaxUint64, -1)
parent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+leaf.RootCid().String()+`"}}`), math.MaxUint64, -1)
if err != nil {
t.Fatal(err)
}
@ -423,7 +430,7 @@ func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI,
t.Fatal(err)
}
return leaf, parent, grandparent
return immutablePathCidContainer{leaf}, parent, grandparent
}
func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) {
@ -466,7 +473,7 @@ func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) {
valid := true
for _, p := range pins {
c := p.Path().Cid()
c := p.Path().RootCid()
if cSet.Has(c) {
cSet.Remove(c)
} else {
@ -480,7 +487,7 @@ func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) {
if !valid {
pinStrs := make([]string, len(pins))
for i, p := range pins {
pinStrs[i] = p.Path().Cid().String()
pinStrs[i] = p.Path().RootCid().String()
}
pathStrs := make([]string, len(cids))
for i, c := range cids {
@ -511,13 +518,13 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core
}
for _, p := range allPins {
if !all.Visit(p.Path().Cid()) {
if !all.Visit(p.Path().RootCid()) {
t.Fatalf("pin ls returned the same cid multiple times")
}
typeStr := p.Type()
if typeSet, ok := typeMap[p.Type()]; ok {
typeSet.Add(p.Path().Cid())
typeSet.Add(p.Path().RootCid())
} else {
t.Fatalf("unknown pin type: %s", typeStr)
}
@ -538,7 +545,7 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core
t.Fatalf("returned wrong pin type: expected %s, got %s", typeStr, pinType)
}
if c := p.Path().Cid(); !pinProps.Has(c) {
if c := p.Path().RootCid(); !pinProps.Has(c) {
t.Fatalf("%s expected to be in pin ls all as type %s", c.String(), typeStr)
}
}

View File

@ -7,8 +7,8 @@ import (
iface "github.com/ipfs/boxo/coreiface"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/path"
"github.com/stretchr/testify/require"
)

View File

@ -14,10 +14,9 @@ import (
"sync"
"testing"
"github.com/ipfs/boxo/coreiface/path"
coreiface "github.com/ipfs/boxo/coreiface"
"github.com/ipfs/boxo/coreiface/options"
"github.com/ipfs/boxo/path"
"github.com/ipfs/boxo/files"
mdag "github.com/ipfs/boxo/ipld/merkledag"
@ -106,12 +105,12 @@ func (tp *TestSuite) TestAdd(t *testing.T) {
t.Fatal(err)
}
p := func(h string) path.Resolved {
p := func(h string) path.ImmutablePath {
c, err := cid.Parse(h)
if err != nil {
t.Fatal(err)
}
return path.IpfsPath(c)
return path.FromCid(c)
}
rf, err := os.CreateTemp(os.TempDir(), "unixfs-add-real")
@ -410,7 +409,7 @@ func (tp *TestSuite) TestAdd(t *testing.T) {
}
if expected[0].Path != nil && event.Path != nil {
if expected[0].Path.Cid().String() != event.Path.Cid().String() {
if expected[0].Path.RootCid().String() != event.Path.RootCid().String() {
t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path)
}
} else if event.Path != expected[0].Path {
@ -553,7 +552,7 @@ func (tp *TestSuite) TestAddPinned(t *testing.T) {
t.Fatalf("expected 1 pin, got %d", len(pins))
}
if pins[0].Path().String() != "/ipld/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" {
if pins[0].Path().String() != "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" {
t.Fatalf("got unexpected pin: %s", pins[0].Path().String())
}
}
@ -597,7 +596,10 @@ func (tp *TestSuite) TestGetEmptyFile(t *testing.T) {
t.Fatal(err)
}
emptyFilePath := path.New(emptyFile)
emptyFilePath, err := path.NewPath(emptyFile)
if err != nil {
t.Fatal(err)
}
r, err := api.Unixfs().Get(ctx, emptyFilePath)
if err != nil {
@ -626,18 +628,18 @@ func (tp *TestSuite) TestGetDir(t *testing.T) {
if err != nil {
t.Fatal(err)
}
p := path.IpfsPath(edir.Cid())
p := path.FromCid(edir.Cid())
emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir"))
if err != nil {
t.Fatal(err)
}
if p.String() != path.IpfsPath(emptyDir.Cid()).String() {
if p.String() != path.FromCid(emptyDir.Cid()).String() {
t.Fatalf("expected path %s, got: %s", emptyDir.Cid(), p.String())
}
r, err := api.Unixfs().Get(ctx, path.IpfsPath(emptyDir.Cid()))
r, err := api.Unixfs().Get(ctx, path.FromCid(emptyDir.Cid()))
if err != nil {
t.Fatal(err)
}
@ -661,7 +663,7 @@ func (tp *TestSuite) TestGetNonUnixfs(t *testing.T) {
t.Fatal(err)
}
_, err = api.Unixfs().Get(ctx, path.IpfsPath(nd.Cid()))
_, err = api.Unixfs().Get(ctx, path.FromCid(nd.Cid()))
if !strings.Contains(err.Error(), "proto: required field") {
t.Fatalf("expected protobuf error, got: %s", err)
}
@ -787,7 +789,7 @@ func (tp *TestSuite) TestLsEmptyDir(t *testing.T) {
t.Fatal(err)
}
links, err := api.Unixfs().Ls(ctx, path.IpfsPath(emptyDir.Cid()))
links, err := api.Unixfs().Ls(ctx, path.FromCid(emptyDir.Cid()))
if err != nil {
t.Fatal(err)
}
@ -816,7 +818,7 @@ func (tp *TestSuite) TestLsNonUnixfs(t *testing.T) {
t.Fatal(err)
}
links, err := api.Unixfs().Ls(ctx, path.IpfsPath(nd.Cid()))
links, err := api.Unixfs().Ls(ctx, path.FromCid(nd.Cid()))
if err != nil {
t.Fatal(err)
}

View File

@ -4,17 +4,16 @@ import (
"context"
"github.com/ipfs/boxo/coreiface/options"
path "github.com/ipfs/boxo/coreiface/path"
"github.com/ipfs/boxo/files"
"github.com/ipfs/boxo/path"
"github.com/ipfs/go-cid"
)
type AddEvent struct {
Name string
Path path.Resolved `json:",omitempty"`
Bytes int64 `json:",omitempty"`
Size string `json:",omitempty"`
Path path.ImmutablePath `json:",omitempty"`
Bytes int64 `json:",omitempty"`
Size string `json:",omitempty"`
}
// FileType is an enum of possible UnixFS file types.
@ -66,7 +65,7 @@ type UnixfsAPI interface {
// Add imports the data from the reader into merkledag file
//
// TODO: a long useful comment on how to use this for many different scenarios
Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.Resolved, error)
Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.ImmutablePath, error)
// Get returns a read-only handle to a file tree referenced by a path
//