diff --git a/API.md b/API.md deleted file mode 100644 index 60a7939..0000000 --- a/API.md +++ /dev/null @@ -1,67 +0,0 @@ -# API - -```js -const BlockService = require('ipfs-block-service') -``` - -### `new BlockService(repo)` - -- `repo: Repo` - -Creates a new block service backed by [IPFS Repo][repo] `repo` for storage. - -### `goOnline(bitswap)` - -- `bitswap: Bitswap` - -Add a bitswap instance that communicates with the network to retreive blocks -that are not in the local store. - -If the node is online all requests for blocks first check locally and -afterwards ask the network for the blocks. - -### `goOffline()` - -Remove the bitswap instance and fall back to offline mode. - -### `isOnline()` - -Returns a `Boolean` indicating if the block service is online or not. - -### `put(block, callback)` - -- `block: Block` -- `callback: Function` - -Asynchronously adds a block instance to the underlying repo. - -### `putStream()` - -Returns a through pull-stream, which `Block`s can be written to, and -that emits the meta data about the written block. - -### `get(multihash [, extension], callback)` - -- `multihash: Multihash` -- `extension: String`, defaults to 'data' -- `callback: Function` - -Asynchronously returns the block whose content multihash matches `multihash`. - -### `getStream(multihash [, extension])` - -- `multihash: Multihash` -- `extension: String`, defaults to 'data' - -Returns a source pull-stream, which emits the requested block. - -### `delete(multihashes, [, extension], callback)` - -- `multihashes: Multihash|[]Multihash` -- `extension: String`, defaults to 'data'- `extension: String`, defaults to 'data' -- `callback: Function` - -Deletes all blocks referenced by multihashes. - -[multihash]: https://github.com/multiformats/js-multihash -[repo]: https://github.com/ipfs/specs/tree/master/repo diff --git a/README.md b/README.md index 98cff4a..6129403 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,17 @@ var repo = new IPFSRepo('example', { stores: Store }) // create a block const block = new Block('hello world') console.log(block.data) -console.log(block.key) +console.log(block.key()) // create a service const bs = new BlockService(repo) // add the block, then retrieve it -bs.put(block, function (err) { - bs.get(block.key, function (err, b) { +bs.put({ + block: block, + cid: cid, +}, function (err) { + bs.get(cid, function (err, b) { console.log(block.data.toString() === b.data.toString()) }) }) @@ -118,10 +121,70 @@ the global namespace. ``` -You can find the [API documentation here](API.md) +# API -[ipfs]: https://ipfs.io -[bitswap]: https://github.com/ipfs/specs/tree/master/bitswap +```js +const BlockService = require('ipfs-block-service') +``` + +### `new BlockService(repo)` + +- `repo: Repo` + +Creates a new block service backed by [IPFS Repo][repo] `repo` for storage. + +### `goOnline(bitswap)` + +- `bitswap: Bitswap` + +Add a bitswap instance that communicates with the network to retreive blocks +that are not in the local store. + +If the node is online all requests for blocks first check locally and +afterwards ask the network for the blocks. + +### `goOffline()` + +Remove the bitswap instance and fall back to offline mode. + +### `isOnline()` + +Returns a `Boolean` indicating if the block service is online or not. + +### `put(blockAndCID, callback)` + +- `blockAndCID: { block: block, cid: cid }` +- `callback: Function` + +Asynchronously adds a block instance to the underlying repo. + +### `putStream()` + +Returns a through pull-stream, which `blockAndCID`s can be written to, and +that emits the meta data about the written block. + +### `get(cid [, extension], callback)` + +- `cid: CID` +- `extension: String`, defaults to 'data' +- `callback: Function` + +Asynchronously returns the block whose content multihash matches `multihash`. + +### `getStream(cid [, extension])` + +- `cid: CID` +- `extension: String`, defaults to 'data' + +Returns a source pull-stream, which emits the requested block. + +### `delete(cids, [, extension], callback)` + +- `cids: CID | []CID` +- `extension: String`, defaults to 'data' - `extension: String`, defaults to 'data' +- `callback: Function` + +Deletes all blocks referenced by multihashes. ## Contribute @@ -134,3 +197,9 @@ This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/c ## License [MIT](LICENSE) + +[ipfs]: https://ipfs.io +[bitswap]: https://github.com/ipfs/specs/tree/master/bitswap +[repo]: https://github.com/ipfs/specs/tree/master/repo + + diff --git a/package.json b/package.json index e603238..b41bf47 100644 --- a/package.json +++ b/package.json @@ -37,13 +37,13 @@ }, "homepage": "https://github.com/ipfs/js-ipfs-block-service#readme", "devDependencies": { - "aegir": "^8.0.0", + "aegir": "^8.1.2", "buffer-loader": "0.0.1", "chai": "^3.5.0", "fs-pull-blob-store": "^0.3.0", "idb-pull-blob-store": "^0.5.1", - "ipfs-block": "^0.3.0", - "ipfs-repo": "^0.9.0", + "ipfs-block": "^0.4.0", + "ipfs-repo": "^0.10.0", "lodash": "^4.15.0", "ncp": "^2.0.0", "pre-commit": "^1.1.3", @@ -51,6 +51,7 @@ "run-series": "^1.1.4" }, "dependencies": { + "cids": "^0.2.0", "pull-stream": "^3.4.5", "run-parallel-limit": "^1.0.3" }, @@ -60,4 +61,4 @@ "Richard Littauer ", "Stephen Whitmore " ] -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 5ee06b9..216121d 100644 --- a/src/index.js +++ b/src/index.js @@ -24,62 +24,73 @@ module.exports = class BlockService { return this._bitswap != null } - put (block, callback) { + // Note: we have to pass the CID, so that bitswap can then use it for + // the smart selectors. For now, passing the CID is used so that we know + // the right multihash, this means that now we have multiple hashes official + // support \o/ + put (blockAndCID, callback) { callback = callback || (() => {}) - if (!block) { - return callback(new Error('Missing block')) + if (!blockAndCID) { + return callback(new Error('Missing block and CID')) } pull( - pull.values([block]), + pull.values([ + blockAndCID + ]), this.putStream(), pull.onEnd(callback) ) } putStream () { + let ps if (this.isOnline()) { - return this._bitswap.putStream() + // NOTE: This will have to change in order for bitswap + // to understand CID + ps = this._bitswap.putStream() + } else { + ps = this._repo.blockstore.putStream() } - return this._repo.blockstore.putStream() + return pull( + pull.map((blockAndCID) => { + return { + data: blockAndCID.block.data, + key: blockAndCID.cid.multihash + } + }), + ps + ) } - get (key, extension, callback) { - if (typeof extension === 'function') { - callback = extension - extension = undefined - } - + get (cid, callback) { pull( - this.getStream(key, extension), + this.getStream(cid), pull.collect((err, result) => { - if (err) return callback(err) + if (err) { + return callback(err) + } callback(null, result[0]) }) ) } - getStream (key, extension) { + getStream (cid) { if (this.isOnline()) { - return this._bitswap.getStream(key) + return this._bitswap.getStream(cid.multihash) } - return this._repo.blockstore.getStream(key, extension) + return this._repo.blockstore.getStream(cid.multihash) } - delete (keys, extension, callback) { - if (typeof extension === 'function') { - callback = extension - extension = undefined - } - - if (!Array.isArray(keys)) { - keys = [keys] + delete (cids, callback) { + if (!Array.isArray(cids)) { + cids = [cids] } - parallelLimit(keys.map((key) => (next) => { - this._repo.blockstore.delete(key, extension, next) + parallelLimit(cids.map((cid) => (next) => { + this._repo.blockstore.delete(cid.multihash, next) }), 100, callback) } } diff --git a/test/block-service-test.js b/test/block-service-test.js index 1b3f2f9..7e877a4 100644 --- a/test/block-service-test.js +++ b/test/block-service-test.js @@ -6,6 +6,7 @@ const Block = require('ipfs-block') const pull = require('pull-stream') const _ = require('lodash') const series = require('run-series') +const CID = require('cids') const BlockService = require('../src') @@ -20,25 +21,15 @@ module.exports = (repo) => { describe('offline', () => { it('store and get a block', (done) => { const b = new Block('A random data block') + const cid = new CID(b.key()) series([ - (cb) => bs.put(b, cb), - (cb) => bs.get(b.key, (err, block) => { - if (err) return cb(err) - expect(b).to.be.eql(block) - cb() - }) - ], done) - }) - - it('store and get a block, with custom extension', (done) => { - const b = new Block('A random data block 2', 'ext') - - series([ - (cb) => bs.put(b, cb), - (cb) => bs.get(b.key, 'ext', (err, block) => { - if (err) return cb(err) - expect(b).to.be.eql(block) + (cb) => bs.put({ block: b, cid: cid }, cb), + (cb) => bs.get(cid, (err, block) => { + if (err) { + return cb(err) + } + expect(b.key()).to.be.eql(block.key()) cb() }) ], done) @@ -46,7 +37,9 @@ module.exports = (repo) => { it('get a non existent block', (done) => { const b = new Block('Not stored') - bs.get(b.key, (err, block) => { + const cid = new CID(b.key()) + + bs.get(cid, (err, block) => { expect(err).to.exist done() }) @@ -58,7 +51,11 @@ module.exports = (repo) => { const b3 = new Block('3') pull( - pull.values([b1, b2, b3]), + pull.values([ + { block: b1, cid: new CID(b1.key()) }, + { block: b2, cid: new CID(b2.key()) }, + { block: b3, cid: new CID(b3.key()) } + ]), bs.putStream(), pull.collect((err, meta) => { expect(err).to.not.exist @@ -68,20 +65,17 @@ module.exports = (repo) => { ) }) - it('get: bad invocation', (done) => { - bs.get(null, (err) => { - expect(err).to.be.an('error') - done() - }) - }) - it('get many blocks', (done) => { const b1 = new Block('1') const b2 = new Block('2') const b3 = new Block('3') pull( - pull.values([b1, b2, b3]), + pull.values([ + { block: b1, cid: new CID(b1.key()) }, + { block: b2, cid: new CID(b2.key()) }, + { block: b3, cid: new CID(b3.key()) } + ]), bs.putStream(), pull.onEnd((err) => { expect(err).to.not.exist @@ -91,13 +85,23 @@ module.exports = (repo) => { function getAndAssert () { pull( - pull.values([b1.key, b2.key, b3.key]), - pull.map((key) => bs.getStream(key)), + pull.values([ + b1.key(), + b2.key(), + b3.key() + ]), + pull.map((key) => { + const cid = new CID(key) + return bs.getStream(cid) + }), pull.flatten(), pull.collect((err, blocks) => { expect(err).to.not.exist + const bPutKeys = blocks.map((b) => { + return b.key() + }) - expect(blocks).to.be.eql([b1, b2, b3]) + expect(bPutKeys).to.be.eql([b1.key(), b2.key(), b3.key()]) done() }) ) @@ -106,32 +110,12 @@ module.exports = (repo) => { it('delete a block', (done) => { const b = new Block('Will not live that much') - bs.put(b, (err) => { + bs.put({ block: b, cid: new CID(b.key()) }, (err) => { expect(err).to.not.exist - bs.delete(b.key, (err) => { + const cid = new CID(b.key()) + bs.delete(cid, (err) => { expect(err).to.not.exist - bs.get(b.key, (err, block) => { - expect(err).to.exist - done() - }) - }) - }) - }) - - it('delete: bad invocation', (done) => { - bs.delete(null, (err) => { - expect(err).to.be.an('error') - done() - }) - }) - - it('delete a block, with custom extension', (done) => { - const b = new Block('Will not live that much', 'ext') - bs.put(b, (err) => { - expect(err).to.not.exist - bs.delete(b.key, 'ext', (err) => { - expect(err).to.not.exist - bs.get(b.key, 'ext', (err, block) => { + bs.get(cid, (err, block) => { expect(err).to.exist done() }) @@ -141,7 +125,8 @@ module.exports = (repo) => { it('delete a non existent block', (done) => { const b = new Block('I do not exist') - bs.delete(b.key, (err) => { + const cid = new CID(b.key()) + bs.delete(cid, (err) => { expect(err).to.not.exist done() }) @@ -152,7 +137,11 @@ module.exports = (repo) => { const b2 = new Block('2') const b3 = new Block('3') - bs.delete([b1, b2, b3], 'data', (err) => { + bs.delete([ + new CID(b1.key()), + new CID(b2.key()), + new CID(b3.key()) + ], (err) => { expect(err).to.not.exist done() }) @@ -167,18 +156,26 @@ module.exports = (repo) => { pull( pull.values(blocks), + pull.map((block) => { + return { block: block, cid: new CID(block.key()) } + }), bs.putStream(), pull.onEnd((err) => { expect(err).to.not.exist pull( pull.values(blocks), - pull.map((b) => b.key), - pull.map((key) => bs.getStream(key)), + pull.map((block) => { + return block.key() + }), + pull.map((key) => { + const cid = new CID(key) + return bs.getStream(cid) + }), pull.flatten(), - pull.collect((err, res) => { + pull.collect((err, retrievedBlocks) => { expect(err).to.not.exist - expect(res).to.be.eql(blocks) + expect(retrievedBlocks.length).to.be.eql(blocks.length) done() }) ) @@ -206,16 +203,23 @@ module.exports = (repo) => { }) it('retrieves a block through bitswap', (done) => { + // returns a block with a value equal to its key const bitswap = { getStream (key) { - return pull.values([new Block(key)]) + return pull.values([ + new Block('secret') + ]) } } + bs.goOnline(bitswap) - bs.get('secret', (err, res) => { + const block = new Block('secret') + const cid = new CID(block.key('sha2-256')) + + bs.get(cid, (err, block) => { expect(err).to.not.exist - expect(res).to.be.eql(new Block('secret')) + expect(block.data).to.be.eql(new Block('secret').data) done() }) }) @@ -227,11 +231,18 @@ module.exports = (repo) => { } } bs.goOnline(bitswap) - bs.put(new Block('secret sauce'), done) + + const block = new Block('secret sauce') + + bs.put({ + block: block, + cid: new CID(block.key('sha2-256')) + }, done) }) it('getStream through bitswap', (done) => { const b = new Block('secret sauce 1') + const cid = new CID(b.key('sha2-256')) const bitswap = { getStream () { @@ -240,11 +251,13 @@ module.exports = (repo) => { } bs.goOnline(bitswap) + pull( - bs.getStream(b.key), - pull.collect((err, res) => { + bs.getStream(cid), + pull.collect((err, blocks) => { expect(err).to.not.exist - expect(res).to.be.eql([b]) + expect(blocks[0].data).to.be.eql(b.data) + expect(blocks[0].key('sha2-256')).to.be.eql(cid.multihash) done() }) )