Skip to content
This repository has been archived by the owner on Mar 10, 2020. It is now read-only.

Commit

Permalink
feat: Add promise based api
Browse files Browse the repository at this point in the history
All methods return a promise when not passed a callback

Closes #80
  • Loading branch information
dignifiedquire committed Feb 5, 2016
1 parent 934e869 commit c572348
Show file tree
Hide file tree
Showing 22 changed files with 470 additions and 35 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ If you omit the host and port, the api will parse `window.host`, and use this in
var ipfs = window.ipfsAPI()
```

### Using Promises

If you do not pass in a callback all api functions will return a `Promise`, for example

```js
ipfs.id()
.then(function (id) {
console.log('my id is: ', id)
})
```

This relies on a global `Promise` object. If you are in an environemnt where that is not
yet available you need to bring your own polyfill.

#### Gotchas

When using the api from script tag for things that require buffers (`ipfs.add`, for example), you will have to use either the exposed `ipfs.Buffer`, that works just like a node buffer, or use this [browser buffer](https://github.com/feross/buffer).
Expand Down
29 changes: 22 additions & 7 deletions src/api/dht.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,38 @@ module.exports = send => {
opts = null
}

return send('dht/get', key, opts, null, (err, res) => {
if (err) return cb(err)
if (!res) return cb(new Error('empty response'))
if (res.length === 0) return cb(new Error('no value returned for key'))
const handleResult = (done, err, res) => {
if (err) return done(err)
if (!res) return done(new Error('empty response'))
if (res.length === 0) return done(new Error('no value returned for key'))

// Inconsistent return values in the browser vs node
if (Array.isArray(res)) {
res = res[0]
}

if (res.Type === 5) {
cb(null, res.Extra)
done(null, res.Extra)
} else {
let error = new Error('key was not found (type 6)')
cb(error)
done(error)
}
})
}

if (typeof cb !== 'function' && typeof Promise !== 'undefined') {
const done = (err, res) => {
if (err) throw err
return res
}

return send('dht/get', key, opts)
.then(
res => handleResult(done, null, res),
err => handleResult(done, err)
)
}

return send('dht/get', key, opts, null, handleResult.bind(null, cb))
},
put (key, value, opts, cb) {
if (typeof (opts) === 'function' && !cb) {
Expand Down
5 changes: 5 additions & 0 deletions src/api/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ const ndjson = require('ndjson')
module.exports = send => {
return {
tail (cb) {
if (typeof cb !== 'function' && typeof Promise !== 'undefined') {
return send('log/tail', null, {}, null, false)
.then(res => res.pipe(ndjson.parse()))
}

return send('log/tail', null, {}, null, false, (err, res) => {
if (err) return cb(err)
cb(null, res.pipe(ndjson.parse()))
Expand Down
5 changes: 5 additions & 0 deletions src/api/ping.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

module.exports = send => {
return function ping (id, cb) {
if (typeof cb !== 'function' && typeof Promise !== 'undefined') {
return send('ping', id, {n: 1}, null)
.then(res => res[1])
}

return send('ping', id, { n: 1 }, null, function (err, res) {
if (err) return cb(err, null)
cb(null, res[1])
Expand Down
23 changes: 17 additions & 6 deletions src/request-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ function requestAPI (config, path, args, qs, files, buffer, cb) {
if (args) qs.arg = args
if (files && !Array.isArray(files)) files = [files]

if (typeof buffer === 'function') {
cb = buffer
buffer = false
}

if (qs.r) {
qs.recursive = qs.r
delete qs.r // From IPFS 0.4.0, it throw an error when both r and recursive are passed
Expand Down Expand Up @@ -116,5 +111,21 @@ function requestAPI (config, path, args, qs, files, buffer, cb) {
// -- Interface

exports = module.exports = function getRequestAPI (config) {
return requestAPI.bind(null, config)
return function (path, args, qs, files, buffer, cb) {
if (typeof buffer === 'function') {
cb = buffer
buffer = false
}

if (typeof cb !== 'function' && typeof Promise !== 'undefined') {
return new Promise(function (resolve, reject) {
requestAPI(config, path, args, qs, files, buffer, function (err, res) {
if (err) return reject(err)
resolve(res)
})
})
}

return requestAPI(config, path, args, qs, files, buffer, cb)
}
}
11 changes: 11 additions & 0 deletions test/api/add.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,15 @@ describe('.add', () => {
done()
})
})

describe('promise', () => {
it('add buffer', () => {
let buf = new Buffer(testfile)
return apiClients['a'].add(buf)
.then(res => {
expect(res).to.have.length(1)
expect(res[0]).to.have.property('Hash', 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
})
})
})
})
30 changes: 30 additions & 0 deletions test/api/block.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,34 @@ describe('.block', () => {
done()
})
})

describe('promise', () => {
it('block.put', () => {
return apiClients['a'].block.put(blorb)
.then(res => {
expect(res).to.have.a.property('Key', 'QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ')
})
})

it('block.get', done => {
return apiClients['a'].block.get(blorbKey)
.then(res => {
let buf = ''
res
.on('data', function (data) { buf += data })
.on('end', function () {
expect(buf).to.be.equal('blorb')
done()
})
})
})

it('block.stat', () => {
return apiClients['a'].block.stat(blorbKey)
.then(res => {
expect(res).to.have.property('Key')
expect(res).to.have.property('Size')
})
})
})
})
16 changes: 16 additions & 0 deletions test/api/cat.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,20 @@ describe('.cat', () => {
})
})
})

describe('promise', () => {
it('cat', done => {
return apiClients['a'].cat('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
.then(res => {
let buf = ''
res
.on('error', err => { throw err })
.on('data', data => buf += data)
.on('end', () => {
expect(buf).to.be.equal(testfile.toString())
done()
})
})
})
})
})
9 changes: 9 additions & 0 deletions test/api/commands.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ describe('.commands', () => {
done()
})
})

describe('promise', () => {
it('lists commands', () => {
return apiClients['a'].commands()
.then(res => {
expect(res).to.exist
})
})
})
})
33 changes: 33 additions & 0 deletions test/api/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,37 @@ describe('.config', () => {
done()
})
})

describe('promise', () => {
it('.config.{set, get}', () => {
const confKey = 'arbitraryKey'
const confVal = 'arbitraryVal'

return apiClients['a'].config.set(confKey, confVal)
.then(res => {
return apiClients['a'].config.get(confKey)
})
.then(res => {
expect(res).to.have.a.property('Value', confVal)
})
})

it('.config.show', () => {
return apiClients['c'].config.show()
.then(res => {
expect(res).to.exist
})
})

it('.config.replace', () => {
if (!isNode) {
return
}

return apiClients['c'].config.replace(__dirname + '/../r-config.json')
.then(res => {
expect(res).to.be.equal(null)
})
})
})
})
23 changes: 23 additions & 0 deletions test/api/dht.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,27 @@ describe('.dht', () => {
done()
})
})

describe('promise', () => {
it('returns an error when getting a non-existent key from the DHT', () => {
return apiClients['a'].dht.get('non-existent', {timeout: '100ms'})
.catch(err => {
expect(err).to.be.an.instanceof(Error)
})
})

it('puts a key value pair in the DHT', () => {
return apiClients['a'].dht.put('scope', 'interplanetary')
.then(res => {
expect(res).to.be.an('array')
})
})

it('.dht.findprovs', () => {
return apiClients['a'].dht.findprovs('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
.then(res => {
expect(res).to.be.an('array')
})
})
})
})
18 changes: 18 additions & 0 deletions test/api/diag.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,22 @@ describe('.diag', () => {
done()
})
})

describe('promise', () => {
it('.diag.net', () => {
return apiClients['a'].diag.net()
.then(res => {
expect(res).to.exist
})
})

it('.diag.sys', () => {
return apiClients['a'].diag.sys()
.then(res => {
expect(res).to.exist
expect(res).to.have.a.property('memory')
expect(res).to.have.a.property('diskinfo')
})
})
})
})
10 changes: 10 additions & 0 deletions test/api/id.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@ describe('.id', () => {
done()
})
})

describe('promise', () => {
it('id', () => {
return apiClients['a'].id()
.then(res => {
expect(res).to.have.a.property('ID')
expect(res).to.have.a.property('PublicKey')
})
})
})
})
12 changes: 12 additions & 0 deletions test/api/log.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@ describe('.log', () => {
})
})
})

describe('promise', () => {
it('.log.tail', done => {
apiClients['a'].log.tail()
.then(res => {
res.once('data', obj => {
expect(obj).to.be.an('object')
done()
})
})
})
})
})
29 changes: 29 additions & 0 deletions test/api/ls.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,33 @@ describe('ls', function () {
done()
})
})

describe('promise', () => {
it('should correctly retrieve links', () => {
if (!isNode) return

return apiClients['a'].ls('QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg')
.then(res => {
expect(res).to.have.a.property('Objects')
expect(res.Objects[0]).to.have.a.property('Links')
expect(res.Objects[0]).to.have.property('Hash', 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg')
})
})

it('should correctly handle a nonexisting hash', () => {
return apiClients['a'].ls('surelynotavalidhashheh?')
.catch(err => {
expect(err).to.exist
})
})

it('should correctly handle a nonexisting path', () => {
if (!isNode) return

return apiClients['a'].ls('QmTDH2RXGn8XyDAo9YyfbZAUXwL1FCr44YJCN9HBZmL9Gj/folder_that_isnt_there')
.catch(err => {
expect(err).to.exist
})
})
})
})
20 changes: 20 additions & 0 deletions test/api/name.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,24 @@ describe('.name', () => {
done()
})
})

describe('promise', () => {
it('.name.publish', () => {
return apiClients['a'].name.publish('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
.then(res => {
name = res
expect(name).to.exist
})
})

it('.name.resolve', () => {
return apiClients['a'].name.resolve(name.Name)
.then(res => {
expect(res).to.exist
expect(res).to.be.eql({
Path: '/ipfs/' + name.Value
})
})
})
})
})
Loading

0 comments on commit c572348

Please sign in to comment.