diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b6e5294..f0a01abc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## [0.65.9](https://github.com/ipfs/interface-ipfs-core/compare/v0.65.8...v0.65.9) (2018-05-16) + + +### Bug Fixes + +* add "files." to read* headers ([8b39b12](https://github.com/ipfs/interface-ipfs-core/commit/8b39b12)) +* linting warnings ([aae31b0](https://github.com/ipfs/interface-ipfs-core/commit/aae31b0)) + + +### Features + +* add utils to spawn multiple nodes and get their ID ([e77a2f6](https://github.com/ipfs/interface-ipfs-core/commit/e77a2f6)) + + + ## [0.65.8](https://github.com/ipfs/interface-ipfs-core/compare/v0.65.7...v0.65.8) (2018-05-15) diff --git a/SPEC/FILES.md b/SPEC/FILES.md index bd97ba3d5..b339d806f 100644 --- a/SPEC/FILES.md +++ b/SPEC/FILES.md @@ -19,6 +19,8 @@ * [files.stat](#filesmkdir) * [files.rm](#filesrm) * [files.read](#filesread) +* [files.readReadableStream](#filesreadreadablestream) +* [files.readPullStream](#filesreadpullstream) * [files.write](#fileswrite) * [files.mv](#filesmv) * [files.flush](#filesflush) @@ -599,8 +601,8 @@ The Mutable File System (MFS) is a virtual file system on top of IPFS that expos Where: -- `from` is the path of the source object to copy. -- `to` is the path of the destination object to copy to. +- `from` is the path of the source file to copy. +- `to` is the path of the destination file to copy to. `callback` must follow the `function (err) {}` signature, where `err` is an Error if the operation was not successful. @@ -700,7 +702,7 @@ ipfs.files.stat('/file.txt', (err, stats) => { Where: -- `path` is the path of the object to remove. +- `path` is the path of the file to remove. - `options` is an optional Object that might contain the following keys: - `recursive` is a Boolean value to decide whether or not to remove directories recursively. @@ -728,20 +730,20 @@ ipfs.files.rm('/my/beautiful/directory', { recursive: true }, (err) => { #### `files.read` -> Read a file. +> Read a file into a [`Buffer`][b]. ##### `Go` **WIP** -##### `JavaScript` - ipfs.files.read(path, [options, callback]) +##### `JavaScript` - ipfs.files.read(path, [options], [callback]) Where: -- `path` is the path of the object to read. +- `path` is the path of the file to read. - `options` is an optional Object that might contain the following keys: - `offset` is an Integer with the byte offset to begin reading from. - `count` is an Integer with the maximum number of bytes to read. -`callback` must follow the `function (err, buf) {}` signature, where `err` is an Error if the operation was not successful and `buf` is a Buffer with the contents of `path`. +`callback` must follow the `function (err, buf) {}` signature, where `err` is an Error if the operation was not successful and `buf` is a [`Buffer`][b] with the contents of `path`. If no `callback` is passed, a promise is returned. @@ -749,12 +751,67 @@ If no `callback` is passed, a promise is returned. ```JavaScript ipfs.files.read('/hello-world', (err, buf) => { - console.log(buf.toString()) + console.log(buf.toString('utf8')) }) // Hello, World! ``` +#### `files.readReadableStream` + +> Read a file into a [`ReadableStream`][rs]. + +##### `Go` **WIP** + +##### `JavaScript` - ipfs.files.readReadableStream(path, [options]) + +Where: + +- `path` is the path of the file to read. +- `options` is an optional Object that might contain the following keys: + - `offset` is an Integer with the byte offset to begin reading from. + - `count` is an Integer with the maximum number of bytes to read. + +Returns a [`ReadableStream`][rs] with the contents of `path`. + +**Example:** + +```JavaScript +const stream = ipfs.files.readReadableStream('/hello-world') +stream.on('data', (buf) => console.log(buf.toString('utf8'))) + +// Hello, World! +``` + +#### `files.readPullStream` + +> Read a file into a [`PullStream`][ps]. + +##### `Go` **WIP** + +##### `JavaScript` - ipfs.files.readPullStream(path, [options]) + +Where: + +- `path` is the path of the file to read. +- `options` is an optional Object that might contain the following keys: + - `offset` is an Integer with the byte offset to begin reading from. + - `count` is an Integer with the maximum number of bytes to read. + +Returns a [`PullStream`][ps] with the contents of `path`. + +**Example:** + +```JavaScript +pull( + ipfs.files.readPullStream('/hello-world'), + through(buf => console.log(buf.toString('utf8'))), + collect(err => {}) +) + +// Hello, World! +``` + #### `files.write` > Write to a file. @@ -765,10 +822,13 @@ ipfs.files.read('/hello-world', (err, buf) => { Where: -- `path` is the path of the object to write. +- `path` is the path of the file to write. - `content` can be: - - a Buffer instance. - - a Path (caveat: will only work in Node.js). + - a [`Buffer`][b] + - a [`PullStream`][ps] + - a [`ReadableStream`][rs] + - a [`Blob`][blob] (caveat: will only work in the browser) + - a string path to a file (caveat: will only work in Node.js) - `options` is an optional Object that might contain the following keys: - `offset` is an Integer with the byte offset to begin writing at. - `create` is a Boolean to indicate to create the file if it doesn't exist. @@ -797,8 +857,8 @@ ipfs.files.write('/hello-world', Buffer.from('Hello, world!'), (err) => { Where: -- `from` is the path of the source object to move. -- `to` is the path of the destination object to move to. +- `from` is the path of the source file to move. +- `to` is the path of the destination file to move to. `callback` must follow the `function (err) {}` signature, where `err` is an Error if the operation was not successful. @@ -881,3 +941,4 @@ ipfs.files.ls('/screenshots', function (err, files) { [rs]: https://www.npmjs.com/package/readable-stream [ps]: https://www.npmjs.com/package/pull-stream [cid]: https://www.npmjs.com/package/cids +[blob]: https://developer.mozilla.org/en-US/docs/Web/API/Blob diff --git a/js/src/dag.js b/js/src/dag.js index de74c1728..d337bd045 100644 --- a/js/src/dag.js +++ b/js/src/dag.js @@ -13,6 +13,7 @@ const dagPB = require('ipld-dag-pb') const DAGNode = dagPB.DAGNode const dagCBOR = require('ipld-dag-cbor') const CID = require('cids') +const { spawnNodeWithId } = require('./utils/spawn') module.exports = (common) => { describe('.dag', () => { @@ -26,14 +27,12 @@ module.exports = (common) => { common.setup((err, factory) => { expect(err).to.not.exist() - factory.spawnNode((err, node) => { + + spawnNodeWithId(factory, (err, node) => { expect(err).to.not.exist() ipfs = node - ipfs.id((err, id) => { - expect(err).to.not.exist() - withGo = id.agentVersion.startsWith('go-ipfs') - done() - }) + withGo = node.peerId.agentVersion.startsWith('go-ipfs') + done() }) }) }) diff --git a/js/src/dht.js b/js/src/dht.js index d32595c6a..333d2a424 100644 --- a/js/src/dht.js +++ b/js/src/dht.js @@ -6,22 +6,9 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) const waterfall = require('async/waterfall') -const series = require('async/series') const parallel = require('async/parallel') const CID = require('cids') - -function spawnWithId (factory, callback) { - waterfall([ - (cb) => factory.spawnNode(cb), - (node, cb) => node.id((err, peerId) => { - if (err) { - return cb(err) - } - node.peerId = peerId - cb(null, node) - }) - ], callback) -} +const { spawnNodesWithId } = require('./utils/spawn') module.exports = (common) => { describe('.dht', function () { @@ -40,13 +27,8 @@ module.exports = (common) => { common.setup((err, factory) => { expect(err).to.not.exist() - series([ - (cb) => spawnWithId(factory, cb), - (cb) => spawnWithId(factory, cb), - (cb) => spawnWithId(factory, cb), - (cb) => spawnWithId(factory, cb), - (cb) => spawnWithId(factory, cb) - ], (err, nodes) => { + + spawnNodesWithId(5, factory, (err, nodes) => { expect(err).to.not.exist() nodeA = nodes[0] diff --git a/js/src/files-mfs.js b/js/src/files-mfs.js index 948c5fc5e..2d8a07225 100644 --- a/js/src/files-mfs.js +++ b/js/src/files-mfs.js @@ -5,6 +5,7 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') +const loadFixture = require('aegir/fixtures') const expect = chai.expect chai.use(dirtyChai) @@ -477,8 +478,13 @@ module.exports = (common) => { }) }) - // TODO (achingbrain) - Not yet supported in js-ipfs or go-ipfs yet') + // TODO: (achingbrain) - Not yet supported in js-ipfs or go-ipfs yet') describe.skip('.stat', () => { + const smallFile = { + cid: 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', + data: loadFixture('js/test/fixtures/testfile.txt', 'interface-ipfs-core') + } + before((done) => ipfs.files.add(smallFile.data, done)) it.skip('stat outside of mfs', function (done) { diff --git a/js/src/files.js b/js/src/files.js index 884726132..ff8f3a961 100644 --- a/js/src/files.js +++ b/js/src/files.js @@ -32,11 +32,6 @@ module.exports = (common) => { return loadFixture(path, 'interface-ipfs-core') } - const wrapDirectory = { - path: 'wrapper/', - cid: 'QmbzKtHxQXJnWG9VR66TscUfcoK3CV4nceRsCdyAEsEj9A' - } - const smallFile = { cid: 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', data: fixture('js/test/fixtures/testfile.txt') @@ -339,11 +334,11 @@ module.exports = (common) => { it('files.add with only-hash=true', () => { this.slow(10 * 1000) const content = String(Math.random() + Date.now()) - + return ipfs.files.add(Buffer.from(content), { onlyHash: true }) .then(files => { expect(files).to.have.length(1) - + // 'ipfs.object.get()' should timeout because content wasn't actually added return expectTimeout(ipfs.object.get(files[0].hash), 4000) }) diff --git a/js/src/index.js b/js/src/index.js index 881e50145..7db37eb54 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -17,3 +17,4 @@ exports.stats = require('./stats') exports.repo = require('./repo') exports.bootstrap = require('./bootstrap') exports.types = require('./types') +exports.util = require('./util') diff --git a/js/src/key.js b/js/src/key.js index 96109570b..11c11993e 100644 --- a/js/src/key.js +++ b/js/src/key.js @@ -8,6 +8,7 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) const hat = require('hat') +const { spawnNodeWithId } = require('./utils/spawn') module.exports = (common) => { describe('.key', () => { @@ -25,14 +26,11 @@ module.exports = (common) => { common.setup((err, factory) => { expect(err).to.not.exist() - factory.spawnNode((err, node) => { + spawnNodeWithId(factory, (err, node) => { expect(err).to.not.exist() ipfs = node - ipfs.id((err, id) => { - expect(err).to.not.exist() - withGo = id.agentVersion.startsWith('go-ipfs') - done() - }) + withGo = node.peerId.agentVersion.startsWith('go-ipfs') + done() }) }) }) diff --git a/js/src/miscellaneous.js b/js/src/miscellaneous.js index abe804b59..7c3030435 100644 --- a/js/src/miscellaneous.js +++ b/js/src/miscellaneous.js @@ -7,6 +7,7 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) +const { spawnNodeWithId } = require('./utils/spawn') module.exports = (common) => { describe('.miscellaneous', () => { @@ -20,14 +21,11 @@ module.exports = (common) => { common.setup((err, factory) => { expect(err).to.not.exist() - factory.spawnNode((err, node) => { + spawnNodeWithId(factory, (err, node) => { expect(err).to.not.exist() ipfs = node - ipfs.id((err, id) => { - expect(err).to.not.exist() - withGo = id.agentVersion.startsWith('go-ipfs') - done() - }) + withGo = node.peerId.agentVersion.startsWith('go-ipfs') + done() }) }) }) diff --git a/js/src/pubsub.js b/js/src/pubsub.js index b4c40c551..a877073e5 100644 --- a/js/src/pubsub.js +++ b/js/src/pubsub.js @@ -8,10 +8,10 @@ const expect = chai.expect chai.use(dirtyChai) const series = require('async/series') const each = require('async/each') -const waterfall = require('async/waterfall') const parallel = require('async/parallel') const whilst = require('async/whilst') const hat = require('hat') +const { spawnNodesWithId } = require('./utils/spawn') // On Browsers it will be false, but the tests currently aren't run // there anyway @@ -36,19 +36,6 @@ function waitForPeers (ipfs, topic, peersToWait, callback) { }, 500) } -function spawnWithId (factory, callback) { - waterfall([ - (cb) => factory.spawnNode(cb), - (node, cb) => node.id((err, res) => { - if (err) { - return cb(err) - } - node.peerId = res - cb(null, node) - }) - ], callback) -} - function makeCheck (n, done) { let i = 0 return (err) => { @@ -83,11 +70,7 @@ module.exports = (common) => { return done(err) } - series([ - (cb) => spawnWithId(factory, cb), - (cb) => spawnWithId(factory, cb), - (cb) => spawnWithId(factory, cb) - ], (err, nodes) => { + spawnNodesWithId(3, factory, (err, nodes) => { if (err) { return done(err) } diff --git a/js/src/stats.js b/js/src/stats.js index df9cf1e72..83441e024 100644 --- a/js/src/stats.js +++ b/js/src/stats.js @@ -13,7 +13,6 @@ chai.use(dirtyChai) module.exports = (common) => { describe('.stats', () => { let ipfs - let withGo before(function (done) { // CI takes longer to instantiate the daemon, so we need to increase the @@ -25,11 +24,7 @@ module.exports = (common) => { factory.spawnNode((err, node) => { expect(err).to.not.exist() ipfs = node - node.id((err, id) => { - expect(err).to.not.exist() - withGo = id.agentVersion.startsWith('go-ipfs') - done() - }) + done() }) }) }) diff --git a/js/src/swarm.js b/js/src/swarm.js index 45284b448..5905412c3 100644 --- a/js/src/swarm.js +++ b/js/src/swarm.js @@ -13,6 +13,7 @@ const PeerId = require('peer-id') const os = require('os') const path = require('path') const hat = require('hat') +const { spawnNodes } = require('./utils/spawn') module.exports = (common) => { describe('.swarm', function () { @@ -30,18 +31,13 @@ module.exports = (common) => { common.setup((err, factory) => { expect(err).to.not.exist() factoryInstance = factory - series([ - (cb) => factory.spawnNode((err, node) => { - expect(err).to.not.exist() - ipfsA = node - cb() - }), - (cb) => factory.spawnNode((err, node) => { - expect(err).to.not.exist() - ipfsB = node - cb() - }) - ], done) + + spawnNodes(2, factory, (err, nodes) => { + expect(err).to.not.exist() + ipfsA = nodes[0] + ipfsB = nodes[1] + done() + }) }) }) diff --git a/js/src/util.js b/js/src/util.js index 7e884d711..b64ef2b3c 100644 --- a/js/src/util.js +++ b/js/src/util.js @@ -3,38 +3,39 @@ const crypto = require('libp2p-crypto') const isIPFS = require('is-ipfs') - const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -describe('.types', function () { - let ipfs +module.exports = (common) => { + describe('.util', function () { + let ipfs - before(function (done) { - // CI takes longer to instantiate the daemon, so we need to increase the - // timeout for the before step - this.timeout(60 * 1000) + before(function (done) { + // CI takes longer to instantiate the daemon, so we need to increase the + // timeout for the before step + this.timeout(60 * 1000) - common.setup((err, factory) => { - expect(err).to.not.exist() - factory.spawnNode((err, node) => { + common.setup((err, factory) => { expect(err).to.not.exist() - ipfs = node - done() + factory.spawnNode((err, node) => { + expect(err).to.not.exist() + ipfs = node + done() + }) }) }) - }) - after((done) => { - common.teardown(done) - }) + after((done) => { + common.teardown(done) + }) - it('util object', () => { - expect(ipfs.util).to.be.deep.equal({ - crypto: crypto, - isIPFS: isIPFS + it('util object', () => { + expect(ipfs.util).to.be.deep.equal({ + crypto: crypto, + isIPFS: isIPFS + }) }) }) -}) +} diff --git a/js/src/utils/spawn.js b/js/src/utils/spawn.js new file mode 100644 index 000000000..877011ebb --- /dev/null +++ b/js/src/utils/spawn.js @@ -0,0 +1,30 @@ +const waterfall = require('async/waterfall') +const times = require('async/times') + +// Spawn a node, get it's id and set it as `peerId` on the node +function spawnNodeWithId (factory, callback) { + waterfall([ + (cb) => factory.spawnNode(cb), + (node, cb) => node.id((err, id) => { + if (err) return cb(err) + node.peerId = id + cb(null, node) + }) + ], callback) +} + +exports.spawnNodeWithId = spawnNodeWithId + +// Spawn n nodes +function spawnNodes (n, factory, callback) { + times(n, (_, cb) => factory.spawnNode(cb), callback) +} + +exports.spawnNodes = spawnNodes + +// Spawn n nodes, getting their id's and setting them as `peerId` on the nodes +function spawnNodesWithId (n, factory, callback) { + times(n, (_, cb) => spawnNodeWithId(factory, cb), callback) +} + +exports.spawnNodesWithId = spawnNodesWithId diff --git a/js/src/utils/stats.js b/js/src/utils/stats.js index 24440a71b..d9c56f07c 100644 --- a/js/src/utils/stats.js +++ b/js/src/utils/stats.js @@ -3,7 +3,6 @@ 'use strict' -const Big = require('big.js') const { expect } = require('chai') const isBigInt = (n) => { diff --git a/package.json b/package.json index 351c93bb2..bd4ec908c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "interface-ipfs-core", - "version": "0.65.8", + "version": "0.65.9", "description": "A test suite and interface you can use to implement a IPFS core interface.", "leadMaintainer": "Alan Shaw ", "main": "js/src/index.js", @@ -57,7 +57,6 @@ "devDependencies": {}, "contributors": [ "Alan Shaw ", - "Alex Potsides ", "David Dias ", "Dmitriy Ryajov ", "Enrico Marino ", @@ -83,6 +82,7 @@ "Thiago Delgado ", "Vasco Santos ", "Volker Mische ", + "achingbrain ", "greenkeeperio-bot ", "haad ", "kumavis ",