diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7148a28..b93e8b8d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +# [19.0.0](https://github.com/ipfs/js-ipfs-api/compare/v18.2.1...v19.0.0) (2018-03-28) + + +### Bug Fixes + +* **bitswap:** 0.4.14 returns empty array instead of null ([5e37a54](https://github.com/ipfs/js-ipfs-api/commit/5e37a54)) +* **ping:** tests were failing and there it was missing to catch when count and n are used at the same time ([2181568](https://github.com/ipfs/js-ipfs-api/commit/2181568)) + + +### Features + +* streamable ping and optional packet number ([#723](https://github.com/ipfs/js-ipfs-api/issues/723)) ([3f3ce8a](https://github.com/ipfs/js-ipfs-api/commit/3f3ce8a)) + + + ## [18.2.1](https://github.com/ipfs/js-ipfs-api/compare/v18.2.0...v18.2.1) (2018-03-22) diff --git a/README.md b/README.md index 58c48048e..9e9a92a14 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,9 @@ $ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods "[\"PUT\", \"P - [miscellaneous operations](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md) - [`ipfs.id([callback])`](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md#id) - [`ipfs.version([callback])`](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md#version) - - [`ipfs.ping()`](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md#ping) + - [`ipfs.ping(id, [options, callback])`](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md#ping) + - `ipfs.pingPullStream(id, [options])` + - `ipfs.pingReadableStream(id, [options])` - [`ipfs.dns(domain, [callback])`](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md#dns) - [`ipfs.stop([callback])`](https://github.com/ipfs/interface-ipfs-core/tree/master/SPEC/MISCELLANEOUS.md#stop). Alias to `ipfs.shutdown`. diff --git a/package.json b/package.json index f1ec47ba9..0fabfe21c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipfs-api", - "version": "18.2.1", + "version": "19.0.0", "description": "A client library for the IPFS HTTP API", "main": "src/index.js", "browser": { @@ -39,7 +39,7 @@ "is-ipfs": "^0.3.2", "is-stream": "^1.1.0", "lru-cache": "^4.1.2", - "multiaddr": "^3.0.2", + "multiaddr": "^3.1.0", "multihashes": "~0.4.13", "ndjson": "^1.5.0", "once": "^1.4.0", @@ -68,15 +68,17 @@ "aegir": "^13.0.6", "browser-process-platform": "^0.1.1", "chai": "^4.1.2", + "cross-env": "^5.1.4", "dirty-chai": "^2.0.1", "eslint-plugin-react": "^7.7.0", - "go-ipfs-dep": "^0.4.13", + "go-ipfs-dep": "^0.4.14", "gulp": "^3.9.1", "hapi": "^17.2.3", "interface-ipfs-core": "~0.58.0", "ipfs": "~0.28.2", - "ipfsd-ctl": "~0.30.4", + "ipfsd-ctl": "~0.31.0", "pre-commit": "^1.2.2", + "pull-stream": "^3.6.2", "socket.io": "^2.0.4", "socket.io-client": "^2.0.4", "stream-equal": "^1.1.1" @@ -118,6 +120,7 @@ "Jeromy ", "Joe Turgeon ", "Jonathan ", + "João Antunes ", "Juan Batiz-Benet ", "Kevin Wang ", "Kristoffer Ström ", diff --git a/src/ping-pull-stream.js b/src/ping-pull-stream.js new file mode 100644 index 000000000..9c18140e2 --- /dev/null +++ b/src/ping-pull-stream.js @@ -0,0 +1,29 @@ +'use strict' + +const toPull = require('stream-to-pull-stream') +const deferred = require('pull-defer') +const moduleConfig = require('./utils/module-config') + +module.exports = (arg) => { + const send = moduleConfig(arg) + + return (id, opts = {}) => { + // Default number of packtes to 1 + if (!opts.n && !opts.count) { + opts.n = 1 + } + const request = { + path: 'ping', + args: id, + qs: opts + } + const p = deferred.source() + + send(request, (err, stream) => { + if (err) { return p.abort(err) } + p.resolve(toPull.source(stream)) + }) + + return p + } +} diff --git a/src/ping-readable-stream.js b/src/ping-readable-stream.js new file mode 100644 index 000000000..6281a44de --- /dev/null +++ b/src/ping-readable-stream.js @@ -0,0 +1,33 @@ +'use strict' + +const Stream = require('readable-stream') +const pump = require('pump') +const moduleConfig = require('./utils/module-config') + +module.exports = (arg) => { + const send = moduleConfig(arg) + + return (id, opts = {}) => { + // Default number of packtes to 1 + if (!opts.n && !opts.count) { + opts.n = 1 + } + const request = { + path: 'ping', + args: id, + qs: opts + } + // ndjson streams objects + const pt = new Stream.PassThrough({ + objectMode: true + }) + + send(request, (err, stream) => { + if (err) { return pt.destroy(err) } + + pump(stream, pt) + }) + + return pt + } +} diff --git a/src/ping.js b/src/ping.js index 4dbb77b6c..2682e9752 100644 --- a/src/ping.js +++ b/src/ping.js @@ -7,30 +7,36 @@ const streamToValue = require('./utils/stream-to-value') module.exports = (arg) => { const send = moduleConfig(arg) - return promisify((id, callback) => { + return promisify((id, opts, callback) => { + if (typeof opts === 'function') { + callback = opts + opts = {} + } + + if (opts.n && opts.count) { + return callback(new Error('Use either n or count, not both')) + } + + // Default number of packtes to 1 + if (!opts.n && !opts.count) { + opts.n = 1 + } + const request = { path: 'ping', args: id, - qs: { n: 1 } + qs: opts } // Transform the response stream to a value: - // { Success: , Time: , Text: } + // [{ Success: , Time: , Text: }] const transform = (res, callback) => { streamToValue(res, (err, res) => { if (err) { return callback(err) } - // go-ipfs http api currently returns 3 lines for a ping. - // they're a little messed, so take the correct values from each lines. - const pingResult = { - Success: res[1].Success, - Time: res[1].Time, - Text: res[2].Text - } - - callback(null, pingResult) + callback(null, res) }) } diff --git a/src/utils/load-commands.js b/src/utils/load-commands.js index f1b5f6923..e86982e18 100644 --- a/src/utils/load-commands.js +++ b/src/utils/load-commands.js @@ -31,6 +31,8 @@ function requireCommands () { object: require('../object'), pin: require('../pin'), ping: require('../ping'), + pingReadableStream: require('../ping-readable-stream'), + pingPullStream: require('../ping-pull-stream'), refs: require('../refs'), repo: require('../repo'), stop: require('../stop'), diff --git a/test/bitswap.spec.js b/test/bitswap.spec.js index 2a69a1b06..b878cf67c 100644 --- a/test/bitswap.spec.js +++ b/test/bitswap.spec.js @@ -33,7 +33,7 @@ describe('.bitswap', function () { ipfs.bitswap.wantlist((err, res) => { expect(err).to.not.exist() expect(res).to.have.to.eql({ - Keys: null + Keys: [] }) done() }) diff --git a/test/ping.spec.js b/test/ping.spec.js index 747464798..2e7c1b820 100644 --- a/test/ping.spec.js +++ b/test/ping.spec.js @@ -3,6 +3,8 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') +const pull = require('pull-stream') +const collect = require('pull-stream/sinks/collect') const expect = chai.expect chai.use(dirtyChai) @@ -12,11 +14,12 @@ const series = require('async/series') const IPFSApi = require('../src') const f = require('./utils/factory') -describe.skip('.ping', () => { +describe('.ping', function () { let ipfs let ipfsd let other let otherd + let otherId before(function (done) { this.timeout(20 * 1000) // slow CI @@ -31,7 +34,6 @@ describe.skip('.ping', () => { }) }, (cb) => { - console.log('going to spawn second node') f.spawn({ initOptions: { bits: 1024 } }, (err, node) => { expect(err).to.not.exist() other = node.api @@ -43,7 +45,14 @@ describe.skip('.ping', () => { ipfsd.api.id((err, id) => { expect(err).to.not.exist() const ma = id.addresses[0] - other.api.swarm.connect(ma, cb) + other.swarm.connect(ma, cb) + }) + }, + (cb) => { + other.id((err, id) => { + expect(err).to.not.exist() + otherId = id.id + cb() }) } ], done) @@ -56,37 +65,106 @@ describe.skip('.ping', () => { ], done) }) - describe('callback API', () => { - it('ping another peer', (done) => { - other.id((err, id) => { - expect(err).to.not.exist() + it('.ping with default n', (done) => { + ipfs.ping(otherId, (err, res) => { + expect(err).to.not.exist() + expect(res).to.be.an('array') + expect(res).to.have.lengthOf(3) + res.forEach(packet => { + expect(packet).to.have.keys('Success', 'Time', 'Text') + expect(packet.Time).to.be.a('number') + }) + const resultMsg = res.find(packet => packet.Text.includes('Average latency')) + expect(resultMsg).to.exist() + done() + }) + }) - ipfs.ping(id.id, (err, res) => { - expect(err).to.not.exist() - expect(res).to.have.a.property('Success') - expect(res).to.have.a.property('Time') - expect(res).to.have.a.property('Text') - expect(res.Text).to.contain('Average latency') - expect(res.Time).to.be.a('number') - done() - }) + it('.ping with count = 2', (done) => { + ipfs.ping(otherId, { count: 2 }, (err, res) => { + expect(err).to.not.exist() + expect(res).to.be.an('array') + expect(res).to.have.lengthOf(4) + res.forEach(packet => { + expect(packet).to.have.keys('Success', 'Time', 'Text') + expect(packet.Time).to.be.a('number') + }) + const resultMsg = res.find(packet => packet.Text.includes('Average latency')) + expect(resultMsg).to.exist() + done() + }) + }) + + it('.ping with n = 2', (done) => { + ipfs.ping(otherId, { n: 2 }, (err, res) => { + expect(err).to.not.exist() + expect(res).to.be.an('array') + expect(res).to.have.lengthOf(4) + res.forEach(packet => { + expect(packet).to.have.keys('Success', 'Time', 'Text') + expect(packet.Time).to.be.a('number') }) + const resultMsg = res.find(packet => packet.Text.includes('Average latency')) + expect(resultMsg).to.exist() + done() }) }) - describe('promise API', () => { - it('ping another peer', () => { - return other.id() - .then((id) => { - return ipfs.ping(id.id) + it('.ping fails with count & n', function (done) { + this.timeout(20 * 1000) + + ipfs.ping(otherId, {count: 2, n: 2}, (err, res) => { + expect(err).to.exist() + done() + }) + }) + + it('.ping with Promises', () => { + return ipfs.ping(otherId) + .then((res) => { + expect(res).to.be.an('array') + expect(res).to.have.lengthOf(3) + res.forEach(packet => { + expect(packet).to.have.keys('Success', 'Time', 'Text') + expect(packet.Time).to.be.a('number') }) - .then((res) => { - expect(res).to.have.a.property('Success') - expect(res).to.have.a.property('Time') - expect(res).to.have.a.property('Text') - expect(res.Text).to.contain('Average latency') - expect(res.Time).to.be.a('number') + const resultMsg = res.find(packet => packet.Text.includes('Average latency')) + expect(resultMsg).to.exist() + }) + }) + + it('.pingPullStream', (done) => { + pull( + ipfs.pingPullStream(otherId), + collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.be.an('array') + expect(data).to.have.lengthOf(3) + data.forEach(packet => { + expect(packet).to.have.keys('Success', 'Time', 'Text') + expect(packet.Time).to.be.a('number') }) - }) + const resultMsg = data.find(packet => packet.Text.includes('Average latency')) + expect(resultMsg).to.exist() + done() + }) + ) + }) + + it('.pingReadableStream', (done) => { + let packetNum = 0 + ipfs.pingReadableStream(otherId) + .on('data', data => { + packetNum++ + expect(data).to.be.an('object') + expect(data).to.have.keys('Success', 'Time', 'Text') + }) + .on('error', err => { + expect(err).not.to.exist() + }) + .on('end', () => { + expect(packetNum).to.equal(3) + done() + }) }) }) diff --git a/test/sub-modules.spec.js b/test/sub-modules.spec.js index 3512731cc..1921b0228 100644 --- a/test/sub-modules.spec.js +++ b/test/sub-modules.spec.js @@ -69,8 +69,12 @@ describe('submodules', () => { it('ping', () => { const ping = require('../src/ping')(config) + const pingPullStream = require('../src/ping-pull-stream')(config) + const pingReadableStream = require('../src/ping-readable-stream')(config) expect(ping).to.be.a('function') + expect(pingPullStream).to.be.a('function') + expect(pingReadableStream).to.be.a('function') }) it('log', () => {