diff --git a/docs/core-api/BITSWAP.md b/docs/core-api/BITSWAP.md index 9de51472ab..d903cfe94d 100644 --- a/docs/core-api/BITSWAP.md +++ b/docs/core-api/BITSWAP.md @@ -1,30 +1,33 @@ # Bitswap API -- [`ipfs.bitswap.wantlist([peerId,] [options])`](#ipfsbitswapwantlistpeerid-options) +- [`ipfs.bitswap.wantlist([options])`](#ipfsbitswapwantlistoptions) - [Parameters](#parameters) - [Options](#options) - [Returns](#returns) - [Example](#example) -- [`ipfs.bitswap.unwant(cids, [options])`](#ipfsbitswapunwantcids-options) +- [`ipfs.bitswap.wantlistForPeer(peerId, [options])`](#ipfsbitswapwantlistforpeerpeerid-options) - [Parameters](#parameters-1) - [Options](#options-1) - [Returns](#returns-1) - [Example](#example-1) -- [`ipfs.bitswap.stat([options])`](#ipfsbitswapstatoptions) +- [`ipfs.bitswap.unwant(cids, [options])`](#ipfsbitswapunwantcids-options) - [Parameters](#parameters-2) - [Options](#options-2) - [Returns](#returns-2) - [Example](#example-2) +- [`ipfs.bitswap.stat([options])`](#ipfsbitswapstatoptions) + - [Parameters](#parameters-3) + - [Options](#options-3) + - [Returns](#returns-3) + - [Example](#example-3) -## `ipfs.bitswap.wantlist([peerId,] [options])` +## `ipfs.bitswap.wantlist([options])` -> Returns the wantlist, optionally filtered by peer ID +> Returns the wantlist for your node ### Parameters -| Name | Type | Description | -| ---- | ---- | ----------- | -| peerId | [PeerId][], [CID][], `String` or `Buffer` | An optional peer ID to return the wantlist for | +None ### Options @@ -47,9 +50,40 @@ An optional object which may have the following keys: const list = await ipfs.bitswap.wantlist() console.log(list) // [ CID('QmHash') ] +``` + +A great source of [examples][] can be found in the tests for this API. + +## `ipfs.bitswap.wantlistForPeer(peerId, [options])` + +> Returns the wantlist for a connected peer + +### Parameters + +| Name | Type | Default | Description | +| ---- | ---- | ------- | ----------- | +| peerId | [PeerId][], [CID][], `String` or `Buffer` | A peer ID to return the wantlist for | + +### Options + +An optional object which may have the following keys: + +| Name | Type | Default | Description | +| ---- | ---- | ------- | ----------- | +| timeout | `Number` | `undefined` | A timeout in ms | +| signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | + +### Returns + +| Type | Description | +| -------- | -------- | +| `Promise` | An array of [CID][]s currently in the wantlist | -const list2 = await ipfs.bitswap.wantlist(peerId) -console.log(list2) +### Example + +```JavaScript +const list = await ipfs.bitswap.wantlistForPeer(peerId) +console.log(list) // [ CID('QmHash') ] ``` diff --git a/docs/core-api/BOOTSTRAP.md b/docs/core-api/BOOTSTRAP.md index 1d1baa44b1..dca8cf600a 100644 --- a/docs/core-api/BOOTSTRAP.md +++ b/docs/core-api/BOOTSTRAP.md @@ -4,25 +4,35 @@ Warning: your node requires bootstrappers to join the network and find other peers. -If you edit this list, you may find you have reduced or no connectivity. If this is the case, please reset your node's bootstrapper list with `ipfs.bootstrap.add({ default: true })`. +If you edit this list, you may find you have reduced or no connectivity. If this is the case, please reset your node's bootstrapper list with `ipfs.bootstrap.reset()`. -- [`ipfs.bootstrap.add([addr,] [options])`](#ipfsbootstrapaddaddr-options) +- [`ipfs.bootstrap.add(addr, [options])`](#ipfsbootstrapaddaddr-options) - [Parameters](#parameters) - [Options](#options) - [Returns](#returns) - [Example](#example) -- [`ipfs.bootstrap.list([options])`](#ipfsbootstraplistoptions) +- [`ipfs.bootstrap.reset([options])`](#ipfsbootstrapresetoptions) - [Parameters](#parameters-1) - [Options](#options-1) - [Returns](#returns-1) - [Example](#example-1) -- [`ipfs.bootstrap.rm([addr,] [options])`](#ipfsbootstraprmaddr-options) +- [`ipfs.bootstrap.list([options])`](#ipfsbootstraplistoptions) - [Parameters](#parameters-2) - [Options](#options-2) - [Returns](#returns-2) - [Example](#example-2) - -## `ipfs.bootstrap.add([addr,] [options])` +- [`ipfs.bootstrap.rm(addr, [options])`](#ipfsbootstraprmaddr-options) + - [Parameters](#parameters-3) + - [Options](#options-3) + - [Returns](#returns-3) + - [Example](#example-3) +- [`ipfs.bootstrap.clear([options])`](#ipfsbootstrapclearoptions) + - [Parameters](#parameters-4) + - [Options](#options-4) + - [Returns](#returns-4) + - [Example](#example-4) + +## `ipfs.bootstrap.add(addr, [options])` > Add a peer address to the bootstrap list @@ -30,7 +40,7 @@ If you edit this list, you may find you have reduced or no connectivity. If thi | Name | Type | Description | | ---- | ---- | ----------- | -| addr | [MultiAddr][] | The address of a network peer. If omitted the `default` option must be passed as `true`. | +| addr | [MultiAddr][] | The address of a network peer | ### Options @@ -38,12 +48,9 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | -| default | `boolean` | `false` | If true, add the default peers to the list | | timeout | `Number` | `undefined` | A timeout in ms | | signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | -Note: If passing the `default` option, `addr` is an optional parameter (may be `undefined`/`null`) and options may be passed as the first argument. i.e. `ipfs.bootstrap.add({ default: true })` - ### Returns | Type | Description | @@ -71,6 +78,48 @@ console.log(res.Peers) A great source of [examples][] can be found in the tests for this API. +## `ipfs.bootstrap.reset([options])` + +> Reset the bootstrap list to contain only the default bootstrap nodes + +### Parameters + +None. + +### Options + +An optional object which may have the following keys: + +| Name | Type | Default | Description | +| ---- | ---- | ------- | ----------- | +| timeout | `Number` | `undefined` | A timeout in ms | +| signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | + +### Returns + +| Type | Description | +| -------- | -------- | +| `Promise<{ Peers: Array }>` | An object that contains an array with all the added addresses | + +example of the returned object: + +```JavaScript +{ + Peers: [address1, address2, ...] +} +``` + +### Example + +```JavaScript +const res = await ipfs.bootstrap.reset() +console.log(res.Peers) +// Logs: +// ['/ip4/104....9z'] +``` + +A great source of [examples][] can be found in the tests for this API. + ## `ipfs.bootstrap.list([options])` > List all peer addresses in the bootstrap list @@ -113,7 +162,7 @@ console.log(res.Peers) A great source of [examples][] can be found in the tests for this API. -## `ipfs.bootstrap.rm([addr,] [options])` +## `ipfs.bootstrap.rm(addr, [options])` > Remove a peer address from the bootstrap list @@ -121,7 +170,7 @@ A great source of [examples][] can be found in the tests for this API. | Name | Type | Description | | ---- | ---- | ----------- | -| addr | [MultiAddr][] | The address of a network peer. If omitted the `all` option must be passed as `true`. | +| addr | [MultiAddr][] | The address of a network peer | ### Options @@ -129,11 +178,48 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | -| all | `boolean` | `false` | If true, remove all peers from the bootstrap list | | timeout | `Number` | `undefined` | A timeout in ms | | signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | -Note: If passing the `all` option, `addr` is an optional parameter (may be `undefined`/`null`) and options may be passed as the first argument. i.e. `ipfs.bootstrap.rm({ all: true })` +### Returns + +| Type | Description | +| -------- | -------- | +| `Promise<{ Peers: Array }>` | An object that contains an array with all the removed addresses | + +```JavaScript +{ + Peers: [address1, address2, ...] +} +``` + +### Example + +```JavaScript +const res = await ipfs.bootstrap.rm('address1') +console.log(res.Peers) +// Logs: +// [address1, ...] +``` + +A great source of [examples][] can be found in the tests for this API. + +## `ipfs.bootstrap.clear([options])` + +> Remove all peer addresses from the bootstrap list + +### Parameters + +None. + +### Options + +An optional object which may have the following keys: + +| Name | Type | Default | Description | +| ---- | ---- | ------- | ----------- | +| timeout | `Number` | `undefined` | A timeout in ms | +| signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | ### Returns @@ -150,7 +236,7 @@ Note: If passing the `all` option, `addr` is an optional parameter (may be `unde ### Example ```JavaScript -const res = await ipfs.bootstrap.rm(null, { all: true }) +const res = await ipfs.bootstrap.clear() console.log(res.Peers) // Logs: // [address1, address2, ...] diff --git a/docs/core-api/DAG.md b/docs/core-api/DAG.md index 464d9d5478..4e6b441a60 100644 --- a/docs/core-api/DAG.md +++ b/docs/core-api/DAG.md @@ -7,12 +7,12 @@ - [Options](#options) - [Returns](#returns) - [Example](#example) -- [`ipfs.dag.get(cid, [path], [options])`](#ipfsdaggetcid-path-options) +- [`ipfs.dag.get(cid, [options])`](#ipfsdaggetcid-path-options) - [Parameters](#parameters-1) - [Options](#options-1) - [Returns](#returns-1) - [Example](#example-1) -- [`ipfs.dag.tree(cid, [path,] [options])`](#ipfsdagtreecid-path-options) +- [`ipfs.dag.tree(cid, [options])`](#ipfsdagtreecid-path-options) - [Parameters](#parameters-2) - [Options](#options-2) - [Returns](#returns-2) @@ -65,7 +65,7 @@ console.log(cid.toString()) A great source of [examples][] can be found in the tests for this API. -## `ipfs.dag.get(cid, [path], [options])` +## `ipfs.dag.get(cid, [options])` > Retrieve an IPLD format node @@ -73,8 +73,7 @@ A great source of [examples][] can be found in the tests for this API. | Name | Type | Description | | ---- | ---- | ----------- | -| cid | [CID][] | A DAG node that follows one of the supported IPLD formats | -| path | `String` | An optional path within the DAG to resolve | +| cid | [CID][] | A CID that resolves to a node to get | ### Options @@ -82,6 +81,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | +| path | `String` | An optional path within the DAG to resolve | | localResolve | `boolean` | `false` | If set to true, it will avoid resolving through different objects | | timeout | `Number` | `undefined` | A timeout in ms | | signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | @@ -114,34 +114,34 @@ const cid = await ipfs.dag.put(obj, { format: 'dag-cbor', hashAlg: 'sha2-256' }) console.log(cid.toString()) // zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5 -async function getAndLog(cidPath) { - const result = await ipfs.dag.get(cidPath) +async function getAndLog(cid, path) { + const result = await ipfs.dag.get(cid, { path }) console.log(result.value) } -await getAndLog('zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5/a') +await getAndLog(cid, '/a') // Logs: // 1 -await getAndLog('zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5/b') +await getAndLog(cid, '/b') // Logs: // [1, 2, 3] -await getAndLog('zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5/c') +await getAndLog(cid, '/c') // Logs: // { // ca: [5, 6, 7], // cb: 'foo' // } -await getAndLog('zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5/c/ca/1') +await getAndLog(cid, '/c/ca/1') // Logs: // 6 ``` A great source of [examples][] can be found in the tests for this API. -## `ipfs.dag.tree(cid, [path,] [options])` +## `ipfs.dag.tree(cid, [options])` > Enumerate all the entries in a graph @@ -150,7 +150,6 @@ A great source of [examples][] can be found in the tests for this API. | Name | Type | Description | | ---- | ---- | ----------- | | cid | [CID][] | A DAG node that follows one of the supported IPLD formats | -| path | `String` | An optional path within the DAG to resolve | ### Options @@ -158,6 +157,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | +| path | `String` | An optional path within the DAG to resolve | | recursive | `boolean` | `false` | If set to true, it will follow the links and continuously run tree on them, returning all the paths in the graph | | timeout | `Number` | `undefined` | A timeout in ms | | signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | diff --git a/docs/core-api/FILES.md b/docs/core-api/FILES.md index 0698419a00..1e35c92002 100644 --- a/docs/core-api/FILES.md +++ b/docs/core-api/FILES.md @@ -72,11 +72,11 @@ _Explore the Mutable File System through interactive coding challenges in our [P - [Options](#options-12) - [Returns](#returns-11) - [Example](#example-8) - - [`ipfs.files.flush([path,] [options])`](#ipfsfilesflushpath-options) + - [`ipfs.files.flush(path, [options])`](#ipfsfilesflushpath-options) - [Parameters](#parameters-13) - [Options](#options-13) - [Returns](#returns-12) - - [`ipfs.files.ls([path], [options])`](#ipfsfileslspath-options) + - [`ipfs.files.ls(path, [options])`](#ipfsfileslspath-options) - [Parameters](#parameters-14) - [Options](#options-14) - [Returns](#returns-13) @@ -869,7 +869,7 @@ If `from` is an IPFS path and the content does not exist in your node's repo, on All values of `from` will be removed after the operation is complete unless they are an IPFS path. -### `ipfs.files.flush([path,] [options])` +### `ipfs.files.flush(path, [options])` > Flush a given path's data to the disk @@ -877,7 +877,7 @@ All values of `from` will be removed after the operation is complete unless they | Name | Type | Description | | ---- | ---- | ----------- | -| path | `String` | Optional [MFS path][] to flush, defaults to `'/'` | +| path | `String` | The [MFS path][] to flush | #### Options @@ -900,7 +900,7 @@ An optional object which may have the following keys: const cid = await ipfs.files.flush('/') ``` -### `ipfs.files.ls([path], [options])` +### `ipfs.files.ls(path, [options])` > List directories in the local mutable namespace @@ -908,7 +908,7 @@ const cid = await ipfs.files.flush('/') | Name | Type | Description | | ---- | ---- | ----------- | -| path | `String` | Optional [MFS path][] to list, defaults to `'/'` | +| path | `String` | The [MFS path][] to list | #### Options diff --git a/docs/core-api/OBJECT.md b/docs/core-api/OBJECT.md index 43395504ed..49664f22fb 100644 --- a/docs/core-api/OBJECT.md +++ b/docs/core-api/OBJECT.md @@ -1,6 +1,6 @@ # Object API -- [`ipfs.object.new([template,] [options])`](#ipfsobjectnewtemplate-options) +- [`ipfs.object.new([options])`](#ipfsobjectnewoptions) - [Parameters](#parameters) - [Options](#options) - [Returns](#returns) @@ -52,15 +52,13 @@ - [Returns](#returns-9) - [Example](#example-9) -## `ipfs.object.new([template,] [options])` +## `ipfs.object.new([options])` > Create a new MerkleDAG node, using a specific layout. Caveat: So far, only UnixFS object layouts are supported. ### Parameters -| Name | Type | Description | -| ---- | ---- | ----------- | -| template | `String` | If defined, must be a string `unixfs-dir` and if that is passed, the created node will be an empty unixfs style directory | +None. ### Options @@ -68,6 +66,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | +| template | `String` | If defined, must be a string `unixfs-dir` and if that is passed, the created node will be an empty unixfs style directory | | recursive | `boolean` | `false` | Resolve until the result is not an IPNS name | | nocache | `boolean` | `cache` | Do not use cached entries | | timeout | `Number` | `undefined` | A timeout in ms | @@ -82,7 +81,9 @@ An optional object which may have the following keys: ### Example ```JavaScript -const cid = await ipfs.object.new('unixfs-dir') +const cid = await ipfs.object.new({ + template: 'unixfs-dir' +}) console.log(cid.toString()) // Logs: // QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn diff --git a/docs/core-api/PIN.md b/docs/core-api/PIN.md index 14970ea841..2999b1cf10 100644 --- a/docs/core-api/PIN.md +++ b/docs/core-api/PIN.md @@ -5,7 +5,7 @@ - [Options](#options) - [Returns](#returns) - [Example](#example) -- [`ipfs.pin.ls([cid], [options])`](#ipfspinlscid-options) +- [`ipfs.pin.ls([options])`](#ipfspinlsoptions) - [Parameters](#parameters-1) - [Options](#options-1) - [Returns](#returns-1) @@ -61,15 +61,14 @@ console.log(pinset) A great source of [examples][] can be found in the tests for this API. -## `ipfs.pin.ls([cid], [options])` +## `ipfs.pin.ls([options])` -> List all the objects pinned to local storage or under a specific hash +> List all the objects pinned to local storage ### Parameters | Name | Type | Description | | ---- | ---- | ----------- | -| cid | [CID][] or `Array` | List these specific CIDs | ### Options @@ -77,6 +76,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | +| paths | [CID][] or `Array` or `String` or `Array` | CIDs or IPFS paths to search for in the pinset | | type | `String` | `undefined` | Filter by this type of pin ("recursive", "direct" or "indirect") | | timeout | `Number` | `undefined` | A timeout in ms | | signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | @@ -98,6 +98,17 @@ for await (const { cid, type } of ipfs.pin.ls()) { // { cid: CID(QmSo73bmN47gBxMNqbdV6rZ4KJiqaArqJ1nu5TvFhqqj1R), type: 'indirect' } ``` +```JavaScript +for await (const { cid, type } of ipfs.pin.ls({ + paths: [ new CID('Qmc5..'), new CID('QmZb..'), new CID('QmSo..') ] +})) { + console.log({ cid, type }) +} +// { cid: CID(Qmc5XkteJdb337s7VwFBAGtiaoj2QCEzyxtNRy3iMudc3E), type: 'recursive' } +// { cid: CID(QmZbj5ruYneZb8FuR9wnLqJCpCXMQudhSdWhdhp5U1oPWJ), type: 'indirect' } +// { cid: CID(QmSo73bmN47gBxMNqbdV6rZ4KJiqaArqJ1nu5TvFhqqj1R), type: 'indirect' } +``` + A great source of [examples][] can be found in the tests for this API. ## `ipfs.pin.rm(cid, [options])` diff --git a/examples/traverse-ipld-graphs/get-path-accross-formats.js b/examples/traverse-ipld-graphs/get-path-accross-formats.js index ef843a1a74..051845e6a8 100644 --- a/examples/traverse-ipld-graphs/get-path-accross-formats.js +++ b/examples/traverse-ipld-graphs/get-path-accross-formats.js @@ -29,7 +29,9 @@ async function main () { hashAlg: 'sha3-512' }) - const result = await ipfs.dag.get(cborNodeCid, 'hobbies/0/Data') + const result = await ipfs.dag.get(cborNodeCid, { + path: 'hobbies/0/Data' + }) console.log(result.value.toString()) } diff --git a/packages/interface-ipfs-core/src/bitswap/index.js b/packages/interface-ipfs-core/src/bitswap/index.js index 34949a7e1f..e2b78a1a9d 100644 --- a/packages/interface-ipfs-core/src/bitswap/index.js +++ b/packages/interface-ipfs-core/src/bitswap/index.js @@ -3,7 +3,8 @@ const { createSuite } = require('../utils/suite') const tests = { stat: require('./stat'), - wantlist: require('./wantlist') + wantlist: require('./wantlist'), + wantlistForPeer: require('./wantlist-for-peer') } module.exports = createSuite(tests) diff --git a/packages/interface-ipfs-core/src/bitswap/utils.js b/packages/interface-ipfs-core/src/bitswap/utils.js index 90439d3cf4..b0ee8fb62b 100644 --- a/packages/interface-ipfs-core/src/bitswap/utils.js +++ b/packages/interface-ipfs-core/src/bitswap/utils.js @@ -9,7 +9,13 @@ async function waitForWantlistKey (ipfs, key, opts = {}) { const end = Date.now() + opts.timeout while (Date.now() < end) { - const list = await ipfs.bitswap.wantlist(opts.peerId) + let list + + if (opts.peerId) { + list = await ipfs.bitswap.wantlistForPeer(opts.peerId) + } else { + list = await ipfs.bitswap.wantlist() + } if (list.some(cid => cid.toString() === key)) { return @@ -28,7 +34,13 @@ async function waitForWantlistKeyToBeRemoved (ipfs, key, opts = {}) { const end = Date.now() + opts.timeout while (Date.now() < end) { - const list = await ipfs.bitswap.wantlist(opts.peerId) + let list + + if (opts.peerId) { + list = await ipfs.bitswap.wantlistForPeer(opts.peerId) + } else { + list = await ipfs.bitswap.wantlist() + } if (list.some(cid => cid.toString() === key)) { await delay(opts.interval) diff --git a/packages/interface-ipfs-core/src/bitswap/wantlist-for-peer.js b/packages/interface-ipfs-core/src/bitswap/wantlist-for-peer.js new file mode 100644 index 0000000000..e65321f16a --- /dev/null +++ b/packages/interface-ipfs-core/src/bitswap/wantlist-for-peer.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +'use strict' + +const { getDescribe, getIt } = require('../utils/mocha') +const { waitForWantlistKey } = require('./utils') +const { isWebWorker } = require('ipfs-utils/src/env') +const testTimeout = require('../utils/test-timeout') + +/** @typedef { import("ipfsd-ctl/src/factory") } Factory */ +/** + * @param {Factory} common + * @param {Object} options + */ +module.exports = (common, options) => { + const describe = getDescribe(options) + const it = getIt(options) + + describe('.bitswap.wantlistForPeer', function () { + this.timeout(60 * 1000) + + let ipfsA + let ipfsB + const key = 'QmUBdnXXPyoDFXj3Hj39dNJ5VkN3QFRskXxcGaYFBB8CNR' + + before(async () => { + ipfsA = (await common.spawn()).api + // webworkers are not dialable because webrtc is not available + ipfsB = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api + // Add key to the wantlist for ipfsB + ipfsB.block.get(key).catch(() => { /* is ok, expected on teardown */ }) + + await ipfsA.swarm.connect(ipfsB.peerId.addresses[0]) + }) + + after(() => common.clean()) + + it('should respect timeout option when getting bitswap wantlist by peer', () => { + return testTimeout(() => ipfsA.bitswap.wantlistForPeer(ipfsB.peerId.id, { + timeout: 1 + })) + }) + + it('should get the wantlist by peer ID for a different node', function () { + return waitForWantlistKey(ipfsA, key, { + peerId: ipfsB.peerId.id, + timeout: 60 * 1000 + }) + }) + }) +} diff --git a/packages/interface-ipfs-core/src/bitswap/wantlist.js b/packages/interface-ipfs-core/src/bitswap/wantlist.js index d35deb6948..f22581b32c 100644 --- a/packages/interface-ipfs-core/src/bitswap/wantlist.js +++ b/packages/interface-ipfs-core/src/bitswap/wantlist.js @@ -47,13 +47,6 @@ module.exports = (common, options) => { return waitForWantlistKey(ipfsB, key) }) - it('should get the wantlist by peer ID for a different node', function () { - return waitForWantlistKey(ipfsA, key, { - peerId: ipfsB.peerId.id, - timeout: 60 * 1000 - }) - }) - it('should not get the wantlist when offline', async () => { const node = await common.spawn() await node.stop() diff --git a/packages/interface-ipfs-core/src/block/put.js b/packages/interface-ipfs-core/src/block/put.js index e0eaee8f29..475054fa2e 100644 --- a/packages/interface-ipfs-core/src/block/put.js +++ b/packages/interface-ipfs-core/src/block/put.js @@ -78,7 +78,7 @@ module.exports = (common, options) => { expect(block.cid.version).to.equal(1) expect(block.cid.codec).to.equal('raw') expect(multihash.decode(block.cid.multihash).name).to.equal('sha2-512') - expect(await all(ipfs.pin.ls(block.cid))).to.have.lengthOf(1) + expect(await all(ipfs.pin.ls({ paths: block.cid }))).to.have.lengthOf(1) }) it('should put a Block instance', async () => { diff --git a/packages/interface-ipfs-core/src/bootstrap/add.js b/packages/interface-ipfs-core/src/bootstrap/add.js index 83f0626eea..6531eb3b8d 100644 --- a/packages/interface-ipfs-core/src/bootstrap/add.js +++ b/packages/interface-ipfs-core/src/bootstrap/add.js @@ -28,8 +28,7 @@ module.exports = (common, options) => { after(() => common.clean()) it('should respect timeout option when adding bootstrap nodes', () => { - return testTimeout(() => ipfs.bootstrap.add(null, { - default: true, + return testTimeout(() => ipfs.bootstrap.add(validIp4, { timeout: 1 })) }) @@ -47,15 +46,8 @@ module.exports = (common, options) => { expect(peers).to.have.property('length').that.is.equal(1) }) - it('should return a list of bootstrap peers when called with the default option', async () => { - const res = await ipfs.bootstrap.add(null, { default: true }) - - const peers = res.Peers - expect(peers).to.have.property('length').that.is.gt(1) - }) - it('should prevent duplicate inserts of bootstrap peers', async () => { - await ipfs.bootstrap.rm(null, { all: true }) + await ipfs.bootstrap.clear() const added = await ipfs.bootstrap.add(validIp4) expect(added).to.have.property('Peers').that.deep.equals([validIp4]) diff --git a/packages/interface-ipfs-core/src/bootstrap/clear.js b/packages/interface-ipfs-core/src/bootstrap/clear.js new file mode 100644 index 0000000000..2d65561b9c --- /dev/null +++ b/packages/interface-ipfs-core/src/bootstrap/clear.js @@ -0,0 +1,56 @@ +/* eslint-env mocha */ +'use strict' + +const { getDescribe, getIt, expect } = require('../utils/mocha') +const testTimeout = require('../utils/test-timeout') + +/** @typedef { import("ipfsd-ctl/src/factory") } Factory */ +/** + * @param {Factory} common + * @param {Object} options + */ +module.exports = (common, options) => { + const describe = getDescribe(options) + const it = getIt(options) + + const validIp4 = '/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z' + + describe('.bootstrap.clear', function () { + this.timeout(100 * 1000) + + let ipfs + + before(async () => { ipfs = (await common.spawn()).api }) + + after(() => common.clean()) + + it('should respect timeout option when removing bootstrap nodes', () => { + return testTimeout(() => ipfs.bootstrap.clear({ + timeout: 1 + })) + }) + + it('should return a list containing the peer removed when called with a valid arg (ip4)', async () => { + await ipfs.bootstrap.clear() + + const addRes = await ipfs.bootstrap.add(validIp4) + expect(addRes).to.be.eql({ Peers: [validIp4] }) + + const rmRes = await ipfs.bootstrap.clear() + expect(rmRes).to.be.eql({ Peers: [validIp4] }) + + const peers = rmRes.Peers + expect(peers).to.have.property('length').that.is.equal(1) + }) + + it('should return a list of all peers removed when all option is passed', async () => { + const addRes = await ipfs.bootstrap.reset() + const addedPeers = addRes.Peers + + const rmRes = await ipfs.bootstrap.clear() + const removedPeers = rmRes.Peers + + expect(removedPeers.sort()).to.deep.equal(addedPeers.sort()) + }) + }) +} diff --git a/packages/interface-ipfs-core/src/bootstrap/index.js b/packages/interface-ipfs-core/src/bootstrap/index.js index 858d977475..b8276573df 100644 --- a/packages/interface-ipfs-core/src/bootstrap/index.js +++ b/packages/interface-ipfs-core/src/bootstrap/index.js @@ -3,7 +3,9 @@ const { createSuite } = require('../utils/suite') const tests = { add: require('./add'), + clear: require('./clear'), list: require('./list'), + reset: require('./reset'), rm: require('./rm') } diff --git a/packages/interface-ipfs-core/src/bootstrap/reset.js b/packages/interface-ipfs-core/src/bootstrap/reset.js new file mode 100644 index 0000000000..0f34242f85 --- /dev/null +++ b/packages/interface-ipfs-core/src/bootstrap/reset.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +'use strict' + +const { getDescribe, getIt, expect } = require('../utils/mocha') +const testTimeout = require('../utils/test-timeout') + +/** @typedef { import("ipfsd-ctl/src/factory") } Factory */ +/** + * @param {Factory} common + * @param {Object} options + */ +module.exports = (common, options) => { + const describe = getDescribe(options) + const it = getIt(options) + + describe('.bootstrap.reset', function () { + this.timeout(100 * 1000) + + let ipfs + + before(async () => { + ipfs = (await common.spawn()).api + }) + + after(() => common.clean()) + + it('should respect timeout option when resetting the bootstrap nodes', () => { + return testTimeout(() => ipfs.bootstrap.reset({ + timeout: 1 + })) + }) + + it('should return a list of bootstrap peers when resetting the bootstrap nodes', async () => { + const res = await ipfs.bootstrap.reset() + + const peers = res.Peers + expect(peers).to.have.property('length').that.is.gt(1) + }) + + it('should return a list of all peers removed when all option is passed', async () => { + const addRes = await ipfs.bootstrap.reset() + const addedPeers = addRes.Peers + + const rmRes = await ipfs.bootstrap.clear() + const removedPeers = rmRes.Peers + + expect(removedPeers.sort()).to.deep.equal(addedPeers.sort()) + }) + }) +} diff --git a/packages/interface-ipfs-core/src/bootstrap/rm.js b/packages/interface-ipfs-core/src/bootstrap/rm.js index d4f43e0318..db4240f5bc 100644 --- a/packages/interface-ipfs-core/src/bootstrap/rm.js +++ b/packages/interface-ipfs-core/src/bootstrap/rm.js @@ -36,13 +36,6 @@ module.exports = (common, options) => { .and.be.an.instanceOf(Error) }) - it('should return an empty list because no peers removed when called without an arg or options', async () => { - const res = await ipfs.bootstrap.rm(null) - - const peers = res.Peers - expect(peers).to.have.property('length').that.is.equal(0) - }) - it('should return a list containing the peer removed when called with a valid arg (ip4)', async () => { const addRes = await ipfs.bootstrap.add(validIp4) expect(addRes).to.be.eql({ Peers: [validIp4] }) @@ -53,15 +46,5 @@ module.exports = (common, options) => { const peers = rmRes.Peers expect(peers).to.have.property('length').that.is.equal(1) }) - - it('should return a list of all peers removed when all option is passed', async () => { - const addRes = await ipfs.bootstrap.add(null, { default: true }) - const addedPeers = addRes.Peers - - const rmRes = await ipfs.bootstrap.rm(null, { all: true }) - const removedPeers = rmRes.Peers - - expect(removedPeers.sort()).to.deep.equal(addedPeers.sort()) - }) }) } diff --git a/packages/interface-ipfs-core/src/dag/get.js b/packages/interface-ipfs-core/src/dag/get.js index b88a6fa729..9003706336 100644 --- a/packages/interface-ipfs-core/src/dag/get.js +++ b/packages/interface-ipfs-core/src/dag/get.js @@ -85,7 +85,9 @@ module.exports = (common, options) => { }) it('should get a dag-pb node with path', async () => { - const result = await ipfs.dag.get(cidPb, '/') + const result = await ipfs.dag.get(cidPb, { + path: '/' + }) const node = result.value @@ -94,7 +96,9 @@ module.exports = (common, options) => { }) it('should get a dag-pb node local value', async function () { - const result = await ipfs.dag.get(cidPb, 'Data') + const result = await ipfs.dag.get(cidPb, { + path: 'Data' + }) expect(result.value).to.eql(Buffer.from('I am inside a Protobuf')) }) @@ -102,7 +106,9 @@ module.exports = (common, options) => { it.skip('should get a dag-pb node value two levels deep', (done) => {}) it('should get a dag-cbor node with path', async () => { - const result = await ipfs.dag.get(cidCbor, '/') + const result = await ipfs.dag.get(cidCbor, { + path: '/' + }) const node = result.value @@ -111,7 +117,9 @@ module.exports = (common, options) => { }) it('should get a dag-cbor node local value', async () => { - const result = await ipfs.dag.get(cidCbor, 'someData') + const result = await ipfs.dag.get(cidCbor, { + path: 'someData' + }) expect(result.value).to.eql('I am inside a Cbor object') }) @@ -120,7 +128,9 @@ module.exports = (common, options) => { it.skip('should get dag-cbor value via dag-pb node', (done) => {}) it('should get dag-pb value via dag-cbor node', async function () { - const result = await ipfs.dag.get(cidCbor, 'pb/Data') + const result = await ipfs.dag.get(cidCbor, { + path: 'pb/Data' + }) expect(result.value).to.eql(Buffer.from('I am inside a Protobuf')) }) @@ -143,7 +153,10 @@ module.exports = (common, options) => { }) it('should get only a CID, due to resolving locally only', async function () { - const result = await ipfs.dag.get(cidCbor, 'pb/Data', { localResolve: true }) + const result = await ipfs.dag.get(cidCbor, { + path: 'pb/Data', + localResolve: true + }) expect(result.value.equals(cidPb)).to.be.true() }) @@ -193,7 +206,9 @@ module.exports = (common, options) => { cid = cid.toBaseEncodedString('base32') expect(cid).to.equal('bafyreic6f672hnponukaacmk2mmt7vs324zkagvu4hcww6yba6kby25zce') - const result = await ipfs.dag.get(cid, 'foo') + const result = await ipfs.dag.get(cid, { + path: 'foo' + }) expect(result.value).to.equal('dag-cbor-bar') }) @@ -207,7 +222,9 @@ module.exports = (common, options) => { const cid2 = await ipfs.dag.put(cbor2, { format: 'dag-cbor', hashAlg: 'sha2-256' }) - const result = await ipfs.dag.get(cid2, 'other/foo') + const result = await ipfs.dag.get(cid2, { + path: 'other/foo' + }) expect(result.value).to.equal('dag-cbor-bar') }) diff --git a/packages/interface-ipfs-core/src/dag/tree.js b/packages/interface-ipfs-core/src/dag/tree.js index ada763b097..ce1393da6b 100644 --- a/packages/interface-ipfs-core/src/dag/tree.js +++ b/packages/interface-ipfs-core/src/dag/tree.js @@ -61,14 +61,9 @@ module.exports = (common, options) => { }) it('should get tree with CID and path', async () => { - const paths = await all(ipfs.dag.tree(cidCbor, 'someData')) - expect(paths).to.eql([]) - }) - - it('should get tree with CID and path as String', async () => { - const cidCborStr = cidCbor.toBaseEncodedString() - - const paths = await all(ipfs.dag.tree(cidCborStr + '/someData')) + const paths = await all(ipfs.dag.tree(cidCbor, { + path: 'someData' + })) expect(paths).to.eql([]) }) @@ -83,7 +78,10 @@ module.exports = (common, options) => { }) it('should get tree with CID and path recursive', async () => { - const paths = await all(ipfs.dag.tree(cidCbor, 'pb', { recursive: true })) + const paths = await all(ipfs.dag.tree(cidCbor, { + path: 'pb', + recursive: true + })) expect(paths).to.have.members([ 'Links', 'Data' diff --git a/packages/interface-ipfs-core/src/files/flush.js b/packages/interface-ipfs-core/src/files/flush.js index 479307d5eb..1487aaee2a 100644 --- a/packages/interface-ipfs-core/src/files/flush.js +++ b/packages/interface-ipfs-core/src/files/flush.js @@ -33,9 +33,13 @@ module.exports = (common, options) => { } }) + it('should require a path', () => { + expect(ipfs.files.flush()).to.eventually.be.rejected() + }) + it('should flush root', async () => { const root = await ipfs.files.stat('/') - const flushed = await ipfs.files.flush() + const flushed = await ipfs.files.flush('/') expect(root.cid.toString()).to.equal(flushed.toString()) }) @@ -52,7 +56,7 @@ module.exports = (common, options) => { }) it('should respect timeout option when flushing changes', async () => { - await testTimeout(() => ipfs.files.flush({ + await testTimeout(() => ipfs.files.flush('/', { timeout: 1 })) }) diff --git a/packages/interface-ipfs-core/src/files/ls.js b/packages/interface-ipfs-core/src/files/ls.js index 21ed098554..1fc930eb3c 100644 --- a/packages/interface-ipfs-core/src/files/ls.js +++ b/packages/interface-ipfs-core/src/files/ls.js @@ -35,7 +35,11 @@ module.exports = (common, options) => { after(() => common.clean()) - it('lists the root directory by default', async () => { + it('should require a path', () => { + expect(all(ipfs.files.ls())).to.eventually.be.rejected() + }) + + it('lists the root directory', async () => { const fileName = `small-file-${Math.random()}.txt` const content = Buffer.from('Hello world') @@ -43,7 +47,7 @@ module.exports = (common, options) => { create: true }) - const files = await all(ipfs.files.ls()) + const files = await all(ipfs.files.ls('/')) expect(files).to.have.lengthOf(1).and.to.containSubset([{ cid: new CID('Qmetpc7cZmN25Wcc6R27cGCAvCDqCS5GjHG4v7xABEfpmJ'), @@ -201,7 +205,7 @@ module.exports = (common, options) => { }) it('should respect timeout option when listing files', async () => { - await testTimeout(() => drain(ipfs.files.ls({ + await testTimeout(() => drain(ipfs.files.ls('/', { timeout: 1 }))) }) diff --git a/packages/interface-ipfs-core/src/object/new.js b/packages/interface-ipfs-core/src/object/new.js index 5c40b84c78..9788218d01 100644 --- a/packages/interface-ipfs-core/src/object/new.js +++ b/packages/interface-ipfs-core/src/object/new.js @@ -36,7 +36,7 @@ module.exports = (common, options) => { }) it('should create a new object with unixfs-dir template', async () => { - const cid = await ipfs.object.new('unixfs-dir') + const cid = await ipfs.object.new({ template: 'unixfs-dir' }) expect(cid.toBaseEncodedString()).to.equal('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') }) }) diff --git a/packages/interface-ipfs-core/src/pin/index.js b/packages/interface-ipfs-core/src/pin/index.js index b3723d07c5..15f55419d3 100644 --- a/packages/interface-ipfs-core/src/pin/index.js +++ b/packages/interface-ipfs-core/src/pin/index.js @@ -2,9 +2,9 @@ const { createSuite } = require('../utils/suite') const tests = { + add: require('./add'), ls: require('./ls'), - rm: require('./rm'), - add: require('./add') + rm: require('./rm') } module.exports = createSuite(tests) diff --git a/packages/interface-ipfs-core/src/pin/ls.js b/packages/interface-ipfs-core/src/pin/ls.js index 5aa9048f97..7ca8f1c565 100644 --- a/packages/interface-ipfs-core/src/pin/ls.js +++ b/packages/interface-ipfs-core/src/pin/ls.js @@ -119,7 +119,9 @@ module.exports = (common, options) => { }) it('should list pins for a specific hash', async () => { - const pinset = await all(ipfs.pin.ls(fixtures.files[0].cid)) + const pinset = await all(ipfs.pin.ls({ + paths: fixtures.files[0].cid + })) expect(pinset).to.have.lengthOf(1) expect(pinset[0].type).to.equal('recursive') expect(pinset[0].cid.toString()).to.equal(fixtures.files[0].cid) @@ -127,35 +129,49 @@ module.exports = (common, options) => { it('should throw an error on missing direct pins for existing path', () => { // ipfs.txt is an indirect pin, so lookup for direct one should throw an error - return expect(all(ipfs.pin.ls(`/ipfs/${fixtures.directory.cid}/files/ipfs.txt`, { type: 'direct' }))) + return expect(all(ipfs.pin.ls({ + paths: `/ipfs/${fixtures.directory.cid}/files/ipfs.txt`, + type: 'direct' + }))) .to.eventually.be.rejected .and.be.an.instanceOf(Error) .and.to.have.property('message', `path '/ipfs/${fixtures.directory.cid}/files/ipfs.txt' is not pinned`) }) it('should throw an error on missing link for a specific path', () => { - return expect(all(ipfs.pin.ls(`/ipfs/${fixtures.directory.cid}/I-DONT-EXIST.txt`, { type: 'direct' }))) + return expect(all(ipfs.pin.ls({ + paths: `/ipfs/${fixtures.directory.cid}/I-DONT-EXIST.txt`, + type: 'direct' + }))) .to.eventually.be.rejected .and.be.an.instanceOf(Error) .and.to.have.property('message', `no link named "I-DONT-EXIST.txt" under ${fixtures.directory.cid}`) }) it('should list indirect pins for a specific path', async () => { - const pinset = await all(ipfs.pin.ls(`/ipfs/${fixtures.directory.cid}/files/ipfs.txt`, { type: 'indirect' })) + const pinset = await all(ipfs.pin.ls({ + paths: `/ipfs/${fixtures.directory.cid}/files/ipfs.txt`, + type: 'indirect' + })) expect(pinset).to.have.lengthOf(1) expect(pinset[0].type).to.equal(`indirect through ${fixtures.directory.cid}`) expect(pinset[0].cid.toString()).to.equal(fixtures.directory.files[1].cid) }) it('should list recursive pins for a specific hash', async () => { - const pinset = await all(ipfs.pin.ls(fixtures.files[0].cid, { type: 'recursive' })) + const pinset = await all(ipfs.pin.ls({ + paths: fixtures.files[0].cid, + type: 'recursive' + })) expect(pinset).to.have.lengthOf(1) expect(pinset[0].type).to.equal('recursive') expect(pinset[0].cid.toString()).to.equal(fixtures.files[0].cid) }) it('should list pins for multiple CIDs', async () => { - const pinset = await all(ipfs.pin.ls([fixtures.files[0].cid, fixtures.files[1].cid])) + const pinset = await all(ipfs.pin.ls({ + paths: [fixtures.files[0].cid, fixtures.files[1].cid] + })) const cids = pinset.map(p => p.cid.toString()) expect(cids).to.include(fixtures.files[0].cid) expect(cids).to.include(fixtures.files[1].cid) diff --git a/packages/ipfs-core-utils/package.json b/packages/ipfs-core-utils/package.json index bf675a47a5..a2d4d249d5 100644 --- a/packages/ipfs-core-utils/package.json +++ b/packages/ipfs-core-utils/package.json @@ -29,6 +29,7 @@ "license": "MIT", "dependencies": { "buffer": "^5.6.0", + "cids": "^0.8.0", "err-code": "^2.0.0", "ipfs-utils": "^2.2.2" }, diff --git a/packages/ipfs-core-utils/src/to-cid-and-path.js b/packages/ipfs-core-utils/src/to-cid-and-path.js new file mode 100644 index 0000000000..ab15475850 --- /dev/null +++ b/packages/ipfs-core-utils/src/to-cid-and-path.js @@ -0,0 +1,48 @@ +'use strict' + +const CID = require('cids') +const errCode = require('err-code') + +const IPFS_PREFIX = '/ipfs/' + +const toCidAndPath = (string) => { + if (Buffer.isBuffer(string)) { + try { + string = new CID(string) + } catch (err) { + throw errCode(err, 'ERR_INVALID_CID') + } + } + + if (CID.isCID(string)) { + return { + cid: string, + path: undefined + } + } + + if (string.startsWith(IPFS_PREFIX)) { + string = string.substring(IPFS_PREFIX.length) + } + + const parts = string.split('/') + let cid + let path + + try { + cid = new CID(parts.shift()) + } catch (err) { + throw errCode(err, 'ERR_INVALID_CID') + } + + if (parts.length) { + path = `/${parts.join('/')}` + } + + return { + cid, + path + } +} + +module.exports = toCidAndPath diff --git a/packages/ipfs-http-client/README.md b/packages/ipfs-http-client/README.md index 01e5ecc8a9..1a9b837ff9 100644 --- a/packages/ipfs-http-client/README.md +++ b/packages/ipfs-http-client/README.md @@ -280,7 +280,7 @@ const ipfs = ipfsClient({ host: '1.1.1.1', port: '80', apiPath: '/ipfs/api/v0' } ```javascript const bitswap = require('ipfs-http-client/src/bitswap')('/ip4/127.0.0.1/tcp/5001') -const list = await bitswap.wantlist(key) +const list = await bitswap.wantlist() // ... ``` diff --git a/packages/ipfs-http-client/src/bitswap/index.js b/packages/ipfs-http-client/src/bitswap/index.js index 5c8354498c..36d70b7db8 100644 --- a/packages/ipfs-http-client/src/bitswap/index.js +++ b/packages/ipfs-http-client/src/bitswap/index.js @@ -2,6 +2,7 @@ module.exports = config => ({ wantlist: require('./wantlist')(config), + wantlistForPeer: require('./wantlist-for-peer')(config), stat: require('./stat')(config), unwant: require('./unwant')(config) }) diff --git a/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js b/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js new file mode 100644 index 0000000000..b777ee7d6c --- /dev/null +++ b/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js @@ -0,0 +1,23 @@ +'use strict' + +const CID = require('cids') +const configure = require('../lib/configure') +const toUrlSearchParams = require('../lib/to-url-search-params') + +module.exports = configure(api => { + return async (peerId, options = {}) => { + peerId = typeof peerId === 'string' ? peerId : new CID(peerId).toString() + + const res = await (await api.post('bitswap/wantlist', { + timeout: options.timeout, + signal: options.signal, + searchParams: toUrlSearchParams({ + ...options, + peer: peerId + }), + headers: options.headers + })).json() + + return (res.Keys || []).map(k => new CID(k['/'])) + } +}) diff --git a/packages/ipfs-http-client/src/bitswap/wantlist.js b/packages/ipfs-http-client/src/bitswap/wantlist.js index d8357e7ac0..2599c7ed79 100644 --- a/packages/ipfs-http-client/src/bitswap/wantlist.js +++ b/packages/ipfs-http-client/src/bitswap/wantlist.js @@ -5,16 +5,7 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (peer, options = {}) => { - if (peer && (peer.timeout || peer.signal)) { - options = peer - peer = undefined - } - - if (peer) { - options.peer = typeof peer === 'string' ? peer : new CID(peer).toString() - } - + return async (options = {}) => { const res = await (await api.post('bitswap/wantlist', { timeout: options.timeout, signal: options.signal, diff --git a/packages/ipfs-http-client/src/bootstrap/clear.js b/packages/ipfs-http-client/src/bootstrap/clear.js new file mode 100644 index 0000000000..99d8091429 --- /dev/null +++ b/packages/ipfs-http-client/src/bootstrap/clear.js @@ -0,0 +1,20 @@ +'use strict' + +const configure = require('../lib/configure') +const toUrlSearchParams = require('../lib/to-url-search-params') + +module.exports = configure(api => { + return async (options = {}) => { + const res = await api.post('bootstrap/rm', { + timeout: options.timeout, + signal: options.signal, + searchParams: toUrlSearchParams({ + ...options, + all: true + }), + headers: options.headers + }) + + return res.json() + } +}) diff --git a/packages/ipfs-http-client/src/bootstrap/index.js b/packages/ipfs-http-client/src/bootstrap/index.js index 519a7161e0..3a1021d342 100644 --- a/packages/ipfs-http-client/src/bootstrap/index.js +++ b/packages/ipfs-http-client/src/bootstrap/index.js @@ -2,6 +2,8 @@ module.exports = config => ({ add: require('./add')(config), + clear: require('./clear')(config), rm: require('./rm')(config), + reset: require('./reset')(config), list: require('./list')(config) }) diff --git a/packages/ipfs-http-client/src/bootstrap/reset.js b/packages/ipfs-http-client/src/bootstrap/reset.js new file mode 100644 index 0000000000..9332aafc71 --- /dev/null +++ b/packages/ipfs-http-client/src/bootstrap/reset.js @@ -0,0 +1,20 @@ +'use strict' + +const configure = require('../lib/configure') +const toUrlSearchParams = require('../lib/to-url-search-params') + +module.exports = configure(api => { + return async (options = {}) => { + const res = await api.post('bootstrap/add', { + timeout: options.timeout, + signal: options.signal, + searchParams: toUrlSearchParams({ + ...options, + default: true + }), + headers: options.headers + }) + + return res.json() + } +}) diff --git a/packages/ipfs-http-client/src/dag/get.js b/packages/ipfs-http-client/src/dag/get.js index 2875aea8f6..cbc8e76f3d 100644 --- a/packages/ipfs-http-client/src/dag/get.js +++ b/packages/ipfs-http-client/src/dag/get.js @@ -15,13 +15,8 @@ module.exports = configure((api, options) => { const getBlock = require('../block/get')(options) const dagResolve = require('./resolve')(options) - return async (cid, path, options = {}) => { - if (path && typeof path === 'object') { - options = path - path = null - } - - const resolved = await dagResolve(cid, path, options) + return async (cid, options = {}) => { + const resolved = await dagResolve(cid, options) const block = await getBlock(resolved.cid, options) const dagResolver = resolvers[block.cid.codec] diff --git a/packages/ipfs-http-client/src/dag/resolve.js b/packages/ipfs-http-client/src/dag/resolve.js index acced6a42c..ba8c903846 100644 --- a/packages/ipfs-http-client/src/dag/resolve.js +++ b/packages/ipfs-http-client/src/dag/resolve.js @@ -5,17 +5,12 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (cid, path, options = {}) => { - if (path && typeof path === 'object') { - options = path - path = null - } - + return async (cid, options = {}) => { const res = await api.post('dag/resolve', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: path ? [cid, path].join(path.startsWith('/') ? '' : '/') : `${cid}`, + arg: `${cid}${options.path ? `/${options.path}`.replace(/\/[/]+/g, '/') : ''}`, ...options }), headers: options.headers diff --git a/packages/ipfs-http-client/src/files/flush.js b/packages/ipfs-http-client/src/files/flush.js index cb97a6d98c..d9934be9ad 100644 --- a/packages/ipfs-http-client/src/files/flush.js +++ b/packages/ipfs-http-client/src/files/flush.js @@ -6,9 +6,8 @@ const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { return async (path, options = {}) => { - if (typeof path !== 'string') { - options = path || {} - path = '/' + if (!path || typeof path !== 'string') { + throw new Error('ipfs.files.flush requires a path') } const res = await api.post('files/flush', { diff --git a/packages/ipfs-http-client/src/files/ls.js b/packages/ipfs-http-client/src/files/ls.js index 2e22ddcdb5..2420955e74 100644 --- a/packages/ipfs-http-client/src/files/ls.js +++ b/packages/ipfs-http-client/src/files/ls.js @@ -7,9 +7,8 @@ const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { return async function * ls (path, options = {}) { - if (typeof path !== 'string') { - options = path || {} - path = '/' + if (!path || typeof path !== 'string') { + throw new Error('ipfs.files.ls requires a path') } const res = await api.post('files/ls', { diff --git a/packages/ipfs-http-client/src/object/new.js b/packages/ipfs-http-client/src/object/new.js index 6b3bb04ebc..f0ae4e5574 100644 --- a/packages/ipfs-http-client/src/object/new.js +++ b/packages/ipfs-http-client/src/object/new.js @@ -5,17 +5,12 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (template, options = {}) => { - if (typeof template !== 'string') { - options = template || {} - template = null - } - + return async (options = {}) => { const res = await api.post('object/new', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: template, + arg: options.template, ...options }), headers: options.headers diff --git a/packages/ipfs-http-client/src/pin/ls.js b/packages/ipfs-http-client/src/pin/ls.js index 5fdc17386d..5133e3a568 100644 --- a/packages/ipfs-http-client/src/pin/ls.js +++ b/packages/ipfs-http-client/src/pin/ls.js @@ -5,20 +5,17 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async function * ls (path, options = {}) { - if (path && (path.type || path.timeout)) { - options = path || {} - path = [] + return async function * ls (options = {}) { + if (options.paths) { + options.paths = Array.isArray(options.paths) ? options.paths : [options.paths] } - path = Array.isArray(path) ? path : [path] - const res = await api.post('pin/ls', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: path.map(p => `${p}`), ...options, + arg: (options.paths || []).map(path => `${path}`), stream: true }), headers: options.headers diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index ed14eca677..54ec3d96db 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -117,6 +117,7 @@ "it-all": "^1.0.1", "it-concat": "^1.0.0", "it-drain": "^1.0.1", + "it-first": "^1.0.1", "it-glob": "0.0.8", "it-last": "^1.0.1", "it-map": "^1.0.0", @@ -182,7 +183,6 @@ "ipfs-interop": "^1.0.4", "ipfsd-ctl": "^4.1.1", "iso-random-stream": "^1.1.1", - "it-first": "^1.0.1", "it-to-buffer": "^1.0.0", "nanoid": "^3.0.2", "ncp": "^2.0.0", diff --git a/packages/ipfs/src/cli/commands/bitswap/wantlist.js b/packages/ipfs/src/cli/commands/bitswap/wantlist.js index 09991fea77..2f489eaf83 100644 --- a/packages/ipfs/src/cli/commands/bitswap/wantlist.js +++ b/packages/ipfs/src/cli/commands/bitswap/wantlist.js @@ -28,9 +28,18 @@ module.exports = { async handler ({ ctx, peer, cidBase, timeout }) { const { ipfs, print } = ctx - const list = await ipfs.bitswap.wantlist(peer, { - timeout - }) + let list + + if (peer) { + list = await ipfs.bitswap.wantlistForPeer(peer, { + timeout + }) + } else { + list = await ipfs.bitswap.wantlist({ + timeout + }) + } + list.forEach(cid => print(cidToString(cid, { base: cidBase, upgrade: false }))) } } diff --git a/packages/ipfs/src/cli/commands/bootstrap/add.js b/packages/ipfs/src/cli/commands/bootstrap/add.js index ec6c9a14e5..b43b45a298 100644 --- a/packages/ipfs/src/cli/commands/bootstrap/add.js +++ b/packages/ipfs/src/cli/commands/bootstrap/add.js @@ -20,10 +20,20 @@ module.exports = { }, async handler ({ ctx: { ipfs, print }, peer, default: defaultPeers, timeout }) { - const list = await ipfs.bootstrap.add(peer, { - default: defaultPeers, - timeout - }) + let list + + if (peer) { + list = await ipfs.bootstrap.add(peer, { + timeout + }) + } else if (defaultPeers) { + list = await ipfs.bootstrap.reset({ + timeout + }) + } else { + throw new Error('Please specify a peer or the --default flag') + } + list.Peers.forEach((peer) => print(peer)) } } diff --git a/packages/ipfs/src/cli/commands/bootstrap/rm.js b/packages/ipfs/src/cli/commands/bootstrap/rm.js index 60073504c4..ef9bab21e1 100644 --- a/packages/ipfs/src/cli/commands/bootstrap/rm.js +++ b/packages/ipfs/src/cli/commands/bootstrap/rm.js @@ -23,10 +23,20 @@ module.exports = { }, async handler ({ ctx: { ipfs, print }, all, peer, timeout }) { - const list = await ipfs.bootstrap.rm(peer, { - all, - timeout - }) + let list + + if (peer) { + list = await ipfs.bootstrap.rm(peer, { + timeout + }) + } else if (all) { + list = await ipfs.bootstrap.clear({ + timeout + }) + } else { + throw new Error('Please specify a peer or the --all flag') + } + list.Peers.forEach((peer) => print(peer)) } } diff --git a/packages/ipfs/src/cli/commands/dag/get.js b/packages/ipfs/src/cli/commands/dag/get.js index b2d3a5adc6..8506bd5a20 100644 --- a/packages/ipfs/src/cli/commands/dag/get.js +++ b/packages/ipfs/src/cli/commands/dag/get.js @@ -1,8 +1,8 @@ 'use strict' -const CID = require('cids') const parseDuration = require('parse-duration').default const { Buffer } = require('buffer') +const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') module.exports = { command: 'get ', @@ -21,20 +21,22 @@ module.exports = { }, async handler ({ ctx: { ipfs, print }, cidpath, localResolve, timeout }) { - const refParts = cidpath.split('/') - const cidString = refParts[0] - const path = refParts.slice(1).join('/') - const cid = new CID(cidString) - const options = { localResolve, timeout } + const { + cid, path + } = toCidAndPath(cidpath) + let result try { - result = await ipfs.dag.get(cid, path, options) + result = await ipfs.dag.get(cid, { + ...options, + path + }) } catch (err) { return print(`dag get failed: ${err}`) } diff --git a/packages/ipfs/src/cli/commands/object/new.js b/packages/ipfs/src/cli/commands/object/new.js index db4b090fb4..7576448c76 100644 --- a/packages/ipfs/src/cli/commands/object/new.js +++ b/packages/ipfs/src/cli/commands/object/new.js @@ -22,7 +22,8 @@ module.exports = { }, async handler ({ ctx: { ipfs, print }, template, cidBase, timeout }) { - const cid = await ipfs.object.new(template, { + const cid = await ipfs.object.new({ + template, timeout }) print(cidToString(cid, { base: cidBase, upgrade: false })) diff --git a/packages/ipfs/src/cli/commands/pin/ls.js b/packages/ipfs/src/cli/commands/pin/ls.js index 3e31ff1b5f..5cf9147b76 100644 --- a/packages/ipfs/src/cli/commands/pin/ls.js +++ b/packages/ipfs/src/cli/commands/pin/ls.js @@ -43,8 +43,6 @@ module.exports = { }, async handler ({ ctx: { ipfs, print }, ipfsPath, type, quiet, cidBase, stream, timeout }) { - const paths = ipfsPath - const printPin = res => { let line = cidToString(res.cid, { base: cidBase }) if (!quiet) { @@ -54,11 +52,20 @@ module.exports = { } if (!stream) { - const pins = await all(ipfs.pin.ls(paths, { type, stream: false, timeout })) + const pins = await all(ipfs.pin.ls({ + paths: ipfsPath, + type, + stream: false, + timeout + })) return pins.forEach(printPin) } - for await (const res of ipfs.pin.ls(paths, { type, timeout })) { + for await (const res of ipfs.pin.ls({ + paths: ipfsPath, + type, + timeout + })) { printPin(res) } } diff --git a/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js b/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js new file mode 100644 index 0000000000..4eb4092cd3 --- /dev/null +++ b/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js @@ -0,0 +1,12 @@ +'use strict' + +const PeerId = require('peer-id') +const { withTimeoutOption } = require('../../utils') + +module.exports = ({ bitswap }) => { + return withTimeoutOption(async function wantlistForPeer (peerId, options = {}) { // eslint-disable-line require-await + const list = bitswap.wantlistForPeer(PeerId.createFromCID(peerId), options) + + return Array.from(list).map(e => e[1].cid) + }) +} diff --git a/packages/ipfs/src/core/components/bitswap/wantlist.js b/packages/ipfs/src/core/components/bitswap/wantlist.js index 44b366ef5b..13930dd166 100644 --- a/packages/ipfs/src/core/components/bitswap/wantlist.js +++ b/packages/ipfs/src/core/components/bitswap/wantlist.js @@ -1,19 +1,10 @@ 'use strict' -const PeerId = require('peer-id') -const CID = require('cids') const { withTimeoutOption } = require('../../utils') module.exports = ({ bitswap }) => { - return withTimeoutOption(async function wantlist (peerId, options = {}) { // eslint-disable-line require-await - if (peerId && !CID.isCID(peerId) && typeof peerId !== 'string' && !Buffer.isBuffer(peerId) && !PeerId.isPeerId(peerId)) { - options = peerId - peerId = null - } - - const list = peerId - ? bitswap.wantlistForPeer(PeerId.createFromCID(peerId)) - : bitswap.getWantlist(options) + return withTimeoutOption(async function wantlist (options = {}) { // eslint-disable-line require-await + const list = bitswap.getWantlist(options) return Array.from(list).map(e => e[1].cid) }) diff --git a/packages/ipfs/src/core/components/bootstrap/add.js b/packages/ipfs/src/core/components/bootstrap/add.js index 855c5b3698..e3fc4b914d 100644 --- a/packages/ipfs/src/core/components/bootstrap/add.js +++ b/packages/ipfs/src/core/components/bootstrap/add.js @@ -1,27 +1,24 @@ 'use strict' -const defaultConfig = require('../../runtime/config-nodejs.js') const { isValidMultiaddr } = require('./utils') const { withTimeoutOption } = require('../../utils') module.exports = ({ repo }) => { - return withTimeoutOption(async function add (multiaddr, options) { - options = options || {} - - if (multiaddr && !isValidMultiaddr(multiaddr)) { + return withTimeoutOption(async function add (multiaddr, options = {}) { + if (!isValidMultiaddr(multiaddr)) { throw new Error(`${multiaddr} is not a valid Multiaddr`) } - const config = await repo.config.getAll() - if (options.default) { - config.Bootstrap = defaultConfig().Bootstrap - } else if (multiaddr && config.Bootstrap.indexOf(multiaddr) === -1) { + const config = await repo.config.getAll(options) + + if (config.Bootstrap.indexOf(multiaddr) === -1) { config.Bootstrap.push(multiaddr) } + await repo.config.set(config) return { - Peers: options.default ? defaultConfig().Bootstrap : [multiaddr] + Peers: [multiaddr] } }) } diff --git a/packages/ipfs/src/core/components/bootstrap/clear.js b/packages/ipfs/src/core/components/bootstrap/clear.js new file mode 100644 index 0000000000..100e01964a --- /dev/null +++ b/packages/ipfs/src/core/components/bootstrap/clear.js @@ -0,0 +1,15 @@ +'use strict' + +const { withTimeoutOption } = require('../../utils') + +module.exports = ({ repo }) => { + return withTimeoutOption(async function clear (options = {}) { + const config = await repo.config.getAll(options) + const removed = config.Bootstrap || [] + config.Bootstrap = [] + + await repo.config.set(config) + + return { Peers: removed } + }) +} diff --git a/packages/ipfs/src/core/components/bootstrap/reset.js b/packages/ipfs/src/core/components/bootstrap/reset.js new file mode 100644 index 0000000000..23d4134965 --- /dev/null +++ b/packages/ipfs/src/core/components/bootstrap/reset.js @@ -0,0 +1,17 @@ +'use strict' + +const defaultConfig = require('../../runtime/config-nodejs.js') +const { withTimeoutOption } = require('../../utils') + +module.exports = ({ repo }) => { + return withTimeoutOption(async function reset (options = {}) { + const config = await repo.config.getAll(options) + config.Bootstrap = defaultConfig().Bootstrap + + await repo.config.set(config) + + return { + Peers: defaultConfig().Bootstrap + } + }) +} diff --git a/packages/ipfs/src/core/components/bootstrap/rm.js b/packages/ipfs/src/core/components/bootstrap/rm.js index c8a9b80fe0..63a135858c 100644 --- a/packages/ipfs/src/core/components/bootstrap/rm.js +++ b/packages/ipfs/src/core/components/bootstrap/rm.js @@ -4,29 +4,16 @@ const { isValidMultiaddr } = require('./utils') const { withTimeoutOption } = require('../../utils') module.exports = ({ repo }) => { - return withTimeoutOption(async function rm (multiaddr, options) { - options = options || {} - - if (multiaddr && !isValidMultiaddr(multiaddr)) { + return withTimeoutOption(async function rm (multiaddr, options = {}) { + if (!isValidMultiaddr(multiaddr)) { throw new Error(`${multiaddr} is not a valid Multiaddr`) } - let res = [] - const config = await repo.config.getAll() - - if (options.all) { - res = config.Bootstrap || [] - config.Bootstrap = [] - } else { - config.Bootstrap = (config.Bootstrap || []).filter(ma => ma !== multiaddr) - } + const config = await repo.config.getAll(options) + config.Bootstrap = (config.Bootstrap || []).filter(ma => ma !== multiaddr) await repo.config.set(config) - if (!options.all && multiaddr) { - res.push(multiaddr) - } - - return { Peers: res } + return { Peers: [multiaddr] } }) } diff --git a/packages/ipfs/src/core/components/dag/get.js b/packages/ipfs/src/core/components/dag/get.js index d4a11598d4..e3d1adf7d0 100644 --- a/packages/ipfs/src/core/components/dag/get.js +++ b/packages/ipfs/src/core/components/dag/get.js @@ -1,35 +1,36 @@ 'use strict' -const { parseArgs } = require('./utils') const { withTimeoutOption } = require('../../utils') +const first = require('it-first') +const last = require('it-last') +const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') module.exports = ({ ipld, preload }) => { - return withTimeoutOption(async function get (cid, path, options) { - [cid, path, options] = parseArgs(cid, path, options) + return withTimeoutOption(async function get (ipfsPath, options = {}) { + const { + cid, + path + } = toCidAndPath(ipfsPath) + + if (path) { + options.path = path + } if (options.preload !== false) { preload(cid) } - if (path == null || path === '/') { - const value = await ipld.get(cid, options) - - return { - value, - remainderPath: '' + if (options.path) { + if (options.localResolve) { + return first(ipld.resolve(cid, options.path)) } - } else { - let result - - for await (const entry of ipld.resolve(cid, path)) { - if (options.localResolve) { - return entry - } - result = entry - } + return last(ipld.resolve(cid, options.path)) + } - return result + return { + value: await ipld.get(cid, options), + remainderPath: '' } }) } diff --git a/packages/ipfs/src/core/components/dag/resolve.js b/packages/ipfs/src/core/components/dag/resolve.js index dd863dd111..5c8e747084 100644 --- a/packages/ipfs/src/core/components/dag/resolve.js +++ b/packages/ipfs/src/core/components/dag/resolve.js @@ -1,16 +1,23 @@ 'use strict' -const { parseArgs } = require('./utils') const { withTimeoutOption } = require('../../utils') +const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') module.exports = ({ ipld, preload }) => { - return withTimeoutOption(async function * resolve (cid, path, options) { // eslint-disable-line require-await - [cid, path, options] = parseArgs(cid, path, options) + return withTimeoutOption(async function * resolve (ipfsPath, options) { // eslint-disable-line require-await + const { + cid, + path + } = toCidAndPath(ipfsPath) + + if (path) { + options.path = path + } if (options.preload !== false) { preload(cid) } - yield * ipld.resolve(cid, path, { signal: options.signal }) + yield * ipld.resolve(cid, options.path, { signal: options.signal }) }) } diff --git a/packages/ipfs/src/core/components/dag/tree.js b/packages/ipfs/src/core/components/dag/tree.js index bab3380ece..0f0c223df1 100644 --- a/packages/ipfs/src/core/components/dag/tree.js +++ b/packages/ipfs/src/core/components/dag/tree.js @@ -1,16 +1,23 @@ 'use strict' -const { parseArgs } = require('./utils') const { withTimeoutOption } = require('../../utils') +const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') module.exports = ({ ipld, preload }) => { - return withTimeoutOption(async function * tree (cid, path, options) { // eslint-disable-line require-await - [cid, path, options] = parseArgs(cid, path, options) + return withTimeoutOption(async function * tree (ipfsPath, options = {}) { // eslint-disable-line require-await + const { + cid, + path + } = toCidAndPath(ipfsPath) + + if (path) { + options.path = path + } if (options.preload !== false) { preload(cid) } - yield * ipld.tree(cid, path, options) + yield * ipld.tree(cid, options.path, options) }) } diff --git a/packages/ipfs/src/core/components/dag/utils.js b/packages/ipfs/src/core/components/dag/utils.js deleted file mode 100644 index 7f417223db..0000000000 --- a/packages/ipfs/src/core/components/dag/utils.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict' - -const CID = require('cids') -const errCode = require('err-code') -const { Buffer } = require('buffer') - -exports.parseArgs = (cid, path, options) => { - options = options || {} - - // Allow options in path position - if (path !== undefined && typeof path !== 'string') { - options = path - path = undefined - } - - if (typeof cid === 'string') { - if (cid.startsWith('/ipfs/')) { - cid = cid.substring(6) - } - - const split = cid.split('/') - - try { - cid = new CID(split[0]) - } catch (err) { - throw errCode(err, 'ERR_INVALID_CID') - } - - split.shift() - - if (split.length > 0) { - path = split.join('/') - } else { - path = path || '/' - } - } else if (Buffer.isBuffer(cid)) { - try { - cid = new CID(cid) - } catch (err) { - throw errCode(err, 'ERR_INVALID_CID') - } - } - - return [ - cid, - path, - options - ] -} diff --git a/packages/ipfs/src/core/components/files/flush.js b/packages/ipfs/src/core/components/files/flush.js index 899e3d9711..f3c5798cc2 100644 --- a/packages/ipfs/src/core/components/files/flush.js +++ b/packages/ipfs/src/core/components/files/flush.js @@ -9,16 +9,11 @@ const defaultOptions = { } module.exports = (context) => { - return withTimeoutOption(async function mfsFlush (path = '/', options = defaultOptions) { - if (path && typeof path !== 'string') { - options = path - path = '/' - } - + return withTimeoutOption(async function mfsFlush (path, options = {}) { options = applyDefaultOptions(options, defaultOptions) - const result = await stat(context)(path, options) + const { cid } = await stat(context)(path, options) - return result.cid + return cid }) } diff --git a/packages/ipfs/src/core/components/files/ls.js b/packages/ipfs/src/core/components/files/ls.js index 73f63fdb6d..0792efddf0 100644 --- a/packages/ipfs/src/core/components/files/ls.js +++ b/packages/ipfs/src/core/components/files/ls.js @@ -39,12 +39,7 @@ const toOutput = (fsEntry) => { } module.exports = (context) => { - return withTimeoutOption(async function * mfsLs (path = '/', options = {}) { - if (typeof path === 'object' && !(path instanceof String)) { - options = path - path = '/' - } - + return withTimeoutOption(async function * mfsLs (path, options = {}) { const mfsPath = await toMfsPath(context, path, options) const fsDir = await exporter(mfsPath.mfsPath, context.ipld) diff --git a/packages/ipfs/src/core/components/files/utils/to-mfs-path.js b/packages/ipfs/src/core/components/files/utils/to-mfs-path.js index ca218febfa..c5a23828af 100644 --- a/packages/ipfs/src/core/components/files/utils/to-mfs-path.js +++ b/packages/ipfs/src/core/components/files/utils/to-mfs-path.js @@ -4,6 +4,7 @@ const loadMfsRoot = require('./with-mfs-root') const toPathComponents = require('./to-path-components') const exporter = require('ipfs-unixfs-exporter') const errCode = require('err-code') +const CID = require('cids') const IPFS_PREFIX = 'ipfs' @@ -13,6 +14,10 @@ const toMfsPath = async (context, path, options) => { const root = await loadMfsRoot(context, options) paths = paths.map(path => { + if (CID.isCID(path)) { + path = `/ipfs/${path}` + } + path = (path || '').trim() path = path.replace(/(\/\/+)/g, '/') diff --git a/packages/ipfs/src/core/components/index.js b/packages/ipfs/src/core/components/index.js index 41f7d971f3..6b59eaf151 100644 --- a/packages/ipfs/src/core/components/index.js +++ b/packages/ipfs/src/core/components/index.js @@ -10,11 +10,14 @@ exports.block = { exports.bitswap = { stat: require('./bitswap/stat'), unwant: require('./bitswap/unwant'), - wantlist: require('./bitswap/wantlist') + wantlist: require('./bitswap/wantlist'), + wantlistForPeer: require('./bitswap/wantlist-for-peer') } exports.bootstrap = { add: require('./bootstrap/add'), + clear: require('./bootstrap/clear'), list: require('./bootstrap/list'), + reset: require('./bootstrap/reset'), rm: require('./bootstrap/rm') } exports.cat = require('./cat') diff --git a/packages/ipfs/src/core/components/init.js b/packages/ipfs/src/core/components/init.js index 7a0213eaea..40dc7c2363 100644 --- a/packages/ipfs/src/core/components/init.js +++ b/packages/ipfs/src/core/components/init.js @@ -323,7 +323,8 @@ function createApi ({ bitswap: { stat: notStarted, unwant: notStarted, - wantlist: notStarted + wantlist: notStarted, + wantlistForPeer: notStarted }, bootstrap: { add: Components.bootstrap.add({ repo }), diff --git a/packages/ipfs/src/core/components/object/new.js b/packages/ipfs/src/core/components/object/new.js index 935de73dbc..4c0ebaedee 100644 --- a/packages/ipfs/src/core/components/object/new.js +++ b/packages/ipfs/src/core/components/object/new.js @@ -8,19 +8,11 @@ const { withTimeoutOption } = require('../../utils') const { Buffer } = require('buffer') module.exports = ({ ipld, preload }) => { - return withTimeoutOption(async function _new (template, options) { - options = options || {} - - // allow options in the template position - if (template && typeof template !== 'string') { - options = template - template = null - } - + return withTimeoutOption(async function _new (options = {}) { let data - if (template) { - if (template === 'unixfs-dir') { + if (options.template) { + if (options.template === 'unixfs-dir') { data = (new Unixfs('directory')).marshal() } else { throw new Error('unknown template') @@ -33,7 +25,8 @@ module.exports = ({ ipld, preload }) => { const cid = await ipld.put(node, multicodec.DAG_PB, { cidVersion: 0, - hashAlg: multicodec.SHA2_256 + hashAlg: multicodec.SHA2_256, + signal: options.signal }) if (options.preload !== false) { diff --git a/packages/ipfs/src/core/components/pin/ls.js b/packages/ipfs/src/core/components/pin/ls.js index 11d4f89bb1..7240a1ac35 100644 --- a/packages/ipfs/src/core/components/pin/ls.js +++ b/packages/ipfs/src/core/components/pin/ls.js @@ -11,16 +11,9 @@ const { withTimeoutOption } = require('../../utils') const PIN_LS_CONCURRENCY = 8 module.exports = ({ pinManager, dag }) => { - return withTimeoutOption(async function * ls (paths, options) { - options = options || {} - + return withTimeoutOption(async function * ls (options = {}) { let type = PinTypes.all - if (paths && !Array.isArray(paths) && !CID.isCID(paths) && typeof paths !== 'string') { - options = paths - paths = null - } - if (options.type) { type = options.type if (typeof options.type === 'string') { @@ -32,17 +25,17 @@ module.exports = ({ pinManager, dag }) => { } } - if (paths) { - paths = Array.isArray(paths) ? paths : [paths] + if (options.paths) { + options.paths = Array.isArray(options.paths) ? options.paths : [options.paths] // check the pinned state of specific hashes - const cids = await resolvePath(dag, paths) + const cids = await resolvePath(dag, options.paths) yield * parallelMap(PIN_LS_CONCURRENCY, async cid => { const { reason, pinned } = await pinManager.isPinnedWithType(cid, type) if (!pinned) { - throw new Error(`path '${paths[cids.indexOf(cid)]}' is not pinned`) + throw new Error(`path '${options.paths[cids.indexOf(cid)]}' is not pinned`) } if (reason === PinTypes.direct || reason === PinTypes.recursive) { diff --git a/packages/ipfs/src/core/components/pin/pin-manager.js b/packages/ipfs/src/core/components/pin/pin-manager.js index d2e4cb961a..edcc887dcd 100644 --- a/packages/ipfs/src/core/components/pin/pin-manager.js +++ b/packages/ipfs/src/core/components/pin/pin-manager.js @@ -154,7 +154,7 @@ class PinManager { } const mh = await this.repo.datastore.get(PIN_DS_KEY) - const pinRoot = await this.dag.get(new CID(mh), '', { preload: false }) + const pinRoot = await this.dag.get(new CID(mh), { preload: false }) const [ rKeys, dKeys diff --git a/packages/ipfs/src/core/components/pin/pin-set.js b/packages/ipfs/src/core/components/pin/pin-set.js index 77a54ccc07..8f1e17a7a8 100644 --- a/packages/ipfs/src/core/components/pin/pin-set.js +++ b/packages/ipfs/src/core/components/pin/pin-set.js @@ -261,7 +261,7 @@ exports = module.exports = function (dag) { throw new Error('No link found with name ' + name) } - const res = await dag.get(link.Hash, '', { preload: false }) + const res = await dag.get(link.Hash, { preload: false }) const keys = [] const stepPin = link => keys.push(link.Hash) @@ -284,7 +284,7 @@ exports = module.exports = function (dag) { stepBin(link, idx, pbh.data) // walk the links of this fanout bin - const res = await dag.get(linkHash, '', { preload: false }) + const res = await dag.get(link.Hash, { preload: false }) await pinSet.walkItems(res.value, { stepPin, stepBin }) } @@ -305,7 +305,7 @@ exports = module.exports = function (dag) { for (const topLevelLink of rootNode.Links) { cids.push(topLevelLink.Hash) - const res = await dag.get(topLevelLink.Hash, '', { preload: false }) + const res = await dag.get(topLevelLink.Hash, { preload: false }) await pinSet.walkItems(res.value, { stepBin }) } diff --git a/packages/ipfs/src/core/components/start.js b/packages/ipfs/src/core/components/start.js index 54b31f2842..215e19e0b6 100644 --- a/packages/ipfs/src/core/components/start.js +++ b/packages/ipfs/src/core/components/start.js @@ -240,12 +240,15 @@ function createApi ({ bitswap: { stat: Components.bitswap.stat({ bitswap }), unwant: Components.bitswap.unwant({ bitswap }), - wantlist: Components.bitswap.wantlist({ bitswap }) + wantlist: Components.bitswap.wantlist({ bitswap }), + wantlistForPeer: Components.bitswap.wantlistForPeer({ bitswap }) }, block, bootstrap: { add: Components.bootstrap.add({ repo }), + clear: Components.bootstrap.clear({ repo }), list: Components.bootstrap.list({ repo }), + reset: Components.bootstrap.reset({ repo }), rm: Components.bootstrap.rm({ repo }) }, cat: Components.cat({ ipld, preload }), diff --git a/packages/ipfs/src/core/components/stop.js b/packages/ipfs/src/core/components/stop.js index fdd9756637..5463a160c0 100644 --- a/packages/ipfs/src/core/components/stop.js +++ b/packages/ipfs/src/core/components/stop.js @@ -127,12 +127,15 @@ function createApi ({ bitswap: { stat: notStarted, unwant: notStarted, - wantlist: notStarted + wantlist: notStarted, + wantlistForPeer: notStarted }, block, bootstrap: { add: Components.bootstrap.add({ repo }), + clear: Components.bootstrap.clear({ repo }), list: Components.bootstrap.list({ repo }), + reset: Components.bootstrap.reset({ repo }), rm: Components.bootstrap.rm({ repo }) }, cat: Components.cat({ ipld, preload }), diff --git a/packages/ipfs/src/core/utils.js b/packages/ipfs/src/core/utils.js index 03ee734996..8e2e4507ae 100644 --- a/packages/ipfs/src/core/utils.js +++ b/packages/ipfs/src/core/utils.js @@ -10,6 +10,7 @@ const Key = require('interface-datastore').Key const { TimeoutError } = require('./errors') const ERR_BAD_PATH = 'ERR_BAD_PATH' + exports.OFFLINE_ERROR = 'This command must be run in online mode. Try running \'ipfs daemon\' first.' exports.MFS_FILE_TYPES = { diff --git a/packages/ipfs/src/http/api/resources/bitswap.js b/packages/ipfs/src/http/api/resources/bitswap.js index 3df56a791f..8d5bbba9dd 100644 --- a/packages/ipfs/src/http/api/resources/bitswap.js +++ b/packages/ipfs/src/http/api/resources/bitswap.js @@ -38,10 +38,19 @@ exports.wantlist = { } } = request - const list = await ipfs.bitswap.wantlist(peer, { - signal, - timeout - }) + let list + + if (peer) { + list = await ipfs.bitswap.wantlistForPeer(peer, { + signal, + timeout + }) + } else { + list = await ipfs.bitswap.wantlist({ + signal, + timeout + }) + } return h.response({ Keys: list.map(cid => ({ diff --git a/packages/ipfs/src/http/api/resources/bootstrap.js b/packages/ipfs/src/http/api/resources/bootstrap.js index 9021943b65..541f6d9a2b 100644 --- a/packages/ipfs/src/http/api/resources/bootstrap.js +++ b/packages/ipfs/src/http/api/resources/bootstrap.js @@ -1,5 +1,6 @@ 'use strict' +const Boom = require('@hapi/boom') const Joi = require('../../utils/joi') exports.list = { @@ -72,11 +73,22 @@ exports.add = { } } = request - const list = await ipfs.bootstrap.add(addr, { - default: def || !addr, - signal, - timeout - }) + let list + + if (def) { + list = await ipfs.bootstrap.reset({ + signal, + timeout + }) + } else if (addr) { + list = await ipfs.bootstrap.add(addr, { + signal, + timeout + }) + } else { + throw Boom.badRequest('arg is required') + } + return h.response(list) } } @@ -108,8 +120,7 @@ exports.addDefault = { } } = request - const list = await ipfs.bootstrap.add(null, { - default: true, + const list = await ipfs.bootstrap.reset({ signal, timeout }) @@ -152,11 +163,22 @@ exports.rm = { } } = request - const list = await ipfs.bootstrap.rm(addr, { - all: all || !addr, - signal, - timeout - }) + let list + + if (all) { + list = await ipfs.bootstrap.clear({ + signal, + timeout + }) + } else if (addr) { + list = await ipfs.bootstrap.rm(addr, { + signal, + timeout + }) + } else { + throw Boom.badRequest('arg is required') + } + return h.response(list) } } @@ -188,11 +210,11 @@ exports.rmAll = { } } = request - const list = await ipfs.bootstrap.rm(null, { - all: true, + const list = await ipfs.bootstrap.clear({ signal, timeout }) + return h.response(list) } } diff --git a/packages/ipfs/src/http/api/resources/dag.js b/packages/ipfs/src/http/api/resources/dag.js index 7bf05cfb88..4faaf9f728 100644 --- a/packages/ipfs/src/http/api/resources/dag.js +++ b/packages/ipfs/src/http/api/resources/dag.js @@ -125,7 +125,8 @@ exports.get = { let result try { - result = await ipfs.dag.get(cid, path, { + result = await ipfs.dag.get(cid, { + path, signal, timeout }) @@ -296,7 +297,8 @@ exports.resolve = { query: Joi.object().keys({ arg: Joi.cidAndPath().required(), cidBase: Joi.cidBase(), - timeout: Joi.timeout() + timeout: Joi.timeout(), + path: Joi.string() }) .rename('cid-base', 'cidBase', { override: true, @@ -320,7 +322,8 @@ exports.resolve = { path }, cidBase, - timeout + timeout, + path: queryPath } } = request @@ -328,10 +331,11 @@ exports.resolve = { // along with the path inside that node as the remainder path try { let lastCid = cid - let lastRemainderPath = path + let lastRemainderPath = path || queryPath - if (path) { - for await (const { value, remainderPath } of ipfs.dag.resolve(lastCid, path, { + if (lastRemainderPath) { + for await (const { value, remainderPath } of ipfs.dag.resolve(lastCid, { + path: lastRemainderPath, signal, timeout })) { diff --git a/packages/ipfs/src/http/api/resources/object.js b/packages/ipfs/src/http/api/resources/object.js index 506aa4c6e2..4b2067234d 100644 --- a/packages/ipfs/src/http/api/resources/object.js +++ b/packages/ipfs/src/http/api/resources/object.js @@ -87,7 +87,8 @@ exports.new = { let cid, node try { - cid = await ipfs.object.new(template, { + cid = await ipfs.object.new({ + template, signal, timeout }) diff --git a/packages/ipfs/src/http/api/resources/pin.js b/packages/ipfs/src/http/api/resources/pin.js index eadfc45ebb..b97ce5e8a5 100644 --- a/packages/ipfs/src/http/api/resources/pin.js +++ b/packages/ipfs/src/http/api/resources/pin.js @@ -52,13 +52,16 @@ exports.ls = { } } = request + const source = ipfs.pin.ls({ + paths, + type, + signal, + timeout + }) + if (!stream) { const res = await pipe( - ipfs.pin.ls(paths, { - type, - signal, - timeout - }), + source, reduce((res, { type, cid }) => { res.Keys[cidToString(cid, { base: cidBase })] = { Type: type } return res @@ -69,11 +72,7 @@ exports.ls = { } return streamResponse(request, h, () => pipe( - ipfs.pin.ls(paths, { - type, - signal, - timeout - }), + source, map(({ type, cid }) => ({ Type: type, Cid: cidToString(cid, { base: cidBase }) })), ndjson.stringify )) diff --git a/packages/ipfs/src/http/utils/joi.js b/packages/ipfs/src/http/utils/joi.js index e0384b85e3..360b402cb6 100644 --- a/packages/ipfs/src/http/utils/joi.js +++ b/packages/ipfs/src/http/utils/joi.js @@ -5,6 +5,7 @@ const CID = require('cids') const parseDuration = require('parse-duration').default const multiaddr = require('multiaddr') const multibase = require('multibase') +const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') const toIpfsPath = (value) => { if (!value) { @@ -112,19 +113,7 @@ module.exports = Joi return } - value = value.toString() - - if (!value.startsWith('/ipfs/')) { - value = `/ipfs/${value}` - } - - // section after /ipfs/ should be a valid CID - const parts = value.split('/') - - return { - cid: new CID(parts[2]), - path: parts.slice(3).join('/') - } + return toCidAndPath(value) } } }, diff --git a/packages/ipfs/test/cli/bitswap.js b/packages/ipfs/test/cli/bitswap.js index a366ac7ba3..e8b48bd630 100644 --- a/packages/ipfs/test/cli/bitswap.js +++ b/packages/ipfs/test/cli/bitswap.js @@ -18,6 +18,7 @@ describe('bitswap', () => { ipfs = { bitswap: { wantlist: sinon.stub(), + wantlistForPeer: sinon.stub(), stat: sinon.stub(), unwant: sinon.stub() } @@ -30,7 +31,7 @@ describe('bitswap', () => { } it('should return the wantlist', async () => { - ipfs.bitswap.wantlist.withArgs(undefined, defaultOptions).resolves([ + ipfs.bitswap.wantlist.withArgs(defaultOptions).resolves([ new CID(key0), new CID(key1) ]) @@ -41,7 +42,9 @@ describe('bitswap', () => { }) it('should get wantlist with CIDs encoded in specified base', async () => { - ipfs.bitswap.wantlist.withArgs(undefined, defaultOptions).resolves([ + ipfs.bitswap.wantlist.withArgs({ + ...defaultOptions + }).resolves([ new CID(key0), new CID(key1) ]) @@ -51,14 +54,24 @@ describe('bitswap', () => { }) it('wantlist peerid', async () => { - ipfs.bitswap.wantlist.withArgs(peerId, defaultOptions).resolves([]) + ipfs.bitswap.wantlistForPeer.withArgs(peerId, defaultOptions).resolves([]) const out = await cli(`bitswap wantlist ${peerId}`, { ipfs }) expect(out).to.eql('') }) it('wantlist with a timeout', async () => { - ipfs.bitswap.wantlist.withArgs(peerId, { + ipfs.bitswap.wantlist.withArgs({ + ...defaultOptions, + timeout: 1000 + }).resolves([]) + + const out = await cli('bitswap wantlist --timeout=1s', { ipfs }) + expect(out).to.eql('') + }) + + it('wantlist for peer with a timeout', async () => { + ipfs.bitswap.wantlistForPeer.withArgs(peerId, { ...defaultOptions, timeout: 1000 }).resolves([]) diff --git a/packages/ipfs/test/cli/bootstrap.js b/packages/ipfs/test/cli/bootstrap.js index b0a72da840..0d1ba24951 100644 --- a/packages/ipfs/test/cli/bootstrap.js +++ b/packages/ipfs/test/cli/bootstrap.js @@ -14,7 +14,9 @@ describe('bootstrap', () => { bootstrap: { add: sinon.stub(), list: sinon.stub(), - rm: sinon.stub() + rm: sinon.stub(), + clear: sinon.stub(), + reset: sinon.stub() } } }) @@ -45,15 +47,11 @@ describe('bootstrap', () => { describe('add', () => { const defaultOptions = { - default: false, timeout: undefined } it('add default', async () => { - ipfs.bootstrap.add.withArgs(undefined, { - ...defaultOptions, - default: true - }).returns({ + ipfs.bootstrap.reset.withArgs(defaultOptions).returns({ Peers: defaultList }) @@ -112,7 +110,6 @@ describe('bootstrap', () => { describe('rm', () => { const defaultOptions = { - all: false, timeout: undefined } @@ -128,10 +125,7 @@ describe('bootstrap', () => { }) it('should remove all bootstrap nodes', async () => { - ipfs.bootstrap.rm.withArgs(undefined, { - ...defaultOptions, - all: true - }).returns({ + ipfs.bootstrap.clear.withArgs(defaultOptions).returns({ Peers: defaultList }) diff --git a/packages/ipfs/test/cli/dag.js b/packages/ipfs/test/cli/dag.js index 899a4a1c71..7a9694363d 100644 --- a/packages/ipfs/test/cli/dag.js +++ b/packages/ipfs/test/cli/dag.js @@ -26,18 +26,50 @@ describe('dag', () => { describe('get', () => { const defaultOptions = { localResolve: false, - timeout: undefined + timeout: undefined, + path: undefined } it('should get a node', async () => { - const path = 'parentHash' const result = { value: Buffer.from('hello world') } - ipfs.dag.get.withArgs(cid, path, defaultOptions).returns(result) + ipfs.dag.get.withArgs(cid, defaultOptions).returns(result) + + const out = await cli(`dag get ${cid}`, { ipfs }) + + expect(out).to.be.eql('0x' + result.value.toString('hex') + '\n') + }) + + it('should get a node with a deep path', async () => { + const path = '/parentHash' + const result = { + value: Buffer.from('hello world') + } + + ipfs.dag.get.withArgs(cid, { + ...defaultOptions, + path + }).returns(result) + + const out = await cli(`dag get ${cid}${path}`, { ipfs }) + + expect(out).to.be.eql('0x' + result.value.toString('hex') + '\n') + }) + + it('should get a node with a deep path and an ipfs prefix', async () => { + const path = '/parentHash' + const result = { + value: Buffer.from('hello world') + } + + ipfs.dag.get.withArgs(cid, { + ...defaultOptions, + path + }).returns(result) - const out = await cli(`dag get ${cid}/${path}`, { ipfs }) + const out = await cli(`dag get /ipfs/${cid}${path}`, { ipfs }) expect(out).to.be.eql('0x' + result.value.toString('hex') + '\n') }) @@ -47,7 +79,7 @@ describe('dag', () => { value: Buffer.from('hello world') } - ipfs.dag.get.withArgs(cid, '', { + ipfs.dag.get.withArgs(cid, { ...defaultOptions, localResolve: true }).returns(result) @@ -64,7 +96,7 @@ describe('dag', () => { value: Buffer.from('hello world') } - ipfs.dag.get.withArgs(cid, '', { + ipfs.dag.get.withArgs(cid, { ...defaultOptions, timeout: 1000 }).returns(result) diff --git a/packages/ipfs/test/cli/object.js b/packages/ipfs/test/cli/object.js index 051009c03c..a52339df9b 100644 --- a/packages/ipfs/test/cli/object.js +++ b/packages/ipfs/test/cli/object.js @@ -39,25 +39,29 @@ describe('object', () => { describe('new', () => { const defaultOptions = { + template: undefined, timeout: undefined } it('should create a new object', async () => { - ipfs.object.new.withArgs(undefined, defaultOptions).resolves(cid) + ipfs.object.new.withArgs(defaultOptions).resolves(cid) const out = await cli('object new', { ipfs }) expect(out).to.equal(`${cid}\n`) }) it('new unixfs-dir', async () => { - ipfs.object.new.withArgs('unixfs-dir', defaultOptions).resolves(cid) + ipfs.object.new.withArgs({ + ...defaultOptions, + template: 'unixfs-dir' + }).resolves(cid) const out = await cli('object new unixfs-dir', { ipfs }) expect(out).to.equal(`${cid}\n`) }) it('new with a timeout', async () => { - ipfs.object.new.withArgs(undefined, { + ipfs.object.new.withArgs({ ...defaultOptions, timeout: 1000 }).resolves(cid) @@ -67,7 +71,7 @@ describe('object', () => { }) it('should new and print CID encoded in specified base', async () => { - ipfs.object.new.withArgs(undefined, defaultOptions).resolves(cid.toV1()) + ipfs.object.new.withArgs(defaultOptions).resolves(cid.toV1()) const out = await cli('object new --cid-base=base64', { ipfs }) expect(out).to.equal( diff --git a/packages/ipfs/test/cli/pin.js b/packages/ipfs/test/cli/pin.js index 864805976d..3e74992abe 100644 --- a/packages/ipfs/test/cli/pin.js +++ b/packages/ipfs/test/cli/pin.js @@ -26,7 +26,8 @@ describe('pin', () => { pin: { rm: sinon.stub(), add: sinon.stub(), - ls: sinon.stub() + ls: sinon.stub(), + query: sinon.stub() } } }) @@ -159,11 +160,12 @@ describe('pin', () => { const defaultOptions = { type: 'all', stream: false, - timeout: undefined + timeout: undefined, + paths: undefined } it('lists all pins when no hash is passed', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid: new CID(pins.root), type: 'recursive' }]) @@ -173,7 +175,10 @@ describe('pin', () => { }) it('handles multiple hashes', async () => { - ipfs.pin.ls.withArgs([pins.root, pins.solarWiki], defaultOptions).returns([{ + ipfs.pin.ls.withArgs({ + ...defaultOptions, + paths: [pins.root, pins.solarWiki] + }).returns([{ cid: new CID(pins.root), type: 'recursive' }, { @@ -186,7 +191,7 @@ describe('pin', () => { }) it('can print quietly', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid: new CID(pins.root), type: 'recursive' }]) @@ -196,7 +201,7 @@ describe('pin', () => { }) it('can print quietly (short option)', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid: new CID(pins.root), type: 'recursive' }]) @@ -206,7 +211,7 @@ describe('pin', () => { }) it('should ls and print CIDs encoded in specified base', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid: new CID(pins.root).toV1(), type: 'recursive' }]) @@ -216,7 +221,7 @@ describe('pin', () => { }) it('lists all pins with a timeout', async () => { - ipfs.pin.ls.withArgs(undefined, { + ipfs.pin.ls.withArgs({ ...defaultOptions, timeout: 1000 }).returns([{ diff --git a/packages/ipfs/test/core/pin.js b/packages/ipfs/test/core/pin.js index e9c8ecda0c..01f45f30ad 100644 --- a/packages/ipfs/test/core/pin.js +++ b/packages/ipfs/test/core/pin.js @@ -46,7 +46,7 @@ describe('pin', function () { async function isPinnedWithType (path, type) { try { - for await (const _ of pin.ls(path, { type })) { // eslint-disable-line no-unused-vars + for await (const _ of pin.ls({ paths: path, type })) { // eslint-disable-line no-unused-vars return true } return false @@ -199,7 +199,7 @@ describe('pin', function () { }) it('should list pins of a particular CID', async () => { - const out = await all(pin.ls(pins.mercuryDir)) + const out = await all(pin.ls({ paths: pins.mercuryDir })) expect(out[0].cid.toString()).to.eql(pins.mercuryDir) }) @@ -274,7 +274,7 @@ describe('pin', function () { }) it('should list direct pins for CID', async () => { - const out = await all(pin.ls(pins.mercuryDir, { type: 'direct' })) + const out = await all(pin.ls({ paths: pins.mercuryDir, type: 'direct' })) expect(out).to.have.deep.members([ { @@ -285,7 +285,7 @@ describe('pin', function () { }) it('should list direct pins for path', async () => { - const out = await all(pin.ls(`/ipfs/${pins.root}/mercury/`, { type: 'direct' })) + const out = await all(pin.ls({ paths: `/ipfs/${pins.root}/mercury/`, type: 'direct' })) expect(out).to.have.deep.members([ { @@ -296,17 +296,17 @@ describe('pin', function () { }) it('should list direct pins for path (no match)', () => { - return expect(all(pin.ls(`/ipfs/${pins.root}/mercury/wiki.md`, { type: 'direct' }))) + return expect(all(pin.ls({ paths: `/ipfs/${pins.root}/mercury/wiki.md`, type: 'direct' }))) .to.eventually.be.rejected() }) it('should list direct pins for CID (no match)', () => { - return expect(all(pin.ls(pins.root, { type: 'direct' }))) + return expect(all(pin.ls({ paths: pins.root, type: 'direct' }))) .to.eventually.be.rejected() }) it('should list recursive pins for CID', async () => { - const out = await all(pin.ls(pins.root, { type: 'recursive' })) + const out = await all(pin.ls({ paths: pins.root, type: 'recursive' })) expect(out).to.have.deep.members([ { @@ -317,12 +317,12 @@ describe('pin', function () { }) it('should list recursive pins for CID (no match)', () => { - return expect(all(pin.ls(pins.mercuryDir, { type: 'recursive' }))) + return expect(all(pin.ls({ paths: pins.mercuryDir, type: 'recursive' }))) .to.eventually.be.rejected() }) it('should list indirect pins for CID', async () => { - const out = await all(pin.ls(pins.solarWiki, { type: 'indirect' })) + const out = await all(pin.ls({ paths: pins.solarWiki, type: 'indirect' })) expect(out).to.have.deep.members([ { @@ -333,7 +333,7 @@ describe('pin', function () { }) it('should list indirect pins for CID (no match)', () => { - return expect(all(pin.ls(pins.root, { type: 'indirect' }))) + return expect(all(pin.ls({ paths: pins.root, type: 'indirect' }))) .to.eventually.be.rejected() }) }) diff --git a/packages/ipfs/test/core/preload.spec.js b/packages/ipfs/test/core/preload.spec.js index a6c1308f5b..8e57233e81 100644 --- a/packages/ipfs/test/core/preload.spec.js +++ b/packages/ipfs/test/core/preload.spec.js @@ -201,7 +201,7 @@ describe('preload', () => { it('should preload content retrieved with object.get', async function () { this.timeout(50 * 1000) - const cid = await ipfs.object.new(null, { preload: false }) + const cid = await ipfs.object.new({ preload: false }) await ipfs.object.get(cid) await MockPreloadNode.waitForCids(cid) }) diff --git a/packages/ipfs/test/http-api/inject/bitswap.js b/packages/ipfs/test/http-api/inject/bitswap.js index 6c2716a896..e4e29d15ad 100644 --- a/packages/ipfs/test/http-api/inject/bitswap.js +++ b/packages/ipfs/test/http-api/inject/bitswap.js @@ -16,6 +16,7 @@ describe('/bitswap', () => { ipfs = { bitswap: { wantlist: sinon.stub(), + wantlistForPeer: sinon.stub(), stat: sinon.stub(), unwant: sinon.stub() } @@ -33,7 +34,7 @@ describe('/bitswap', () => { }) it('/wantlist', async () => { - ipfs.bitswap.wantlist.withArgs(undefined, defaultOptions).returns([ + ipfs.bitswap.wantlist.withArgs(defaultOptions).returns([ cid ]) @@ -47,7 +48,7 @@ describe('/bitswap', () => { }) it('/wantlist?timeout=1s', async () => { - ipfs.bitswap.wantlist.withArgs(undefined, { + ipfs.bitswap.wantlist.withArgs({ ...defaultOptions, timeout: 1000 }).returns([ @@ -65,7 +66,7 @@ describe('/bitswap', () => { // TODO: unskip after switch to v1 CIDs by default it.skip('/wantlist?cid-base=base64', async () => { - ipfs.bitswap.wantlist.withArgs(undefined, defaultOptions).returns([ + ipfs.bitswap.wantlist.withArgs(defaultOptions).returns([ cid ]) @@ -79,7 +80,7 @@ describe('/bitswap', () => { }) it('/wantlist?cid-base=invalid', async () => { - ipfs.bitswap.wantlist.withArgs(undefined, defaultOptions).returns([ + ipfs.bitswap.wantlist.withArgs(defaultOptions).returns([ cid ]) @@ -95,7 +96,7 @@ describe('/bitswap', () => { it('/wantlist?peer=QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D', async () => { const peerId = 'QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D' - ipfs.bitswap.wantlist.withArgs(peerId, defaultOptions).returns([ + ipfs.bitswap.wantlistForPeer.withArgs(peerId, defaultOptions).returns([ cid ]) @@ -108,6 +109,25 @@ describe('/bitswap', () => { expect(res).to.have.nested.property('result.Keys').that.deep.includes({ '/': cid.toString() }) }) + it('/wantlist?peer=QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D&timeout=1s', async () => { + const peerId = 'QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D' + + ipfs.bitswap.wantlistForPeer.withArgs(peerId, { + ...defaultOptions, + timeout: 1000 + }).returns([ + cid + ]) + + const res = await http({ + method: 'POST', + url: `/api/v0/bitswap/wantlist?peer=${peerId}&timeout=1s` + }, { ipfs }) + + expect(res).to.have.property('statusCode', 200) + expect(res).to.have.nested.property('result.Keys').that.deep.includes({ '/': cid.toString() }) + }) + it('/wantlist?peer=invalid', async () => { const peerId = 'invalid' diff --git a/packages/ipfs/test/http-api/inject/bootstrap.js b/packages/ipfs/test/http-api/inject/bootstrap.js index f8d8e49a1c..999c8602d0 100644 --- a/packages/ipfs/test/http-api/inject/bootstrap.js +++ b/packages/ipfs/test/http-api/inject/bootstrap.js @@ -18,7 +18,9 @@ describe('/bootstrap', () => { bootstrap: { list: sinon.stub(), add: sinon.stub(), - rm: sinon.stub() + rm: sinon.stub(), + clear: sinon.stub(), + reset: sinon.stub() } } }) @@ -81,7 +83,6 @@ describe('/bootstrap', () => { describe('/add', () => { const defaultOptions = { - default: false, signal: sinon.match.instanceOf(AbortSignal), timeout: undefined } @@ -110,11 +111,27 @@ describe('/bootstrap', () => { expect(res).to.have.deep.nested.property('result.Peers', [validIp4]) }) - it('restores default', async () => { - ipfs.bootstrap.add.withArgs(undefined, { + it('adds a bootstrapper with a timeout', async () => { + ipfs.bootstrap.add.withArgs(validIp4, { ...defaultOptions, - default: true + timeout: 1000 }).returns({ + Peers: [ + validIp4 + ] + }) + + const res = await http({ + method: 'POST', + url: `/api/v0/bootstrap/add?arg=${validIp4}&timeout=1s` + }, { ipfs }) + + expect(res).to.have.property('statusCode', 200) + expect(res).to.have.deep.nested.property('result.Peers', [validIp4]) + }) + + it('restores default', async () => { + ipfs.bootstrap.reset.withArgs(defaultOptions).returns({ Peers: defaultList }) @@ -128,9 +145,8 @@ describe('/bootstrap', () => { }) it('accepts a timeout', async () => { - ipfs.bootstrap.add.withArgs(undefined, { + ipfs.bootstrap.reset.withArgs({ ...defaultOptions, - default: true, timeout: 1000 }).returns({ Peers: defaultList @@ -147,7 +163,6 @@ describe('/bootstrap', () => { describe('/default', () => { const defaultOptions = { - default: true, signal: sinon.match.instanceOf(AbortSignal), timeout: undefined } @@ -157,7 +172,7 @@ describe('/bootstrap', () => { }) it('restores default', async () => { - ipfs.bootstrap.add.withArgs(null, defaultOptions).returns({ + ipfs.bootstrap.reset.withArgs(defaultOptions).returns({ Peers: defaultList }) @@ -171,7 +186,7 @@ describe('/bootstrap', () => { }) it('accepts a timeout', async () => { - ipfs.bootstrap.add.withArgs(null, { + ipfs.bootstrap.reset.withArgs({ ...defaultOptions, timeout: 1000 }).returns({ @@ -191,7 +206,6 @@ describe('/bootstrap', () => { describe('/rm', () => { const defaultOptions = { - all: false, signal: sinon.match.instanceOf(AbortSignal), timeout: undefined } @@ -220,11 +234,27 @@ describe('/bootstrap', () => { expect(res).to.have.deep.nested.property('result.Peers', [validIp4]) }) - it('removes all bootstrappers', async () => { - ipfs.bootstrap.rm.withArgs(undefined, { + it('removes a bootstrapper with a timeout', async () => { + ipfs.bootstrap.rm.withArgs(validIp4, { ...defaultOptions, - all: true + timeout: 1000 }).returns({ + Peers: [ + validIp4 + ] + }) + + const res = await http({ + method: 'POST', + url: `/api/v0/bootstrap/rm?arg=${validIp4}&timeout=1s` + }, { ipfs }) + + expect(res).to.have.property('statusCode', 200) + expect(res).to.have.deep.nested.property('result.Peers', [validIp4]) + }) + + it('removes all bootstrappers', async () => { + ipfs.bootstrap.clear.withArgs(defaultOptions).returns({ Peers: defaultList }) @@ -238,9 +268,8 @@ describe('/bootstrap', () => { }) it('accepts a timeout', async () => { - ipfs.bootstrap.rm.withArgs(undefined, { + ipfs.bootstrap.clear.withArgs({ ...defaultOptions, - all: true, timeout: 1000 }).returns({ Peers: defaultList @@ -257,7 +286,6 @@ describe('/bootstrap', () => { describe('/all', () => { const defaultOptions = { - all: true, signal: sinon.match.instanceOf(AbortSignal), timeout: undefined } @@ -267,7 +295,7 @@ describe('/bootstrap', () => { }) it('removes all bootstrappers', async () => { - ipfs.bootstrap.rm.withArgs(null, defaultOptions).returns({ + ipfs.bootstrap.clear.withArgs(defaultOptions).returns({ Peers: defaultList }) @@ -281,7 +309,7 @@ describe('/bootstrap', () => { }) it('accepts a timeout', async () => { - ipfs.bootstrap.rm.withArgs(null, { + ipfs.bootstrap.clear.withArgs({ ...defaultOptions, timeout: 1000 }).returns({ diff --git a/packages/ipfs/test/http-api/inject/dag.js b/packages/ipfs/test/http-api/inject/dag.js index 170443563b..f3cbca2785 100644 --- a/packages/ipfs/test/http-api/inject/dag.js +++ b/packages/ipfs/test/http-api/inject/dag.js @@ -45,7 +45,8 @@ describe('/dag', () => { describe('/get', () => { const defaultOptions = { signal: sinon.match.instanceOf(AbortSignal), - timeout: undefined + timeout: undefined, + path: undefined } it('only accepts POST', () => { @@ -72,7 +73,7 @@ describe('/dag', () => { it('returns value', async () => { const node = new DAGNode(Buffer.from([]), []) - ipfs.dag.get.withArgs(cid, '', defaultOptions).returns({ value: node }) + ipfs.dag.get.withArgs(cid, defaultOptions).returns({ value: node }) const res = await http({ method: 'POST', @@ -86,7 +87,7 @@ describe('/dag', () => { it('uses text encoding for data by default', async () => { const node = new DAGNode(Buffer.from([0, 1, 2, 3]), []) - ipfs.dag.get.withArgs(cid, '', defaultOptions).returns({ value: node }) + ipfs.dag.get.withArgs(cid, defaultOptions).returns({ value: node }) const res = await http({ method: 'POST', @@ -101,7 +102,7 @@ describe('/dag', () => { it('overrides data encoding', async () => { const node = new DAGNode(Buffer.from([0, 1, 2, 3]), []) - ipfs.dag.get.withArgs(cid, '', defaultOptions).returns({ value: node }) + ipfs.dag.get.withArgs(cid, defaultOptions).returns({ value: node }) const res = await http({ method: 'POST', @@ -114,7 +115,10 @@ describe('/dag', () => { }) it('returns value with a path as part of the cid', async () => { - ipfs.dag.get.withArgs(cid, 'foo', defaultOptions).returns({ value: 'bar' }) + ipfs.dag.get.withArgs(cid, { + ...defaultOptions, + path: '/foo' + }).returns({ value: 'bar' }) const res = await http({ method: 'POST', @@ -127,7 +131,10 @@ describe('/dag', () => { it('returns value with a path as part of the cid for dag-pb nodes', async () => { const node = new DAGNode(Buffer.from([0, 1, 2, 3]), []) - ipfs.dag.get.withArgs(cid, 'Data', defaultOptions).returns({ value: node.Data }) + ipfs.dag.get.withArgs(cid, { + ...defaultOptions, + path: '/Data' + }).returns({ value: node.Data }) const res = await http({ method: 'POST', @@ -145,7 +152,7 @@ describe('/dag', () => { qux: Buffer.from([0, 1, 2, 3]) } } - ipfs.dag.get.withArgs(cid, '', defaultOptions).returns({ value: node }) + ipfs.dag.get.withArgs(cid, defaultOptions).returns({ value: node }) const res = await http({ method: 'POST', @@ -161,7 +168,7 @@ describe('/dag', () => { foo: 'bar', baz: Buffer.from([0, 1, 2, 3]) } - ipfs.dag.get.withArgs(cid, '', defaultOptions).returns({ value: node }) + ipfs.dag.get.withArgs(cid, defaultOptions).returns({ value: node }) const res = await http({ method: 'POST', @@ -176,7 +183,7 @@ describe('/dag', () => { const node = { foo: 'bar' } - ipfs.dag.get.withArgs(cid, '', { + ipfs.dag.get.withArgs(cid, { ...defaultOptions, timeout: 1000 }).returns({ value: node }) @@ -309,7 +316,8 @@ describe('/dag', () => { describe('/resolve', () => { const defaultOptions = { signal: sinon.match.instanceOf(AbortSignal), - timeout: undefined + timeout: undefined, + path: undefined } it('only accepts POST', () => { @@ -326,7 +334,7 @@ describe('/dag', () => { }) it('resolves a node', async () => { - ipfs.dag.resolve.withArgs(cid, '', defaultOptions).returns([{ + ipfs.dag.resolve.withArgs(cid, defaultOptions).returns([{ value: cid, remainderPath: '' }]) @@ -341,12 +349,32 @@ describe('/dag', () => { expect(res).to.have.nested.property('result.RemPath', '') }) + it('resolves a node with a path arg', async () => { + ipfs.dag.resolve.withArgs(cid, { + ...defaultOptions, + path: '/foo' + }).returns([{ + value: cid, + remainderPath: '' + }]) + + const res = await http({ + method: 'POST', + url: `/api/v0/dag/resolve?arg=${cid.toString()}&path=/foo` + }, { ipfs }) + + expect(res).to.have.property('statusCode', 200) + expect(res).to.have.deep.nested.property('result.Cid', { '/': cid.toString() }) + expect(res).to.have.nested.property('result.RemPath', '') + }) + it('returns the remainder path from within the resolved node', async () => { - ipfs.dag.resolve.withArgs(cid, 'foo', defaultOptions).returns([{ - value: { - value: cid, - remainderPath: 'foo' - } + ipfs.dag.resolve.withArgs(cid, { + ...defaultOptions, + path: '/foo' + }).returns([{ + value: cid, + remainderPath: 'foo' }]) const res = await http({ @@ -360,7 +388,10 @@ describe('/dag', () => { }) it('returns an error when the path is not available', async () => { - ipfs.dag.resolve.withArgs(cid, 'bar', defaultOptions).throws(new Error('Not found')) + ipfs.dag.resolve.withArgs(cid, { + ...defaultOptions, + path: '/bar' + }).throws(new Error('Not found')) const res = await http({ method: 'POST', @@ -374,7 +405,10 @@ describe('/dag', () => { it('resolves across multiple nodes, returning the CID of the last node traversed', async () => { const cid2 = new CID('QmUBdnXXPyoDFXj3Hj39dNJ5VkN3QFRskXxcGaYFBB8CNA') - ipfs.dag.resolve.withArgs(cid, 'foo/bar', defaultOptions).returns([{ + ipfs.dag.resolve.withArgs(cid, { + ...defaultOptions, + path: '/foo/bar' + }).returns([{ value: cid, remainderPath: 'foo' }, { @@ -393,7 +427,7 @@ describe('/dag', () => { }) it('accepts a timeout', async () => { - ipfs.dag.resolve.withArgs(cid, '', { + ipfs.dag.resolve.withArgs(cid, { ...defaultOptions, timeout: 1000 }).returns([{ diff --git a/packages/ipfs/test/http-api/inject/object.js b/packages/ipfs/test/http-api/inject/object.js index 65ecf9fd4c..5cacebeee8 100644 --- a/packages/ipfs/test/http-api/inject/object.js +++ b/packages/ipfs/test/http-api/inject/object.js @@ -63,7 +63,10 @@ describe('/object', () => { }) it('returns value', async () => { - ipfs.object.new.withArgs(undefined, defaultOptions).returns(cid) + ipfs.object.new.withArgs({ + ...defaultOptions, + template: undefined + }).returns(cid) ipfs.object.get.withArgs(cid, defaultOptions).returns(emptyDirectoryNode) const res = await http({ @@ -79,7 +82,10 @@ describe('/object', () => { it('should create an object with the passed template', async () => { const template = 'unixfs-dir' - ipfs.object.new.withArgs(template, defaultOptions).returns(cid) + ipfs.object.new.withArgs({ + ...defaultOptions, + template + }).returns(cid) ipfs.object.get.withArgs(cid, defaultOptions).returns(emptyDirectoryNode) const res = await http({ @@ -95,7 +101,10 @@ describe('/object', () => { it('should not reate an object with an invalid template', async () => { const template = 'derp' - ipfs.object.new.withArgs(template, defaultOptions).returns(cid) + ipfs.object.new.withArgs({ + ...defaultOptions, + template + }).returns(cid) ipfs.object.get.withArgs(cid, defaultOptions).returns(emptyDirectoryNode) const res = await http({ @@ -108,7 +117,10 @@ describe('/object', () => { // TODO: unskip after switch to v1 CIDs by default it.skip('should create a new object and return a base64 encoded CID', async () => { - ipfs.object.new.withArgs(undefined, defaultOptions).returns(cid) + ipfs.object.new.withArgs({ + ...defaultOptions, + template: undefined + }).returns(cid) ipfs.object.get.withArgs(cid, defaultOptions).returns(emptyDirectoryNode) const res = await http({ @@ -131,8 +143,9 @@ describe('/object', () => { }) it('accepts a timeout', async () => { - ipfs.object.new.withArgs(undefined, { + ipfs.object.new.withArgs({ ...defaultOptions, + template: undefined, timeout: 1000 }).returns(cid) ipfs.object.get.withArgs(cid, { diff --git a/packages/ipfs/test/http-api/inject/pin.js b/packages/ipfs/test/http-api/inject/pin.js index 367eafede6..5d636054fb 100644 --- a/packages/ipfs/test/http-api/inject/pin.js +++ b/packages/ipfs/test/http-api/inject/pin.js @@ -21,7 +21,8 @@ describe('/pin', () => { pin: { ls: sinon.stub(), add: sinon.stub(), - rm: sinon.stub() + rm: sinon.stub(), + query: sinon.stub() } } }) @@ -220,7 +221,8 @@ describe('/pin', () => { const defaultOptions = { type: 'all', signal: sinon.match.instanceOf(AbortSignal), - timeout: undefined + timeout: undefined, + paths: undefined } it('only accepts POST', () => { @@ -237,7 +239,7 @@ describe('/pin', () => { }) it('finds all pinned objects', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid, type: 'recursive' }]) @@ -252,7 +254,7 @@ describe('/pin', () => { }) it('finds all pinned objects streaming', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid: cid, type: 'recursive' }, { @@ -273,7 +275,10 @@ describe('/pin', () => { }) it('finds specific pinned objects', async () => { - ipfs.pin.ls.withArgs([`${cid}`], defaultOptions).returns([{ + ipfs.pin.ls.withArgs({ + ...defaultOptions, + paths: [`${cid}`] + }).returns([{ cid, type: 'recursive' }]) @@ -292,7 +297,7 @@ describe('/pin', () => { }) it('finds pins of type', async () => { - ipfs.pin.ls.withArgs(undefined, { + ipfs.pin.ls.withArgs({ ...defaultOptions, type: 'direct' }).returns([{ @@ -314,7 +319,7 @@ describe('/pin', () => { }) it('should list pins and return base64 encoded CIDs', async () => { - ipfs.pin.ls.withArgs(undefined, defaultOptions).returns([{ + ipfs.pin.ls.withArgs(defaultOptions).returns([{ cid, type: 'direct' }]) @@ -343,7 +348,7 @@ describe('/pin', () => { }) it('accepts a timeout', async () => { - ipfs.pin.ls.withArgs(undefined, { + ipfs.pin.ls.withArgs({ ...defaultOptions, timeout: 1000 }).returns([{