mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-28 13:57:52 +08:00
coreapi: extract interface
License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
This commit is contained in:
parent
6a5a268514
commit
65579081d9
@ -1,36 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
// BlockStat contains information about a block
|
||||
type BlockStat interface {
|
||||
// Size is the size of a block
|
||||
Size() int
|
||||
|
||||
// Path returns path to the block
|
||||
Path() ResolvedPath
|
||||
}
|
||||
|
||||
// BlockAPI specifies the interface to the block layer
|
||||
type BlockAPI interface {
|
||||
// Put imports raw block data, hashing it using specified settings.
|
||||
Put(context.Context, io.Reader, ...options.BlockPutOption) (BlockStat, error)
|
||||
|
||||
// Get attempts to resolve the path and return a reader for data in the block
|
||||
Get(context.Context, Path) (io.Reader, error)
|
||||
|
||||
// Rm removes the block specified by the path from local blockstore.
|
||||
// By default an error will be returned if the block can't be found locally.
|
||||
//
|
||||
// NOTE: If the specified block is pinned it won't be removed and no error
|
||||
// will be returned
|
||||
Rm(context.Context, Path, ...options.BlockRmOption) error
|
||||
|
||||
// Stat returns information on
|
||||
Stat(context.Context, Path) (BlockStat, error)
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
// Package iface defines IPFS Core API which is a set of interfaces used to
|
||||
// interact with IPFS nodes.
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
|
||||
)
|
||||
|
||||
// CoreAPI defines an unified interface to IPFS for Go programs
|
||||
type CoreAPI interface {
|
||||
// Unixfs returns an implementation of Unixfs API
|
||||
Unixfs() UnixfsAPI
|
||||
|
||||
// Block returns an implementation of Block API
|
||||
Block() BlockAPI
|
||||
|
||||
// Dag returns an implementation of Dag API
|
||||
Dag() APIDagService
|
||||
|
||||
// Name returns an implementation of Name API
|
||||
Name() NameAPI
|
||||
|
||||
// Key returns an implementation of Key API
|
||||
Key() KeyAPI
|
||||
|
||||
// Pin returns an implementation of Pin API
|
||||
Pin() PinAPI
|
||||
|
||||
// ObjectAPI returns an implementation of Object API
|
||||
Object() ObjectAPI
|
||||
|
||||
// Dht returns an implementation of Dht API
|
||||
Dht() DhtAPI
|
||||
|
||||
// Swarm returns an implementation of Swarm API
|
||||
Swarm() SwarmAPI
|
||||
|
||||
// PubSub returns an implementation of PubSub API
|
||||
PubSub() PubSubAPI
|
||||
|
||||
// ResolvePath resolves the path using Unixfs resolver
|
||||
ResolvePath(context.Context, Path) (ResolvedPath, error)
|
||||
|
||||
// ResolveNode resolves the path (if not resolved already) using Unixfs
|
||||
// resolver, gets and returns the resolved Node
|
||||
ResolveNode(context.Context, Path) (ipld.Node, error)
|
||||
|
||||
// WithOptions creates new instance of CoreAPI based on this instance with
|
||||
// a set of options applied
|
||||
WithOptions(...options.ApiOption) (CoreAPI, error)
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
|
||||
)
|
||||
|
||||
// APIDagService extends ipld.DAGService
|
||||
type APIDagService interface {
|
||||
ipld.DAGService
|
||||
|
||||
// Pinning returns special NodeAdder which recursively pins added nodes
|
||||
Pinning() ipld.NodeAdder
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer"
|
||||
pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore"
|
||||
)
|
||||
|
||||
// DhtAPI specifies the interface to the DHT
|
||||
// Note: This API will likely get deprecated in near future, see
|
||||
// https://github.com/ipfs/interface-ipfs-core/issues/249 for more context.
|
||||
type DhtAPI interface {
|
||||
// FindPeer queries the DHT for all of the multiaddresses associated with a
|
||||
// Peer ID
|
||||
FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error)
|
||||
|
||||
// FindProviders finds peers in the DHT who can provide a specific value
|
||||
// given a key.
|
||||
FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error)
|
||||
|
||||
// Provide announces to the network that you are providing given values
|
||||
Provide(context.Context, Path, ...options.DhtProvideOption) error
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package iface
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrIsDir = errors.New("this dag node is a directory")
|
||||
ErrNotFile = errors.New("this dag node is not a regular file")
|
||||
ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first")
|
||||
)
|
||||
@ -1,41 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
"gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer"
|
||||
)
|
||||
|
||||
// Key specifies the interface to Keys in KeyAPI Keystore
|
||||
type Key interface {
|
||||
// Key returns key name
|
||||
Name() string
|
||||
|
||||
// Path returns key path
|
||||
Path() Path
|
||||
|
||||
// ID returns key PeerID
|
||||
ID() peer.ID
|
||||
}
|
||||
|
||||
// KeyAPI specifies the interface to Keystore
|
||||
type KeyAPI interface {
|
||||
// Generate generates new key, stores it in the keystore under the specified
|
||||
// name and returns a base58 encoded multihash of it's public key
|
||||
Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error)
|
||||
|
||||
// Rename renames oldName key to newName. Returns the key and whether another
|
||||
// key was overwritten, or an error
|
||||
Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error)
|
||||
|
||||
// List lists keys stored in keystore
|
||||
List(ctx context.Context) ([]Key, error)
|
||||
|
||||
// Self returns the 'main' node key
|
||||
Self(ctx context.Context) (Key, error)
|
||||
|
||||
// Remove removes keys from keystore. Returns ipns path of the removed key
|
||||
Remove(ctx context.Context, name string) (Key, error)
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
var ErrResolveFailed = errors.New("could not resolve name")
|
||||
|
||||
// IpnsEntry specifies the interface to IpnsEntries
|
||||
type IpnsEntry interface {
|
||||
// Name returns IpnsEntry name
|
||||
Name() string
|
||||
// Value returns IpnsEntry value
|
||||
Value() Path
|
||||
}
|
||||
|
||||
type IpnsResult struct {
|
||||
Path
|
||||
Err error
|
||||
}
|
||||
|
||||
// NameAPI specifies the interface to IPNS.
|
||||
//
|
||||
// IPNS is a PKI namespace, where names are the hashes of public keys, and the
|
||||
// private key enables publishing new (signed) values. In both publish and
|
||||
// resolve, the default name used is the node's own PeerID, which is the hash of
|
||||
// its public key.
|
||||
//
|
||||
// You can use .Key API to list and generate more names and their respective keys.
|
||||
type NameAPI interface {
|
||||
// Publish announces new IPNS name
|
||||
Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (IpnsEntry, error)
|
||||
|
||||
// Resolve attempts to resolve the newest version of the specified name
|
||||
Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error)
|
||||
|
||||
// Search is a version of Resolve which outputs paths as they are discovered,
|
||||
// reducing the time to first entry
|
||||
//
|
||||
// Note: by default, all paths read from the channel are considered unsafe,
|
||||
// except the latest (last path in channel read buffer).
|
||||
Search(ctx context.Context, name string, opts ...options.NameResolveOption) (<-chan IpnsResult, error)
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
|
||||
ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
|
||||
)
|
||||
|
||||
// ObjectStat provides information about dag nodes
|
||||
type ObjectStat struct {
|
||||
// Cid is the CID of the node
|
||||
Cid cid.Cid
|
||||
|
||||
// NumLinks is number of links the node contains
|
||||
NumLinks int
|
||||
|
||||
// BlockSize is size of the raw serialized node
|
||||
BlockSize int
|
||||
|
||||
// LinksSize is size of the links block section
|
||||
LinksSize int
|
||||
|
||||
// DataSize is the size of data block section
|
||||
DataSize int
|
||||
|
||||
// CumulativeSize is size of the tree (BlockSize + link sizes)
|
||||
CumulativeSize int
|
||||
}
|
||||
|
||||
// ChangeType denotes type of change in ObjectChange
|
||||
type ChangeType int
|
||||
|
||||
const (
|
||||
// DiffAdd is set when a link was added to the graph
|
||||
DiffAdd ChangeType = iota
|
||||
|
||||
// DiffRemove is set when a link was removed from the graph
|
||||
DiffRemove
|
||||
|
||||
// DiffMod is set when a link was changed in the graph
|
||||
DiffMod
|
||||
)
|
||||
|
||||
// ObjectChange represents a change ia a graph
|
||||
type ObjectChange struct {
|
||||
// Type of the change, either:
|
||||
// * DiffAdd - Added a link
|
||||
// * DiffRemove - Removed a link
|
||||
// * DiffMod - Modified a link
|
||||
Type ChangeType
|
||||
|
||||
// Path to the changed link
|
||||
Path string
|
||||
|
||||
// Before holds the link path before the change. Note that when a link is
|
||||
// added, this will be nil.
|
||||
Before ResolvedPath
|
||||
|
||||
// After holds the link path after the change. Note that when a link is
|
||||
// removed, this will be nil.
|
||||
After ResolvedPath
|
||||
}
|
||||
|
||||
// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities
|
||||
// for manipulating MerkleDAG data structures.
|
||||
type ObjectAPI interface {
|
||||
// New creates new, empty (by default) dag-node.
|
||||
New(context.Context, ...options.ObjectNewOption) (ipld.Node, error)
|
||||
|
||||
// Put imports the data into merkledag
|
||||
Put(context.Context, io.Reader, ...options.ObjectPutOption) (ResolvedPath, error)
|
||||
|
||||
// Get returns the node for the path
|
||||
Get(context.Context, Path) (ipld.Node, error)
|
||||
|
||||
// Data returns reader for data of the node
|
||||
Data(context.Context, Path) (io.Reader, error)
|
||||
|
||||
// Links returns lint or links the node contains
|
||||
Links(context.Context, Path) ([]*ipld.Link, error)
|
||||
|
||||
// Stat returns information about the node
|
||||
Stat(context.Context, Path) (*ObjectStat, error)
|
||||
|
||||
// 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, name string, child Path, opts ...options.ObjectAddLinkOption) (ResolvedPath, error)
|
||||
|
||||
// RmLink removes a link from the node
|
||||
RmLink(ctx context.Context, base Path, link string) (ResolvedPath, error)
|
||||
|
||||
// AppendData appends data to the node
|
||||
AppendData(context.Context, Path, io.Reader) (ResolvedPath, error)
|
||||
|
||||
// SetData sets the data contained in the node
|
||||
SetData(context.Context, Path, io.Reader) (ResolvedPath, error)
|
||||
|
||||
// Diff returns a set of changes needed to transform the first object into the
|
||||
// second.
|
||||
Diff(context.Context, Path, Path) ([]ObjectChange, error)
|
||||
}
|
||||
@ -1,126 +0,0 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
|
||||
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
|
||||
)
|
||||
|
||||
type BlockPutSettings struct {
|
||||
Codec string
|
||||
MhType uint64
|
||||
MhLength int
|
||||
Pin bool
|
||||
}
|
||||
|
||||
type BlockRmSettings struct {
|
||||
Force bool
|
||||
}
|
||||
|
||||
type BlockPutOption func(*BlockPutSettings) error
|
||||
type BlockRmOption func(*BlockRmSettings) error
|
||||
|
||||
func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, cid.Prefix, error) {
|
||||
options := &BlockPutSettings{
|
||||
Codec: "",
|
||||
MhType: mh.SHA2_256,
|
||||
MhLength: -1,
|
||||
Pin: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, cid.Prefix{}, err
|
||||
}
|
||||
}
|
||||
|
||||
var pref cid.Prefix
|
||||
pref.Version = 1
|
||||
|
||||
if options.Codec == "" {
|
||||
if options.MhType != mh.SHA2_256 || (options.MhLength != -1 && options.MhLength != 32) {
|
||||
options.Codec = "protobuf"
|
||||
} else {
|
||||
options.Codec = "v0"
|
||||
}
|
||||
}
|
||||
|
||||
if options.Codec == "v0" && options.MhType == mh.SHA2_256 {
|
||||
pref.Version = 0
|
||||
}
|
||||
|
||||
formatval, ok := cid.Codecs[options.Codec]
|
||||
if !ok {
|
||||
return nil, cid.Prefix{}, fmt.Errorf("unrecognized format: %s", options.Codec)
|
||||
}
|
||||
|
||||
if options.Codec == "v0" {
|
||||
if options.MhType != mh.SHA2_256 || (options.MhLength != -1 && options.MhLength != 32) {
|
||||
return nil, cid.Prefix{}, fmt.Errorf("only sha2-255-32 is allowed with CIDv0")
|
||||
}
|
||||
}
|
||||
|
||||
pref.Codec = formatval
|
||||
|
||||
pref.MhType = options.MhType
|
||||
pref.MhLength = options.MhLength
|
||||
|
||||
return options, pref, nil
|
||||
}
|
||||
|
||||
func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) {
|
||||
options := &BlockRmSettings{
|
||||
Force: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type blockOpts struct{}
|
||||
|
||||
var Block blockOpts
|
||||
|
||||
// Format is an option for Block.Put which specifies the multicodec to use to
|
||||
// serialize the object. Default is "v0"
|
||||
func (blockOpts) Format(codec string) BlockPutOption {
|
||||
return func(settings *BlockPutSettings) error {
|
||||
settings.Codec = codec
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Hash is an option for Block.Put which specifies the multihash settings to use
|
||||
// when hashing the object. Default is mh.SHA2_256 (0x12).
|
||||
// If mhLen is set to -1, default length for the hash will be used
|
||||
func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption {
|
||||
return func(settings *BlockPutSettings) error {
|
||||
settings.MhType = mhType
|
||||
settings.MhLength = mhLen
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Pin is an option for Block.Put which specifies whether to (recursively) pin
|
||||
// added blocks
|
||||
func (blockOpts) Pin(pin bool) BlockPutOption {
|
||||
return func(settings *BlockPutSettings) error {
|
||||
settings.Pin = pin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Force is an option for Block.Rm which, when set to true, will ignore
|
||||
// non-existing blocks
|
||||
func (blockOpts) Force(force bool) BlockRmOption {
|
||||
return func(settings *BlockRmSettings) error {
|
||||
settings.Force = force
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package options
|
||||
|
||||
type DhtProvideSettings struct {
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
type DhtFindProvidersSettings struct {
|
||||
NumProviders int
|
||||
}
|
||||
|
||||
type DhtProvideOption func(*DhtProvideSettings) error
|
||||
type DhtFindProvidersOption func(*DhtFindProvidersSettings) error
|
||||
|
||||
func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) {
|
||||
options := &DhtProvideSettings{
|
||||
Recursive: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func DhtFindProvidersOptions(opts ...DhtFindProvidersOption) (*DhtFindProvidersSettings, error) {
|
||||
options := &DhtFindProvidersSettings{
|
||||
NumProviders: 20,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type dhtOpts struct{}
|
||||
|
||||
var Dht dhtOpts
|
||||
|
||||
// Recursive is an option for Dht.Provide which specifies whether to provide
|
||||
// the given path recursively
|
||||
func (dhtOpts) Recursive(recursive bool) DhtProvideOption {
|
||||
return func(settings *DhtProvideSettings) error {
|
||||
settings.Recursive = recursive
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NumProviders is an option for Dht.FindProviders which specifies the
|
||||
// number of peers to look for. Default is 20
|
||||
func (dhtOpts) NumProviders(numProviders int) DhtFindProvidersOption {
|
||||
return func(settings *DhtFindProvidersSettings) error {
|
||||
settings.NumProviders = numProviders
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package options
|
||||
|
||||
type ApiSettings struct {
|
||||
Offline bool
|
||||
FetchBlocks bool
|
||||
}
|
||||
|
||||
type ApiOption func(*ApiSettings) error
|
||||
|
||||
func ApiOptions(opts ...ApiOption) (*ApiSettings, error) {
|
||||
options := &ApiSettings{
|
||||
Offline: false,
|
||||
FetchBlocks: true,
|
||||
}
|
||||
|
||||
return ApiOptionsTo(options, opts...)
|
||||
}
|
||||
|
||||
func ApiOptionsTo(options *ApiSettings, opts ...ApiOption) (*ApiSettings, error) {
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type apiOpts struct{}
|
||||
|
||||
var Api apiOpts
|
||||
|
||||
func (apiOpts) Offline(offline bool) ApiOption {
|
||||
return func(settings *ApiSettings) error {
|
||||
settings.Offline = offline
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FetchBlocks when set to false prevents api from fetching blocks from the
|
||||
// network while allowing other services such as IPNS to still be online
|
||||
func (apiOpts) FetchBlocks(fetch bool) ApiOption {
|
||||
return func(settings *ApiSettings) error {
|
||||
settings.FetchBlocks = fetch
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
package options
|
||||
|
||||
const (
|
||||
RSAKey = "rsa"
|
||||
Ed25519Key = "ed25519"
|
||||
|
||||
DefaultRSALen = 2048
|
||||
)
|
||||
|
||||
type KeyGenerateSettings struct {
|
||||
Algorithm string
|
||||
Size int
|
||||
}
|
||||
|
||||
type KeyRenameSettings struct {
|
||||
Force bool
|
||||
}
|
||||
|
||||
type KeyGenerateOption func(*KeyGenerateSettings) error
|
||||
type KeyRenameOption func(*KeyRenameSettings) error
|
||||
|
||||
func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) {
|
||||
options := &KeyGenerateSettings{
|
||||
Algorithm: RSAKey,
|
||||
Size: -1,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) {
|
||||
options := &KeyRenameSettings{
|
||||
Force: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type keyOpts struct{}
|
||||
|
||||
var Key keyOpts
|
||||
|
||||
// Type is an option for Key.Generate which specifies which algorithm
|
||||
// should be used for the key. Default is options.RSAKey
|
||||
//
|
||||
// Supported key types:
|
||||
// * options.RSAKey
|
||||
// * options.Ed25519Key
|
||||
func (keyOpts) Type(algorithm string) KeyGenerateOption {
|
||||
return func(settings *KeyGenerateSettings) error {
|
||||
settings.Algorithm = algorithm
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Size is an option for Key.Generate which specifies the size of the key to
|
||||
// generated. Default is -1
|
||||
//
|
||||
// value of -1 means 'use default size for key type':
|
||||
// * 2048 for RSA
|
||||
func (keyOpts) Size(size int) KeyGenerateOption {
|
||||
return func(settings *KeyGenerateSettings) error {
|
||||
settings.Size = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Force is an option for Key.Rename which specifies whether to allow to
|
||||
// replace existing keys.
|
||||
func (keyOpts) Force(force bool) KeyRenameOption {
|
||||
return func(settings *KeyRenameSettings) error {
|
||||
settings.Force = force
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
ropts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultNameValidTime = 24 * time.Hour
|
||||
)
|
||||
|
||||
type NamePublishSettings struct {
|
||||
ValidTime time.Duration
|
||||
Key string
|
||||
|
||||
TTL *time.Duration
|
||||
|
||||
AllowOffline bool
|
||||
}
|
||||
|
||||
type NameResolveSettings struct {
|
||||
Cache bool
|
||||
|
||||
ResolveOpts []ropts.ResolveOpt
|
||||
}
|
||||
|
||||
type NamePublishOption func(*NamePublishSettings) error
|
||||
type NameResolveOption func(*NameResolveSettings) error
|
||||
|
||||
func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) {
|
||||
options := &NamePublishSettings{
|
||||
ValidTime: DefaultNameValidTime,
|
||||
Key: "self",
|
||||
|
||||
AllowOffline: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) {
|
||||
options := &NameResolveSettings{
|
||||
Cache: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type nameOpts struct{}
|
||||
|
||||
var Name nameOpts
|
||||
|
||||
// ValidTime is an option for Name.Publish which specifies for how long the
|
||||
// entry will remain valid. Default value is 24h
|
||||
func (nameOpts) ValidTime(validTime time.Duration) NamePublishOption {
|
||||
return func(settings *NamePublishSettings) error {
|
||||
settings.ValidTime = validTime
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Key is an option for Name.Publish which specifies the key to use for
|
||||
// publishing. Default value is "self" which is the node's own PeerID.
|
||||
// The key parameter must be either PeerID or keystore key alias.
|
||||
//
|
||||
// You can use KeyAPI to list and generate more names and their respective keys.
|
||||
func (nameOpts) Key(key string) NamePublishOption {
|
||||
return func(settings *NamePublishSettings) error {
|
||||
settings.Key = key
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AllowOffline is an option for Name.Publish which specifies whether to allow
|
||||
// publishing when the node is offline. Default value is false
|
||||
func (nameOpts) AllowOffline(allow bool) NamePublishOption {
|
||||
return func(settings *NamePublishSettings) error {
|
||||
settings.AllowOffline = allow
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TTL is an option for Name.Publish which specifies the time duration the
|
||||
// published record should be cached for (caution: experimental).
|
||||
func (nameOpts) TTL(ttl time.Duration) NamePublishOption {
|
||||
return func(settings *NamePublishSettings) error {
|
||||
settings.TTL = &ttl
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Cache is an option for Name.Resolve which specifies if cache should be used.
|
||||
// Default value is true
|
||||
func (nameOpts) Cache(cache bool) NameResolveOption {
|
||||
return func(settings *NameResolveSettings) error {
|
||||
settings.Cache = cache
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
func (nameOpts) ResolveOption(opt ropts.ResolveOpt) NameResolveOption {
|
||||
return func(settings *NameResolveSettings) error {
|
||||
settings.ResolveOpts = append(settings.ResolveOpts, opt)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
package nsopts
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultDepthLimit is the default depth limit used by Resolve.
|
||||
DefaultDepthLimit = 32
|
||||
|
||||
// UnlimitedDepth allows infinite recursion in Resolve. You
|
||||
// probably don't want to use this, but it's here if you absolutely
|
||||
// trust resolution to eventually complete and can't put an upper
|
||||
// limit on how many steps it will take.
|
||||
UnlimitedDepth = 0
|
||||
)
|
||||
|
||||
// ResolveOpts specifies options for resolving an IPNS path
|
||||
type ResolveOpts struct {
|
||||
// Recursion depth limit
|
||||
Depth uint
|
||||
// The number of IPNS records to retrieve from the DHT
|
||||
// (the best record is selected from this set)
|
||||
DhtRecordCount uint
|
||||
// The amount of time to wait for DHT records to be fetched
|
||||
// and verified. A zero value indicates that there is no explicit
|
||||
// timeout (although there is an implicit timeout due to dial
|
||||
// timeouts within the DHT)
|
||||
DhtTimeout time.Duration
|
||||
}
|
||||
|
||||
// DefaultResolveOpts returns the default options for resolving
|
||||
// an IPNS path
|
||||
func DefaultResolveOpts() ResolveOpts {
|
||||
return ResolveOpts{
|
||||
Depth: DefaultDepthLimit,
|
||||
DhtRecordCount: 16,
|
||||
DhtTimeout: time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveOpt is used to set an option
|
||||
type ResolveOpt func(*ResolveOpts)
|
||||
|
||||
// Depth is the recursion depth limit
|
||||
func Depth(depth uint) ResolveOpt {
|
||||
return func(o *ResolveOpts) {
|
||||
o.Depth = depth
|
||||
}
|
||||
}
|
||||
|
||||
// DhtRecordCount is the number of IPNS records to retrieve from the DHT
|
||||
func DhtRecordCount(count uint) ResolveOpt {
|
||||
return func(o *ResolveOpts) {
|
||||
o.DhtRecordCount = count
|
||||
}
|
||||
}
|
||||
|
||||
// DhtTimeout is the amount of time to wait for DHT records to be fetched
|
||||
// and verified. A zero value indicates that there is no explicit timeout
|
||||
func DhtTimeout(timeout time.Duration) ResolveOpt {
|
||||
return func(o *ResolveOpts) {
|
||||
o.DhtTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessOpts converts an array of ResolveOpt into a ResolveOpts object
|
||||
func ProcessOpts(opts []ResolveOpt) ResolveOpts {
|
||||
rsopts := DefaultResolveOpts()
|
||||
for _, option := range opts {
|
||||
option(&rsopts)
|
||||
}
|
||||
return rsopts
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
package options
|
||||
|
||||
type ObjectNewSettings struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
type ObjectPutSettings struct {
|
||||
InputEnc string
|
||||
DataType string
|
||||
Pin bool
|
||||
}
|
||||
|
||||
type ObjectAddLinkSettings struct {
|
||||
Create bool
|
||||
}
|
||||
|
||||
type ObjectNewOption func(*ObjectNewSettings) error
|
||||
type ObjectPutOption func(*ObjectPutSettings) error
|
||||
type ObjectAddLinkOption func(*ObjectAddLinkSettings) error
|
||||
|
||||
func ObjectNewOptions(opts ...ObjectNewOption) (*ObjectNewSettings, error) {
|
||||
options := &ObjectNewSettings{
|
||||
Type: "empty",
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func ObjectPutOptions(opts ...ObjectPutOption) (*ObjectPutSettings, error) {
|
||||
options := &ObjectPutSettings{
|
||||
InputEnc: "json",
|
||||
DataType: "text",
|
||||
Pin: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func ObjectAddLinkOptions(opts ...ObjectAddLinkOption) (*ObjectAddLinkSettings, error) {
|
||||
options := &ObjectAddLinkSettings{
|
||||
Create: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type objectOpts struct{}
|
||||
|
||||
var Object objectOpts
|
||||
|
||||
// Type is an option for Object.New which allows to change the type of created
|
||||
// dag node.
|
||||
//
|
||||
// Supported types:
|
||||
// * 'empty' - Empty node
|
||||
// * 'unixfs-dir' - Empty UnixFS directory
|
||||
func (objectOpts) Type(t string) ObjectNewOption {
|
||||
return func(settings *ObjectNewSettings) error {
|
||||
settings.Type = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// InputEnc is an option for Object.Put which specifies the input encoding of the
|
||||
// data. Default is "json".
|
||||
//
|
||||
// Supported encodings:
|
||||
// * "protobuf"
|
||||
// * "json"
|
||||
func (objectOpts) InputEnc(e string) ObjectPutOption {
|
||||
return func(settings *ObjectPutSettings) error {
|
||||
settings.InputEnc = e
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DataType is an option for Object.Put which specifies the encoding of data
|
||||
// field when using Json or XML input encoding.
|
||||
//
|
||||
// Supported types:
|
||||
// * "text" (default)
|
||||
// * "base64"
|
||||
func (objectOpts) DataType(t string) ObjectPutOption {
|
||||
return func(settings *ObjectPutSettings) error {
|
||||
settings.DataType = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Pin is an option for Object.Put which specifies whether to pin the added
|
||||
// objects, default is false
|
||||
func (objectOpts) Pin(pin bool) ObjectPutOption {
|
||||
return func(settings *ObjectPutSettings) error {
|
||||
settings.Pin = pin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create is an option for Object.AddLink which specifies whether create required
|
||||
// directories for the child
|
||||
func (objectOpts) Create(create bool) ObjectAddLinkOption {
|
||||
return func(settings *ObjectAddLinkSettings) error {
|
||||
settings.Create = create
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,161 +0,0 @@
|
||||
package options
|
||||
|
||||
type PinAddSettings struct {
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
type PinLsSettings struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
// PinRmSettings represents the settings of pin rm command
|
||||
type PinRmSettings struct {
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
type PinUpdateSettings struct {
|
||||
Unpin bool
|
||||
}
|
||||
|
||||
type PinAddOption func(*PinAddSettings) error
|
||||
|
||||
// PinRmOption pin rm option func
|
||||
type PinRmOption func(*PinRmSettings) error
|
||||
|
||||
// PinLsOption pin ls option func
|
||||
type PinLsOption func(*PinLsSettings) error
|
||||
type PinUpdateOption func(*PinUpdateSettings) error
|
||||
|
||||
func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) {
|
||||
options := &PinAddSettings{
|
||||
Recursive: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// PinRmOptions pin rm options
|
||||
func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) {
|
||||
options := &PinRmSettings{
|
||||
Recursive: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) {
|
||||
options := &PinLsSettings{
|
||||
Type: "all",
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) {
|
||||
options := &PinUpdateSettings{
|
||||
Unpin: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type pinType struct{}
|
||||
|
||||
type pinOpts struct {
|
||||
Type pinType
|
||||
}
|
||||
|
||||
var Pin pinOpts
|
||||
|
||||
// All is an option for Pin.Ls which will make it return all pins. It is
|
||||
// the default
|
||||
func (pinType) All() PinLsOption {
|
||||
return Pin.pinType("all")
|
||||
}
|
||||
|
||||
// Recursive is an option for Pin.Ls which will make it only return recursive
|
||||
// pins
|
||||
func (pinType) Recursive() PinLsOption {
|
||||
return Pin.pinType("recursive")
|
||||
}
|
||||
|
||||
// Direct is an option for Pin.Ls which will make it only return direct (non
|
||||
// recursive) pins
|
||||
func (pinType) Direct() PinLsOption {
|
||||
return Pin.pinType("direct")
|
||||
}
|
||||
|
||||
// Indirect is an option for Pin.Ls which will make it only return indirect pins
|
||||
// (objects referenced by other recursively pinned objects)
|
||||
func (pinType) Indirect() PinLsOption {
|
||||
return Pin.pinType("indirect")
|
||||
}
|
||||
|
||||
// Recursive is an option for Pin.Add which specifies whether to pin an entire
|
||||
// object tree or just one object. Default: true
|
||||
func (pinOpts) Recursive(recursive bool) PinAddOption {
|
||||
return func(settings *PinAddSettings) error {
|
||||
settings.Recursive = recursive
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RmRecursive is an option for Pin.Rm
|
||||
func (pinOpts) RmRecursive(recursive bool) PinRmOption {
|
||||
return func(settings *PinRmSettings) error {
|
||||
settings.Recursive = recursive
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Type is an option for Pin.Ls which allows to specify which pin types should
|
||||
// be returned
|
||||
//
|
||||
// Supported values:
|
||||
// * "direct" - directly pinned objects
|
||||
// * "recursive" - roots of recursive pins
|
||||
// * "indirect" - indirectly pinned objects (referenced by recursively pinned
|
||||
// objects)
|
||||
// * "all" - all pinned objects (default)
|
||||
func (pinOpts) pinType(t string) PinLsOption {
|
||||
return func(settings *PinLsSettings) error {
|
||||
settings.Type = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Unpin is an option for Pin.Update which specifies whether to remove the old pin.
|
||||
// Default is true.
|
||||
func (pinOpts) Unpin(unpin bool) PinUpdateOption {
|
||||
return func(settings *PinUpdateSettings) error {
|
||||
settings.Unpin = unpin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
package options
|
||||
|
||||
type PubSubPeersSettings struct {
|
||||
Topic string
|
||||
}
|
||||
|
||||
type PubSubSubscribeSettings struct {
|
||||
Discover bool
|
||||
}
|
||||
|
||||
type PubSubPeersOption func(*PubSubPeersSettings) error
|
||||
type PubSubSubscribeOption func(*PubSubSubscribeSettings) error
|
||||
|
||||
func PubSubPeersOptions(opts ...PubSubPeersOption) (*PubSubPeersSettings, error) {
|
||||
options := &PubSubPeersSettings{
|
||||
Topic: "",
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSettings, error) {
|
||||
options := &PubSubSubscribeSettings{
|
||||
Discover: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type pubsubOpts struct{}
|
||||
|
||||
var PubSub pubsubOpts
|
||||
|
||||
func (pubsubOpts) Topic(topic string) PubSubPeersOption {
|
||||
return func(settings *PubSubPeersSettings) error {
|
||||
settings.Topic = topic
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (pubsubOpts) Discover(discover bool) PubSubSubscribeOption {
|
||||
return func(settings *PubSubSubscribeSettings) error {
|
||||
settings.Discover = discover
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,319 +0,0 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
|
||||
dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag"
|
||||
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
|
||||
)
|
||||
|
||||
type Layout int
|
||||
|
||||
const (
|
||||
BalancedLayout Layout = iota
|
||||
TrickleLayout
|
||||
)
|
||||
|
||||
type UnixfsAddSettings struct {
|
||||
CidVersion int
|
||||
MhType uint64
|
||||
|
||||
Inline bool
|
||||
InlineLimit int
|
||||
RawLeaves bool
|
||||
RawLeavesSet bool
|
||||
|
||||
Chunker string
|
||||
Layout Layout
|
||||
|
||||
Pin bool
|
||||
OnlyHash bool
|
||||
FsCache bool
|
||||
NoCopy bool
|
||||
|
||||
Wrap bool
|
||||
Hidden bool
|
||||
StdinName string
|
||||
|
||||
Events chan<- interface{}
|
||||
Silent bool
|
||||
Progress bool
|
||||
}
|
||||
|
||||
type UnixfsLsSettings struct {
|
||||
ResolveChildren bool
|
||||
}
|
||||
|
||||
type UnixfsAddOption func(*UnixfsAddSettings) error
|
||||
type UnixfsLsOption func(*UnixfsLsSettings) error
|
||||
|
||||
func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, error) {
|
||||
options := &UnixfsAddSettings{
|
||||
CidVersion: -1,
|
||||
MhType: mh.SHA2_256,
|
||||
|
||||
Inline: false,
|
||||
InlineLimit: 32,
|
||||
RawLeaves: false,
|
||||
RawLeavesSet: false,
|
||||
|
||||
Chunker: "size-262144",
|
||||
Layout: BalancedLayout,
|
||||
|
||||
Pin: false,
|
||||
OnlyHash: false,
|
||||
FsCache: false,
|
||||
NoCopy: false,
|
||||
|
||||
Wrap: false,
|
||||
Hidden: false,
|
||||
StdinName: "",
|
||||
|
||||
Events: nil,
|
||||
Silent: false,
|
||||
Progress: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, cid.Prefix{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// nocopy -> rawblocks
|
||||
if options.NoCopy && !options.RawLeaves {
|
||||
// fixed?
|
||||
if options.RawLeavesSet {
|
||||
return nil, cid.Prefix{}, fmt.Errorf("nocopy option requires '--raw-leaves' to be enabled as well")
|
||||
}
|
||||
|
||||
// No, satisfy mandatory constraint.
|
||||
options.RawLeaves = true
|
||||
}
|
||||
|
||||
// (hash != "sha2-256") -> CIDv1
|
||||
if options.MhType != mh.SHA2_256 {
|
||||
switch options.CidVersion {
|
||||
case 0:
|
||||
return nil, cid.Prefix{}, errors.New("CIDv0 only supports sha2-256")
|
||||
case 1, -1:
|
||||
options.CidVersion = 1
|
||||
default:
|
||||
return nil, cid.Prefix{}, fmt.Errorf("unknown CID version: %d", options.CidVersion)
|
||||
}
|
||||
} else {
|
||||
if options.CidVersion < 0 {
|
||||
// Default to CIDv0
|
||||
options.CidVersion = 0
|
||||
}
|
||||
}
|
||||
|
||||
// cidV1 -> raw blocks (by default)
|
||||
if options.CidVersion > 0 && !options.RawLeavesSet {
|
||||
options.RawLeaves = true
|
||||
}
|
||||
|
||||
prefix, err := dag.PrefixForCidVersion(options.CidVersion)
|
||||
if err != nil {
|
||||
return nil, cid.Prefix{}, err
|
||||
}
|
||||
|
||||
prefix.MhType = options.MhType
|
||||
prefix.MhLength = -1
|
||||
|
||||
return options, prefix, nil
|
||||
}
|
||||
|
||||
func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) {
|
||||
options := &UnixfsLsSettings{
|
||||
ResolveChildren: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type unixfsOpts struct{}
|
||||
|
||||
var Unixfs unixfsOpts
|
||||
|
||||
// CidVersion specifies which CID version to use. Defaults to 0 unless an option
|
||||
// that depends on CIDv1 is passed.
|
||||
func (unixfsOpts) CidVersion(version int) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.CidVersion = version
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Hash function to use. Implies CIDv1 if not set to sha2-256 (default).
|
||||
//
|
||||
// Table of functions is declared in https://github.com/multiformats/go-multihash/blob/master/multihash.go
|
||||
func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.MhType = mhtype
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RawLeaves specifies whether to use raw blocks for leaves (data nodes with no
|
||||
// links) instead of wrapping them with unixfs structures.
|
||||
func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.RawLeaves = enable
|
||||
settings.RawLeavesSet = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Inline tells the adder to inline small blocks into CIDs
|
||||
func (unixfsOpts) Inline(enable bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Inline = enable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// InlineLimit sets the amount of bytes below which blocks will be encoded
|
||||
// directly into CID instead of being stored and addressed by it's hash.
|
||||
// Specifying this option won't enable block inlining. For that use `Inline`
|
||||
// option. Default: 32 bytes
|
||||
//
|
||||
// Note that while there is no hard limit on the number of bytes, it should be
|
||||
// kept at a reasonably low value, such as 64; implementations may choose to
|
||||
// reject anything larger.
|
||||
func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.InlineLimit = limit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Chunker specifies settings for the chunking algorithm to use.
|
||||
//
|
||||
// Default: size-262144, formats:
|
||||
// size-[bytes] - Simple chunker splitting data into blocks of n bytes
|
||||
// rabin-[min]-[avg]-[max] - Rabin chunker
|
||||
func (unixfsOpts) Chunker(chunker string) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Chunker = chunker
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Layout tells the adder how to balance data between leaves.
|
||||
// options.BalancedLayout is the default, it's optimized for static seekable
|
||||
// files.
|
||||
// options.TrickleLayout is optimized for streaming data,
|
||||
func (unixfsOpts) Layout(layout Layout) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Layout = layout
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Pin tells the adder to pin the file root recursively after adding
|
||||
func (unixfsOpts) Pin(pin bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Pin = pin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// HashOnly will make the adder calculate data hash without storing it in the
|
||||
// blockstore or announcing it to the network
|
||||
func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.OnlyHash = hashOnly
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap tells the adder to wrap the added file structure with an additional
|
||||
// directory.
|
||||
func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Wrap = wrap
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Hidden enables adding of hidden files (files prefixed with '.')
|
||||
func (unixfsOpts) Hidden(hidden bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Hidden = hidden
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// StdinName is the name set for files which don specify FilePath as
|
||||
// os.Stdin.Name()
|
||||
func (unixfsOpts) StdinName(name string) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.StdinName = name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Events specifies channel which will be used to report events about ongoing
|
||||
// Add operation.
|
||||
//
|
||||
// Note that if this channel blocks it may slowdown the adder
|
||||
func (unixfsOpts) Events(sink chan<- interface{}) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Events = sink
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Silent reduces event output
|
||||
func (unixfsOpts) Silent(silent bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Silent = silent
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Progress tells the adder whether to enable progress events
|
||||
func (unixfsOpts) Progress(enable bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.Progress = enable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FsCache tells the adder to check the filestore for pre-existing blocks
|
||||
//
|
||||
// Experimental
|
||||
func (unixfsOpts) FsCache(enable bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.FsCache = enable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoCopy tells the adder to add the files using filestore. Implies RawLeaves.
|
||||
//
|
||||
// Experimental
|
||||
func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption {
|
||||
return func(settings *UnixfsAddSettings) error {
|
||||
settings.NoCopy = enable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (unixfsOpts) ResolveChildren(resolve bool) UnixfsLsOption {
|
||||
return func(settings *UnixfsLsSettings) error {
|
||||
settings.ResolveChildren = resolve
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
ipfspath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path"
|
||||
"gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
|
||||
)
|
||||
|
||||
//TODO: merge with ipfspath so we don't depend on it
|
||||
|
||||
// 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"
|
||||
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
|
||||
}
|
||||
|
||||
// ResolvedPath is a path which was resolved to the last resolvable node
|
||||
type ResolvedPath 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 ipfspath.Path
|
||||
}
|
||||
|
||||
// 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 := ipfspath.Join(append([]string{base.String()}, a...))
|
||||
return &path{path: ipfspath.FromString(s)}
|
||||
}
|
||||
|
||||
// IpfsPath creates new /ipfs path from the provided CID
|
||||
func IpfsPath(c cid.Cid) ResolvedPath {
|
||||
return &resolvedPath{
|
||||
path: path{ipfspath.Path("/ipfs/" + c.String())},
|
||||
cid: c,
|
||||
root: c,
|
||||
remainder: "",
|
||||
}
|
||||
}
|
||||
|
||||
// IpldPath creates new /ipld path from the provided CID
|
||||
func IpldPath(c cid.Cid) ResolvedPath {
|
||||
return &resolvedPath{
|
||||
path: path{ipfspath.Path("/ipld/" + c.String())},
|
||||
cid: c,
|
||||
root: c,
|
||||
remainder: "",
|
||||
}
|
||||
}
|
||||
|
||||
// ParsePath parses string path to a Path
|
||||
func ParsePath(p string) (Path, error) {
|
||||
pp, err := ipfspath.ParsePath(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &path{path: pp}, nil
|
||||
}
|
||||
|
||||
// NewResolvedPath creates new ResolvedPath. 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) ResolvedPath {
|
||||
return &resolvedPath{
|
||||
path: path{ipath},
|
||||
cid: c,
|
||||
root: root,
|
||||
remainder: remainder,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *path) String() string {
|
||||
return p.path.String()
|
||||
}
|
||||
|
||||
func (p *path) Namespace() string {
|
||||
if len(p.path.Segments()) < 1 {
|
||||
panic("path without namespace") //this shouldn't happen under any scenario
|
||||
}
|
||||
return p.path.Segments()[0]
|
||||
}
|
||||
|
||||
func (p *path) Mutable() bool {
|
||||
//TODO: MFS: check for /local
|
||||
return p.Namespace() == "ipns"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
// Pin holds information about pinned resource
|
||||
type Pin interface {
|
||||
// Path to the pinned object
|
||||
Path() ResolvedPath
|
||||
|
||||
// Type of the pin
|
||||
Type() string
|
||||
}
|
||||
|
||||
// PinStatus holds information about pin health
|
||||
type PinStatus interface {
|
||||
// Ok indicates whether the pin has been verified to be correct
|
||||
Ok() bool
|
||||
|
||||
// BadNodes returns any bad (usually missing) nodes from the pin
|
||||
BadNodes() []BadPinNode
|
||||
}
|
||||
|
||||
// BadPinNode is a node that has been marked as bad by Pin.Verify
|
||||
type BadPinNode interface {
|
||||
// Path is the path of the node
|
||||
Path() ResolvedPath
|
||||
|
||||
// Err is the reason why the node has been marked as bad
|
||||
Err() error
|
||||
}
|
||||
|
||||
// PinAPI specifies the interface to pining
|
||||
type PinAPI interface {
|
||||
// Add creates new pin, be default recursive - pinning the whole referenced
|
||||
// tree
|
||||
Add(context.Context, Path, ...options.PinAddOption) error
|
||||
|
||||
// Ls returns list of pinned objects on this node
|
||||
Ls(context.Context, ...options.PinLsOption) ([]Pin, error)
|
||||
|
||||
// Rm removes pin for object specified by the path
|
||||
Rm(context.Context, Path, ...options.PinRmOption) error
|
||||
|
||||
// Update changes one pin to another, skipping checks for matching paths in
|
||||
// the old tree
|
||||
Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error
|
||||
|
||||
// Verify verifies the integrity of pinned objects
|
||||
Verify(context.Context) (<-chan PinStatus, error)
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer"
|
||||
)
|
||||
|
||||
// PubSubSubscription is an active PubSub subscription
|
||||
type PubSubSubscription interface {
|
||||
io.Closer
|
||||
|
||||
// Next return the next incoming message
|
||||
Next(context.Context) (PubSubMessage, error)
|
||||
}
|
||||
|
||||
// PubSubMessage is a single PubSub message
|
||||
type PubSubMessage interface {
|
||||
// From returns id of a peer from which the message has arrived
|
||||
From() peer.ID
|
||||
|
||||
// Data returns the message body
|
||||
Data() []byte
|
||||
|
||||
// Seq returns message identifier
|
||||
Seq() []byte
|
||||
|
||||
// Topics returns list of topics this message was set to
|
||||
Topics() []string
|
||||
}
|
||||
|
||||
// PubSubAPI specifies the interface to PubSub
|
||||
type PubSubAPI interface {
|
||||
// Ls lists subscribed topics by name
|
||||
Ls(context.Context) ([]string, error)
|
||||
|
||||
// Peers list peers we are currently pubsubbing with
|
||||
Peers(context.Context, ...options.PubSubPeersOption) ([]peer.ID, error)
|
||||
|
||||
// Publish a message to a given pubsub topic
|
||||
Publish(context.Context, string, []byte) error
|
||||
|
||||
// Subscribe to messages on a given topic
|
||||
Subscribe(context.Context, string, ...options.PubSubSubscribeOption) (PubSubSubscription, error)
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr"
|
||||
"gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer"
|
||||
pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore"
|
||||
net "gx/ipfs/QmZ7cBWUXkyWTMN4qH6NGoyMVs7JugyFChBNP4ZUp5rJHH/go-libp2p-net"
|
||||
"gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotConnected = errors.New("not connected")
|
||||
ErrConnNotFound = errors.New("conn not found")
|
||||
)
|
||||
|
||||
// ConnectionInfo contains information about a peer
|
||||
type ConnectionInfo interface {
|
||||
// ID returns PeerID
|
||||
ID() peer.ID
|
||||
|
||||
// Address returns the multiaddress via which we are connected with the peer
|
||||
Address() ma.Multiaddr
|
||||
|
||||
// Direction returns which way the connection was established
|
||||
Direction() net.Direction
|
||||
|
||||
// Latency returns last known round trip time to the peer
|
||||
Latency() (time.Duration, error)
|
||||
|
||||
// Streams returns list of streams established with the peer
|
||||
Streams() ([]protocol.ID, error)
|
||||
}
|
||||
|
||||
// SwarmAPI specifies the interface to libp2p swarm
|
||||
type SwarmAPI interface {
|
||||
// Connect to a given peer
|
||||
Connect(context.Context, pstore.PeerInfo) error
|
||||
|
||||
// Disconnect from a given address
|
||||
Disconnect(context.Context, ma.Multiaddr) error
|
||||
|
||||
// Peers returns the list of peers we are connected to
|
||||
Peers(context.Context) ([]ConnectionInfo, error)
|
||||
|
||||
// KnownAddrs returns the list of all addresses this node is aware of
|
||||
KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error)
|
||||
|
||||
// LocalAddrs returns the list of announced listening addresses
|
||||
LocalAddrs(context.Context) ([]ma.Multiaddr, error)
|
||||
|
||||
// ListenAddrs returns the list of all listening addresses
|
||||
ListenAddrs(context.Context) ([]ma.Multiaddr, error)
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
)
|
||||
|
||||
var apiNotImplemented = errors.New("api not implemented")
|
||||
|
||||
func (tp *provider) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) {
|
||||
api, err := tp.MakeAPISwarm(ctx, false, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return api[0], nil
|
||||
}
|
||||
|
||||
type Provider interface {
|
||||
// Make creates n nodes. fullIdentity set to false can be ignored
|
||||
MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error)
|
||||
}
|
||||
|
||||
func (tp *provider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) {
|
||||
tp.apis <- 1
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
tp.apis <- -1
|
||||
}()
|
||||
|
||||
return tp.Provider.MakeAPISwarm(ctx, fullIdentity, n)
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
Provider
|
||||
|
||||
apis chan int
|
||||
}
|
||||
|
||||
func TestApi(p Provider) func(t *testing.T) {
|
||||
running := 1
|
||||
apis := make(chan int)
|
||||
zeroRunning := make(chan struct{})
|
||||
go func() {
|
||||
for i := range apis {
|
||||
running += i
|
||||
if running < 1 {
|
||||
close(zeroRunning)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
tp := &provider{Provider: p, apis: apis}
|
||||
|
||||
return func(t *testing.T) {
|
||||
t.Run("Block", tp.TestBlock)
|
||||
t.Run("Dag", tp.TestDag)
|
||||
t.Run("Dht", tp.TestDht)
|
||||
t.Run("Key", tp.TestKey)
|
||||
t.Run("Name", tp.TestName)
|
||||
t.Run("Object", tp.TestObject)
|
||||
t.Run("Path", tp.TestPath)
|
||||
t.Run("Pin", tp.TestPin)
|
||||
t.Run("PubSub", tp.TestPubSub)
|
||||
t.Run("Unixfs", tp.TestUnixfs)
|
||||
|
||||
apis <- -1
|
||||
t.Run("TestsCancelCtx", func(t *testing.T) {
|
||||
select {
|
||||
case <-zeroRunning:
|
||||
case <-time.After(time.Second):
|
||||
t.Errorf("%d test swarms(s) not closed", running)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) hasApi(t *testing.T, tf func(coreiface.CoreAPI) error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := tf(api); err != nil {
|
||||
t.Fatal(api)
|
||||
}
|
||||
}
|
||||
@ -1,243 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
|
||||
)
|
||||
|
||||
func (tp *provider) TestBlock(t *testing.T) {
|
||||
tp.hasApi(t, func(api coreiface.CoreAPI) error {
|
||||
if api.Block() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestBlockPut", tp.TestBlockPut)
|
||||
t.Run("TestBlockPutFormat", tp.TestBlockPutFormat)
|
||||
t.Run("TestBlockPutHash", tp.TestBlockPutHash)
|
||||
t.Run("TestBlockGet", tp.TestBlockGet)
|
||||
t.Run("TestBlockRm", tp.TestBlockRm)
|
||||
t.Run("TestBlockStat", tp.TestBlockStat)
|
||||
t.Run("TestBlockPin", tp.TestBlockPin)
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockPut(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Path().Cid().String() != "QmPyo15ynbVrSTVdJL9th7JysHaAbXt9dM9tXk1bMHbRtk" {
|
||||
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockPutFormat(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("cbor"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Path().Cid().String() != "zdpuAn4amuLWo8Widi5v6VQpuo2dnpnwbVE3oB6qqs7mDSeoa" {
|
||||
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockPutHash(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Path().Cid().String() != "zBurKB9YZkcDf6xa53WBE8CFX4ydVqAyf9KPXBFZt5stJzEstaS8Hukkhu4gwpMtc1xHNDbzP7sPtQKyWsP3C8fbhkmrZ" {
|
||||
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockGet(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := api.Block().Get(ctx, res.Path())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
d, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if string(d) != "Hello" {
|
||||
t.Error("didn't get correct data back")
|
||||
}
|
||||
|
||||
p, err := coreiface.ParsePath("/ipfs/" + res.Path().Cid().String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
rp, err := api.ResolvePath(ctx, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rp.Cid().String() != res.Path().Cid().String() {
|
||||
t.Error("paths didn't match")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockRm(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := api.Block().Get(ctx, res.Path())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
d, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if string(d) != "Hello" {
|
||||
t.Error("didn't get correct data back")
|
||||
}
|
||||
|
||||
err = api.Block().Rm(ctx, res.Path())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Block().Get(ctx, res.Path())
|
||||
if err == nil {
|
||||
t.Error("expected err to exist")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "blockservice: key not found") {
|
||||
t.Errorf("unexpected error; %s", err.Error())
|
||||
}
|
||||
|
||||
err = api.Block().Rm(ctx, res.Path())
|
||||
if err == nil {
|
||||
t.Error("expected err to exist")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "blockstore: block not found") {
|
||||
t.Errorf("unexpected error; %s", err.Error())
|
||||
}
|
||||
|
||||
err = api.Block().Rm(ctx, res.Path(), opt.Block.Force(true))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockStat(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stat, err := api.Block().Stat(ctx, res.Path())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if stat.Path().String() != res.Path().String() {
|
||||
t.Error("paths don't match")
|
||||
}
|
||||
|
||||
if stat.Size() != len("Hello") {
|
||||
t.Error("length doesn't match")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBlockPin(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Block().Put(ctx, strings.NewReader(`Hello`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if pins, err := api.Pin().Ls(ctx); err != nil || len(pins) != 0 {
|
||||
t.Fatal("expected 0 pins")
|
||||
}
|
||||
|
||||
res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Pin(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pins, err := api.Pin().Ls(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(pins) != 1 {
|
||||
t.Fatal("expected 1 pin")
|
||||
}
|
||||
if pins[0].Type() != "recursive" {
|
||||
t.Error("expected a recursive pin")
|
||||
}
|
||||
if pins[0].Path().String() != res.Path().String() {
|
||||
t.Error("pin path didn't match")
|
||||
}
|
||||
}
|
||||
@ -1,201 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
|
||||
ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
|
||||
ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor"
|
||||
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
|
||||
)
|
||||
|
||||
func (tp *provider) TestDag(t *testing.T) {
|
||||
tp.hasApi(t, func(api coreiface.CoreAPI) error {
|
||||
if api.Dag() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestPut", tp.TestPut)
|
||||
t.Run("TestPutWithHash", tp.TestPutWithHash)
|
||||
t.Run("TestPath", tp.TestDagPath)
|
||||
t.Run("TestTree", tp.TestTree)
|
||||
t.Run("TestBatch", tp.TestBatch)
|
||||
}
|
||||
|
||||
var (
|
||||
treeExpected = map[string]struct{}{
|
||||
"a": {},
|
||||
"b": {},
|
||||
"c": {},
|
||||
"c/d": {},
|
||||
"c/e": {},
|
||||
}
|
||||
)
|
||||
|
||||
func (tp *provider) TestPut(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Dag().Add(ctx, nd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" {
|
||||
t.Errorf("got wrong cid: %s", nd.Cid().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestPutWithHash(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.ID, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Dag().Add(ctx, nd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if nd.Cid().String() != "z5hRLNd2sv4z1c" {
|
||||
t.Errorf("got wrong cid: %s", nd.Cid().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestDagPath(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
snd, err := ipldcbor.FromJSON(strings.NewReader(`"foo"`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Dag().Add(ctx, snd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+snd.Cid().String()+`"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Dag().Add(ctx, nd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err := coreiface.ParsePath(path.Join(nd.Cid().String(), "lnk"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
rp, err := api.ResolvePath(ctx, p)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
ndd, err := api.Dag().Get(ctx, rp.Cid())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if ndd.Cid().String() != snd.Cid().String() {
|
||||
t.Errorf("got unexpected cid %s, expected %s", ndd.Cid().String(), snd.Cid().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestTree(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Dag().Add(ctx, nd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res, err := api.Dag().Get(ctx, nd.Cid())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
lst := res.Tree("", -1)
|
||||
if len(lst) != len(treeExpected) {
|
||||
t.Errorf("tree length of %d doesn't match expected %d", len(lst), len(treeExpected))
|
||||
}
|
||||
|
||||
for _, ent := range lst {
|
||||
if _, ok := treeExpected[ent]; !ok {
|
||||
t.Errorf("unexpected tree entry %s", ent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBatch(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" {
|
||||
t.Errorf("got wrong cid: %s", nd.Cid().String())
|
||||
}
|
||||
|
||||
_, err = api.Dag().Get(ctx, nd.Cid())
|
||||
if err == nil || !strings.Contains(err.Error(), "not found") {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := api.Dag().AddMany(ctx, []ipld.Node{nd}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Dag().Get(ctx, nd.Cid())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
func (tp *provider) TestDht(t *testing.T) {
|
||||
tp.hasApi(t, func(api iface.CoreAPI) error {
|
||||
if api.Dht() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestDhtFindPeer", tp.TestDhtFindPeer)
|
||||
t.Run("TestDhtFindProviders", tp.TestDhtFindProviders)
|
||||
t.Run("TestDhtProvide", tp.TestDhtProvide)
|
||||
}
|
||||
|
||||
func (tp *provider) TestDhtFindPeer(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
self0, err := apis[0].Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
laddrs0, err := apis[0].Swarm().LocalAddrs(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(laddrs0) != 1 {
|
||||
t.Fatal("unexpected number of local addrs")
|
||||
}
|
||||
|
||||
pi, err := apis[2].Dht().FindPeer(ctx, self0.ID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if pi.Addrs[0].String() != laddrs0[0].String() {
|
||||
t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String())
|
||||
}
|
||||
|
||||
self2, err := apis[2].Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pi, err = apis[1].Dht().FindPeer(ctx, self2.ID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
laddrs2, err := apis[2].Swarm().LocalAddrs(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(laddrs2) != 1 {
|
||||
t.Fatal("unexpected number of local addrs")
|
||||
}
|
||||
|
||||
if pi.Addrs[0].String() != laddrs2[0].String() {
|
||||
t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestDhtFindProviders(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err := addTestObject(ctx, apis[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
provider := <-out
|
||||
|
||||
self0, err := apis[0].Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if provider.ID.String() != self0.ID().String() {
|
||||
t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestDhtProvide(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
off0, err := apis[0].WithOptions(options.Api.Offline(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := off0.Block().Put(ctx, &io.LimitedReader{R: rnd, N: 4092})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p := s.Path()
|
||||
|
||||
out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
provider := <-out
|
||||
|
||||
self0, err := apis[0].Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if provider.ID.String() != "<peer.ID >" {
|
||||
t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String())
|
||||
}
|
||||
|
||||
err = apis[0].Dht().Provide(ctx, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err = apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
provider = <-out
|
||||
|
||||
if provider.ID.String() != self0.ID().String() {
|
||||
t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String())
|
||||
}
|
||||
}
|
||||
@ -1,523 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
func (tp *provider) TestKey(t *testing.T) {
|
||||
tp.hasApi(t, func(api iface.CoreAPI) error {
|
||||
if api.Key() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestListSelf", tp.TestListSelf)
|
||||
t.Run("TestRenameSelf", tp.TestRenameSelf)
|
||||
t.Run("TestRemoveSelf", tp.TestRemoveSelf)
|
||||
t.Run("TestGenerate", tp.TestGenerate)
|
||||
t.Run("TestGenerateSize", tp.TestGenerateSize)
|
||||
t.Run("TestGenerateType", tp.TestGenerateType)
|
||||
t.Run("TestGenerateExisting", tp.TestGenerateExisting)
|
||||
t.Run("TestList", tp.TestList)
|
||||
t.Run("TestRename", tp.TestRename)
|
||||
t.Run("TestRenameToSelf", tp.TestRenameToSelf)
|
||||
t.Run("TestRenameToSelfForce", tp.TestRenameToSelfForce)
|
||||
t.Run("TestRenameOverwriteNoForce", tp.TestRenameOverwriteNoForce)
|
||||
t.Run("TestRenameOverwrite", tp.TestRenameOverwrite)
|
||||
t.Run("TestRenameSameNameNoForce", tp.TestRenameSameNameNoForce)
|
||||
t.Run("TestRenameSameName", tp.TestRenameSameName)
|
||||
t.Run("TestRemove", tp.TestRemove)
|
||||
}
|
||||
|
||||
func (tp *provider) TestListSelf(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
self, err := api.Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
keys, err := api.Key().List(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list keys: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(keys) != 1 {
|
||||
t.Fatalf("there should be 1 key (self), got %d", len(keys))
|
||||
return
|
||||
}
|
||||
|
||||
if keys[0].Name() != "self" {
|
||||
t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name())
|
||||
}
|
||||
|
||||
if keys[0].Path().String() != "/ipns/"+self.ID().Pretty() {
|
||||
t.Errorf("expected the key to have path '/ipns/%s', got '%s'", self.ID().Pretty(), keys[0].Path().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameSelf(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _, err = api.Key().Rename(ctx, "self", "foo")
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "cannot rename key with name 'self'") {
|
||||
t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = api.Key().Rename(ctx, "self", "foo", opt.Key.Force(true))
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "cannot rename key with name 'self'") {
|
||||
t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRemoveSelf(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = api.Key().Remove(ctx, "self")
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "cannot remove key with name 'self'") {
|
||||
t.Fatalf("expected error 'cannot remove key with name 'self'', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestGenerate(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
k, err := api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if k.Name() != "foo" {
|
||||
t.Errorf("expected the key to be called 'foo', got '%s'", k.Name())
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") {
|
||||
t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestGenerateSize(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(1024))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if k.Name() != "foo" {
|
||||
t.Errorf("expected the key to be called 'foo', got '%s'", k.Name())
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") {
|
||||
t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestGenerateType(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
t.Skip("disabled until libp2p/specs#111 is fixed")
|
||||
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
k, err := api.Key().Generate(ctx, "bar", opt.Key.Type(opt.Ed25519Key))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if k.Name() != "bar" {
|
||||
t.Errorf("expected the key to be called 'foo', got '%s'", k.Name())
|
||||
}
|
||||
|
||||
// Expected to be an inlined identity hash.
|
||||
if !strings.HasPrefix(k.Path().String(), "/ipns/12") {
|
||||
t.Errorf("expected the key to be prefixed with '/ipns/12', got '%s'", k.Path().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestGenerateExisting(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "key with name 'foo' already exists") {
|
||||
t.Fatalf("expected error 'key with name 'foo' already exists', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "self")
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "cannot create key with name 'self'") {
|
||||
t.Fatalf("expected error 'cannot create key with name 'self'', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestList(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
l, err := api.Key().List(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(l) != 2 {
|
||||
t.Fatalf("expected to get 2 keys, got %d", len(l))
|
||||
return
|
||||
}
|
||||
|
||||
if l[0].Name() != "self" {
|
||||
t.Fatalf("expected key 0 to be called 'self', got '%s'", l[0].Name())
|
||||
return
|
||||
}
|
||||
|
||||
if l[1].Name() != "foo" {
|
||||
t.Fatalf("expected key 1 to be called 'foo', got '%s'", l[1].Name())
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(l[0].Path().String(), "/ipns/Qm") {
|
||||
t.Fatalf("expected key 0 to be prefixed with '/ipns/Qm', got '%s'", l[0].Name())
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(l[1].Path().String(), "/ipns/Qm") {
|
||||
t.Fatalf("expected key 1 to be prefixed with '/ipns/Qm', got '%s'", l[1].Name())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRename(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
k, overwrote, err := api.Key().Rename(ctx, "foo", "bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if overwrote {
|
||||
t.Error("overwrote should be false")
|
||||
}
|
||||
|
||||
if k.Name() != "bar" {
|
||||
t.Errorf("returned key should be called 'bar', got '%s'", k.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameToSelf(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _, err = api.Key().Rename(ctx, "foo", "self")
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "cannot overwrite key with name 'self'") {
|
||||
t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameToSelfForce(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _, err = api.Key().Rename(ctx, "foo", "self", opt.Key.Force(true))
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "cannot overwrite key with name 'self'") {
|
||||
t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _, err = api.Key().Rename(ctx, "foo", "bar")
|
||||
if err == nil {
|
||||
t.Error("expected error to not be nil")
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "key by that name already exists, refusing to overwrite") {
|
||||
t.Fatalf("expected error 'key by that name already exists, refusing to overwrite', got '%s'", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameOverwrite(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
kfoo, err := api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
k, overwrote, err := api.Key().Rename(ctx, "foo", "bar", opt.Key.Force(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !overwrote {
|
||||
t.Error("overwrote should be true")
|
||||
}
|
||||
|
||||
if k.Name() != "bar" {
|
||||
t.Errorf("returned key should be called 'bar', got '%s'", k.Name())
|
||||
}
|
||||
|
||||
if k.Path().String() != kfoo.Path().String() {
|
||||
t.Errorf("k and kfoo should have equal paths, '%s'!='%s'", k.Path().String(), kfoo.Path().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameSameNameNoForce(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
k, overwrote, err := api.Key().Rename(ctx, "foo", "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if overwrote {
|
||||
t.Error("overwrote should be false")
|
||||
}
|
||||
|
||||
if k.Name() != "foo" {
|
||||
t.Errorf("returned key should be called 'foo', got '%s'", k.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRenameSameName(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
k, overwrote, err := api.Key().Rename(ctx, "foo", "foo", opt.Key.Force(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if overwrote {
|
||||
t.Error("overwrote should be false")
|
||||
}
|
||||
|
||||
if k.Name() != "foo" {
|
||||
t.Errorf("returned key should be called 'foo', got '%s'", k.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestRemove(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
k, err := api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
l, err := api.Key().List(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(l) != 2 {
|
||||
t.Fatalf("expected to get 2 keys, got %d", len(l))
|
||||
return
|
||||
}
|
||||
|
||||
p, err := api.Key().Remove(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if k.Path().String() != p.Path().String() {
|
||||
t.Errorf("k and p should have equal paths, '%s'!='%s'", k.Path().String(), p.Path().String())
|
||||
}
|
||||
|
||||
l, err = api.Key().List(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(l) != 1 {
|
||||
t.Fatalf("expected to get 1 key, got %d", len(l))
|
||||
return
|
||||
}
|
||||
|
||||
if l[0].Name() != "self" {
|
||||
t.Errorf("expected the key to be called 'self', got '%s'", l[0].Name())
|
||||
}
|
||||
}
|
||||
@ -1,278 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"math/rand"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ipath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path"
|
||||
"gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files"
|
||||
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
func (tp *provider) TestName(t *testing.T) {
|
||||
tp.hasApi(t, func(api coreiface.CoreAPI) error {
|
||||
if api.Name() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestPublishResolve", tp.TestPublishResolve)
|
||||
t.Run("TestBasicPublishResolveKey", tp.TestBasicPublishResolveKey)
|
||||
t.Run("TestBasicPublishResolveTimeout", tp.TestBasicPublishResolveTimeout)
|
||||
}
|
||||
|
||||
var rnd = rand.New(rand.NewSource(0x62796532303137))
|
||||
|
||||
func addTestObject(ctx context.Context, api coreiface.CoreAPI) (coreiface.Path, error) {
|
||||
return api.Unixfs().Add(ctx, files.NewReaderFile(&io.LimitedReader{R: rnd, N: 4092}))
|
||||
}
|
||||
|
||||
func appendPath(p coreiface.Path, sub string) coreiface.Path {
|
||||
p, err := coreiface.ParsePath(path.Join(p.String(), sub))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (tp *provider) TestPublishResolve(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
init := func() (coreiface.CoreAPI, coreiface.Path) {
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, nil
|
||||
}
|
||||
api := apis[0]
|
||||
|
||||
p, err := addTestObject(ctx, api)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, nil
|
||||
}
|
||||
return api, p
|
||||
}
|
||||
|
||||
run := func(t *testing.T, ropts []opt.NameResolveOption) {
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
api, p := init()
|
||||
e, err := api.Name().Publish(ctx, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
self, err := api.Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if e.Name() != self.ID().Pretty() {
|
||||
t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name())
|
||||
}
|
||||
|
||||
if e.Value().String() != p.String() {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String())
|
||||
}
|
||||
|
||||
resPath, err := api.Name().Resolve(ctx, e.Name(), ropts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resPath.String() != p.String() {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("publishPath", func(t *testing.T) {
|
||||
api, p := init()
|
||||
e, err := api.Name().Publish(ctx, appendPath(p, "/test"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
self, err := api.Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if e.Name() != self.ID().Pretty() {
|
||||
t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name())
|
||||
}
|
||||
|
||||
if e.Value().String() != p.String()+"/test" {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String())
|
||||
}
|
||||
|
||||
resPath, err := api.Name().Resolve(ctx, e.Name(), ropts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resPath.String() != p.String()+"/test" {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()+"/test")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("revolvePath", func(t *testing.T) {
|
||||
api, p := init()
|
||||
e, err := api.Name().Publish(ctx, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
self, err := api.Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if e.Name() != self.ID().Pretty() {
|
||||
t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name())
|
||||
}
|
||||
|
||||
if e.Value().String() != p.String() {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String())
|
||||
}
|
||||
|
||||
resPath, err := api.Name().Resolve(ctx, e.Name()+"/test", ropts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resPath.String() != p.String()+"/test" {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()+"/test")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("publishRevolvePath", func(t *testing.T) {
|
||||
api, p := init()
|
||||
e, err := api.Name().Publish(ctx, appendPath(p, "/a"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
self, err := api.Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if e.Name() != self.ID().Pretty() {
|
||||
t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name())
|
||||
}
|
||||
|
||||
if e.Value().String() != p.String()+"/a" {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String())
|
||||
}
|
||||
|
||||
resPath, err := api.Name().Resolve(ctx, e.Name()+"/b", ropts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resPath.String() != p.String()+"/a/b" {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()+"/a/b")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
run(t, []opt.NameResolveOption{})
|
||||
})
|
||||
|
||||
t.Run("nocache", func(t *testing.T) {
|
||||
run(t, []opt.NameResolveOption{opt.Name.Cache(false)})
|
||||
})
|
||||
}
|
||||
|
||||
func (tp *provider) TestBasicPublishResolveKey(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
api := apis[0]
|
||||
|
||||
k, err := api.Key().Generate(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err := addTestObject(ctx, api)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
e, err := api.Name().Publish(ctx, p, opt.Name.Key(k.Name()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ipath.Join([]string{"/ipns", e.Name()}) != k.Path().String() {
|
||||
t.Errorf("expected e.Name to equal '%s', got '%s'", e.Name(), k.Path().String())
|
||||
}
|
||||
|
||||
if e.Value().String() != p.String() {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String())
|
||||
}
|
||||
|
||||
resPath, err := api.Name().Resolve(ctx, e.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resPath.String() != p.String() {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestBasicPublishResolveTimeout(t *testing.T) {
|
||||
t.Skip("ValidTime doesn't appear to work at this time resolution")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
api := apis[0]
|
||||
p, err := addTestObject(ctx, api)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
e, err := api.Name().Publish(ctx, p, opt.Name.ValidTime(time.Millisecond*100))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
self, err := api.Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if e.Name() != self.ID().Pretty() {
|
||||
t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name())
|
||||
}
|
||||
|
||||
if e.Value().String() != p.String() {
|
||||
t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String())
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
_, err = api.Name().Resolve(ctx, e.Name())
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: When swarm api is created, add multinode tests
|
||||
@ -1,461 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
)
|
||||
|
||||
func (tp *provider) TestObject(t *testing.T) {
|
||||
tp.hasApi(t, func(api iface.CoreAPI) error {
|
||||
if api.Object() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestNew", tp.TestNew)
|
||||
t.Run("TestObjectPut", tp.TestObjectPut)
|
||||
t.Run("TestObjectGet", tp.TestObjectGet)
|
||||
t.Run("TestObjectData", tp.TestObjectData)
|
||||
t.Run("TestObjectLinks", tp.TestObjectLinks)
|
||||
t.Run("TestObjectStat", tp.TestObjectStat)
|
||||
t.Run("TestObjectAddLink", tp.TestObjectAddLink)
|
||||
t.Run("TestObjectAddLinkCreate", tp.TestObjectAddLinkCreate)
|
||||
t.Run("TestObjectRmLink", tp.TestObjectRmLink)
|
||||
t.Run("TestObjectAddData", tp.TestObjectAddData)
|
||||
t.Run("TestObjectSetData", tp.TestObjectSetData)
|
||||
t.Run("TestDiffTest", tp.TestDiffTest)
|
||||
}
|
||||
|
||||
func (tp *provider) TestNew(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
emptyNode, err := api.Object().New(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dirNode, err := api.Object().New(ctx, opt.Object.Type("unixfs-dir"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if emptyNode.String() != "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" {
|
||||
t.Errorf("Unexpected emptyNode path: %s", emptyNode.String())
|
||||
}
|
||||
|
||||
if dirNode.String() != "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" {
|
||||
t.Errorf("Unexpected dirNode path: %s", dirNode.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectPut(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"YmFy"}`), opt.Object.DataType("base64")) //bar
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pbBytes, err := hex.DecodeString("0a0362617a")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p3, err := api.Object().Put(ctx, bytes.NewReader(pbBytes), opt.Object.InputEnc("protobuf"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if p1.String() != "/ipfs/QmQeGyS87nyijii7kFt1zbe4n2PsXTFimzsdxyE9qh9TST" {
|
||||
t.Errorf("unexpected path: %s", p1.String())
|
||||
}
|
||||
|
||||
if p2.String() != "/ipfs/QmNeYRbCibmaMMK6Du6ChfServcLqFvLJF76PzzF76SPrZ" {
|
||||
t.Errorf("unexpected path: %s", p2.String())
|
||||
}
|
||||
|
||||
if p3.String() != "/ipfs/QmZreR7M2t7bFXAdb1V5FtQhjk4t36GnrvueLJowJbQM9m" {
|
||||
t.Errorf("unexpected path: %s", p3.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectGet(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nd, err := api.Object().Get(ctx, p1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(nd.RawData()[len(nd.RawData())-3:]) != "foo" {
|
||||
t.Fatal("got non-matching data")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectData(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := api.Object().Data(ctx, p1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(data) != "foo" {
|
||||
t.Fatal("got non-matching data")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectLinks(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`"}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
links, err := api.Object().Links(ctx, p2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(links) != 1 {
|
||||
t.Errorf("unexpected number of links: %d", len(links))
|
||||
}
|
||||
|
||||
if links[0].Cid.String() != p1.Cid().String() {
|
||||
t.Fatal("cids didn't batch")
|
||||
}
|
||||
|
||||
if links[0].Name != "bar" {
|
||||
t.Fatal("unexpected link name")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectStat(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stat, err := api.Object().Stat(ctx, p2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if stat.Cid.String() != p2.Cid().String() {
|
||||
t.Error("unexpected stat.Cid")
|
||||
}
|
||||
|
||||
if stat.NumLinks != 1 {
|
||||
t.Errorf("unexpected stat.NumLinks")
|
||||
}
|
||||
|
||||
if stat.BlockSize != 51 {
|
||||
t.Error("unexpected stat.BlockSize")
|
||||
}
|
||||
|
||||
if stat.LinksSize != 47 {
|
||||
t.Errorf("unexpected stat.LinksSize: %d", stat.LinksSize)
|
||||
}
|
||||
|
||||
if stat.DataSize != 4 {
|
||||
t.Error("unexpected stat.DataSize")
|
||||
}
|
||||
|
||||
if stat.CumulativeSize != 54 {
|
||||
t.Error("unexpected stat.DataSize")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectAddLink(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p3, err := api.Object().AddLink(ctx, p2, "abc", p2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
links, err := api.Object().Links(ctx, p3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(links) != 2 {
|
||||
t.Errorf("unexpected number of links: %d", len(links))
|
||||
}
|
||||
|
||||
if links[0].Name != "abc" {
|
||||
t.Errorf("unexpected link 0 name: %s", links[0].Name)
|
||||
}
|
||||
|
||||
if links[1].Name != "bar" {
|
||||
t.Errorf("unexpected link 1 name: %s", links[1].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectAddLinkCreate(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p3, err := api.Object().AddLink(ctx, p2, "abc/d", p2)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "no link by that name") {
|
||||
t.Fatalf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
p3, err = api.Object().AddLink(ctx, p2, "abc/d", p2, opt.Object.Create(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
links, err := api.Object().Links(ctx, p3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(links) != 2 {
|
||||
t.Errorf("unexpected number of links: %d", len(links))
|
||||
}
|
||||
|
||||
if links[0].Name != "abc" {
|
||||
t.Errorf("unexpected link 0 name: %s", links[0].Name)
|
||||
}
|
||||
|
||||
if links[1].Name != "bar" {
|
||||
t.Errorf("unexpected link 1 name: %s", links[1].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectRmLink(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p3, err := api.Object().RmLink(ctx, p2, "bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
links, err := api.Object().Links(ctx, p3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(links) != 0 {
|
||||
t.Errorf("unexpected number of links: %d", len(links))
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectAddData(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().AppendData(ctx, p1, strings.NewReader("bar"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := api.Object().Data(ctx, p2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(r)
|
||||
|
||||
if string(data) != "foobar" {
|
||||
t.Error("unexpected data")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestObjectSetData(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().SetData(ctx, p1, strings.NewReader("bar"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := api.Object().Data(ctx, p2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(r)
|
||||
|
||||
if string(data) != "bar" {
|
||||
t.Error("unexpected data")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestDiffTest(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bar"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
changes, err := api.Object().Diff(ctx, p1, p2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(changes) != 1 {
|
||||
t.Fatal("unexpected changes len")
|
||||
}
|
||||
|
||||
if changes[0].Type != iface.DiffMod {
|
||||
t.Fatal("unexpected change type")
|
||||
}
|
||||
|
||||
if changes[0].Before.String() != p1.String() {
|
||||
t.Fatal("unexpected before path")
|
||||
}
|
||||
|
||||
if changes[0].After.String() != p2.String() {
|
||||
t.Fatal("unexpected before path")
|
||||
}
|
||||
}
|
||||
@ -1,219 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor"
|
||||
)
|
||||
|
||||
func (tp *provider) TestPath(t *testing.T) {
|
||||
t.Run("TestMutablePath", tp.TestMutablePath)
|
||||
t.Run("TestPathRemainder", tp.TestPathRemainder)
|
||||
t.Run("TestEmptyPathRemainder", tp.TestEmptyPathRemainder)
|
||||
t.Run("TestInvalidPathRemainder", tp.TestInvalidPathRemainder)
|
||||
t.Run("TestPathRoot", tp.TestPathRoot)
|
||||
t.Run("TestPathJoin", tp.TestPathJoin)
|
||||
}
|
||||
|
||||
func (tp *provider) TestMutablePath(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(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 ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor"
|
||||
|
||||
if api.Key() == nil {
|
||||
t.Fatal(".Key not implemented")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestPathRemainder(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if api.Dag() == nil {
|
||||
t.Fatal(".Dag not implemented")
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := api.Dag().Add(ctx, nd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := coreiface.ParsePath(nd.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 (tp *provider) TestEmptyPathRemainder(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if api.Dag() == nil {
|
||||
t.Fatal(".Dag not implemented")
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := api.Dag().Add(ctx, nd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := coreiface.ParsePath(nd.Cid().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 (tp *provider) TestInvalidPathRemainder(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if api.Dag() == nil {
|
||||
t.Fatal(".Dag not implemented")
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := api.Dag().Add(ctx, nd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/bar/baz")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = api.ResolvePath(ctx, p1)
|
||||
if err == nil || !strings.Contains(err.Error(), "no such link found") {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestPathRoot(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if api.Block() == nil {
|
||||
t.Fatal(".Block not implemented")
|
||||
}
|
||||
|
||||
blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if api.Dag() == nil {
|
||||
t.Fatal(".Dag not implemented")
|
||||
}
|
||||
|
||||
nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := api.Dag().Add(ctx, nd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/foo")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
rp, err := api.ResolvePath(ctx, p1)
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestPathJoin(t *testing.T) {
|
||||
p1, err := coreiface.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if coreiface.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" {
|
||||
t.Error("unexpected path")
|
||||
}
|
||||
}
|
||||
@ -1,239 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
|
||||
ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor"
|
||||
)
|
||||
|
||||
func (tp *provider) TestPin(t *testing.T) {
|
||||
tp.hasApi(t, func(api iface.CoreAPI) error {
|
||||
if api.Pin() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestPinAdd", tp.TestPinAdd)
|
||||
t.Run("TestPinSimple", tp.TestPinSimple)
|
||||
t.Run("TestPinRecursive", tp.TestPinRecursive)
|
||||
}
|
||||
|
||||
func (tp *provider) TestPinAdd(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
p, err := api.Unixfs().Add(ctx, strFile("foo")())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Pin().Add(ctx, p)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestPinSimple(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
p, err := api.Unixfs().Add(ctx, strFile("foo")())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Pin().Add(ctx, p)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
list, err := api.Pin().Ls(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 1 {
|
||||
t.Errorf("unexpected pin list len: %d", len(list))
|
||||
}
|
||||
|
||||
if list[0].Path().Cid().String() != p.Cid().String() {
|
||||
t.Error("paths don't match")
|
||||
}
|
||||
|
||||
if list[0].Type() != "recursive" {
|
||||
t.Error("unexpected pin type")
|
||||
}
|
||||
|
||||
err = api.Pin().Rm(ctx, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
list, err = api.Pin().Ls(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 0 {
|
||||
t.Errorf("unexpected pin list len: %d", len(list))
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *provider) TestPinRecursive(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
api, err := tp.makeAPI(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
p0, err := api.Unixfs().Add(ctx, strFile("foo")())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
p1, err := api.Unixfs().Add(ctx, strFile("bar")())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`), math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := api.Dag().AddMany(ctx, []ipld.Node{nd2, nd3}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = api.Pin().Add(ctx, iface.IpldPath(nd2.Cid()))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = api.Pin().Add(ctx, iface.IpldPath(nd3.Cid()), opt.Pin.Recursive(false))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
list, err := api.Pin().Ls(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 3 {
|
||||
t.Errorf("unexpected pin list len: %d", len(list))
|
||||
}
|
||||
|
||||
list, err = api.Pin().Ls(ctx, opt.Pin.Type.Direct())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 1 {
|
||||
t.Errorf("unexpected pin list len: %d", len(list))
|
||||
}
|
||||
|
||||
if list[0].Path().String() != iface.IpldPath(nd3.Cid()).String() {
|
||||
t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpfsPath(nd2.Cid()).String())
|
||||
}
|
||||
|
||||
list, err = api.Pin().Ls(ctx, opt.Pin.Type.Recursive())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 1 {
|
||||
t.Errorf("unexpected pin list len: %d", len(list))
|
||||
}
|
||||
|
||||
if list[0].Path().String() != iface.IpldPath(nd2.Cid()).String() {
|
||||
t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpldPath(nd3.Cid()).String())
|
||||
}
|
||||
|
||||
list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 1 {
|
||||
t.Errorf("unexpected pin list len: %d", len(list))
|
||||
}
|
||||
|
||||
if list[0].Path().Cid().String() != p0.Cid().String() {
|
||||
t.Error("unexpected path")
|
||||
}
|
||||
|
||||
res, err := api.Pin().Verify(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n := 0
|
||||
for r := range res {
|
||||
if !r.Ok() {
|
||||
t.Error("expected pin to be ok")
|
||||
}
|
||||
n++
|
||||
}
|
||||
|
||||
if n != 1 {
|
||||
t.Errorf("unexpected verify result count: %d", n)
|
||||
}
|
||||
|
||||
//TODO: figure out a way to test verify without touching IpfsNode
|
||||
/*
|
||||
err = api.Block().Rm(ctx, p0, opt.Block.Force(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res, err = api.Pin().Verify(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n = 0
|
||||
for r := range res {
|
||||
if r.Ok() {
|
||||
t.Error("expected pin to not be ok")
|
||||
}
|
||||
|
||||
if len(r.BadNodes()) != 1 {
|
||||
t.Fatalf("unexpected badNodes len")
|
||||
}
|
||||
|
||||
if r.BadNodes()[0].Path().Cid().String() != p0.Cid().String() {
|
||||
t.Error("unexpected badNode path")
|
||||
}
|
||||
|
||||
if r.BadNodes()[0].Err().Error() != "merkledag: not found" {
|
||||
t.Errorf("unexpected badNode error: %s", r.BadNodes()[0].Err().Error())
|
||||
}
|
||||
n++
|
||||
}
|
||||
|
||||
if n != 1 {
|
||||
t.Errorf("unexpected verify result count: %d", n)
|
||||
}
|
||||
*/
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (tp *provider) TestPubSub(t *testing.T) {
|
||||
tp.hasApi(t, func(api iface.CoreAPI) error {
|
||||
if api.PubSub() == nil {
|
||||
return apiNotImplemented
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Run("TestBasicPubSub", tp.TestBasicPubSub)
|
||||
}
|
||||
|
||||
func (tp *provider) TestBasicPubSub(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
apis, err := tp.MakeAPISwarm(ctx, true, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := apis[0].PubSub().Subscribe(ctx, "testch")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
tick := time.Tick(100 * time.Millisecond)
|
||||
|
||||
for {
|
||||
err = apis[1].PubSub().Publish(ctx, "testch", []byte("hello world"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
select {
|
||||
case <-tick:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
m, err := sub.Next(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(m.Data()) != "hello world" {
|
||||
t.Errorf("got invalid data: %s", string(m.Data()))
|
||||
}
|
||||
|
||||
self1, err := apis[1].Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if m.From() != self1.ID() {
|
||||
t.Errorf("m.From didn't match")
|
||||
}
|
||||
|
||||
peers, err := apis[1].PubSub().Peers(ctx, options.PubSub.Topic("testch"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(peers) != 1 {
|
||||
t.Fatalf("got incorrect number of peers: %d", len(peers))
|
||||
}
|
||||
|
||||
self0, err := apis[0].Key().Self(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if peers[0] != self0.ID() {
|
||||
t.Errorf("peer didn't match")
|
||||
}
|
||||
|
||||
peers, err = apis[1].PubSub().Peers(ctx, options.PubSub.Topic("nottestch"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(peers) != 0 {
|
||||
t.Fatalf("got incorrect number of peers: %d", len(peers))
|
||||
}
|
||||
|
||||
topics, err := apis[0].PubSub().Ls(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(topics) != 1 {
|
||||
t.Fatalf("got incorrect number of topics: %d", len(peers))
|
||||
}
|
||||
|
||||
if topics[0] != "testch" {
|
||||
t.Errorf("topic didn't match")
|
||||
}
|
||||
|
||||
topics, err = apis[1].PubSub().Ls(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(topics) != 0 {
|
||||
t.Fatalf("got incorrect number of topics: %d", len(peers))
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
|
||||
ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
|
||||
"gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs"
|
||||
"gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files"
|
||||
)
|
||||
|
||||
type AddEvent struct {
|
||||
Name string
|
||||
Path ResolvedPath `json:",omitempty"`
|
||||
Bytes int64 `json:",omitempty"`
|
||||
Size string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type FileType int32
|
||||
|
||||
const (
|
||||
TRaw = FileType(unixfs.TRaw)
|
||||
TFile = FileType(unixfs.TFile)
|
||||
TDirectory = FileType(unixfs.TDirectory)
|
||||
TMetadata = FileType(unixfs.TMetadata)
|
||||
TSymlink = FileType(unixfs.TSymlink)
|
||||
THAMTShard = FileType(unixfs.THAMTShard)
|
||||
)
|
||||
|
||||
type LsLink struct {
|
||||
Link *ipld.Link
|
||||
Size uint64
|
||||
Type FileType
|
||||
|
||||
Err error
|
||||
}
|
||||
|
||||
// UnixfsAPI is the basic interface to immutable files in IPFS
|
||||
// NOTE: This API is heavily WIP, things are guaranteed to break frequently
|
||||
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) (ResolvedPath, error)
|
||||
|
||||
// Get returns a read-only handle to a file tree referenced by a path
|
||||
//
|
||||
// Note that some implementations of this API may apply the specified context
|
||||
// to operations performed on the returned file
|
||||
Get(context.Context, Path) (files.Node, error)
|
||||
|
||||
// Ls returns the list of links in a directory. Links aren't guaranteed to be
|
||||
// returned in order
|
||||
Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan LsLink, error)
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
ReadSeekCloser
|
||||
Size() uint64
|
||||
CtxReadFull(context.Context, []byte) (int, error)
|
||||
}
|
||||
|
||||
// A ReadSeekCloser implements interfaces to read, copy, seek and close.
|
||||
type ReadSeekCloser interface {
|
||||
io.Reader
|
||||
io.Seeker
|
||||
io.Closer
|
||||
io.WriterTo
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user