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', () => {