From 162473b1f3cbc1f62c2810274779d051b3529204 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Fri, 30 Nov 2018 17:21:39 +0100 Subject: [PATCH] feat: implementation of the new `resolve()` function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: `resolve()` replaces parts of `get()`. The API docs for it: > Retrieves IPLD Nodes along the `path` that is rooted at `cid`. - `cid` (`CID`, required): the CID the resolving starts. - `path` (`IPLD Path`, required): the path that should be resolved. Returns an async iterator of all the IPLD Nodes that were traversed during the path resolving. Every element is an object with these fields: - `remainderPath` (`string`): the part of the path that wasn’t resolved yet. - `value` (`*`): the value where the resolved path points to. If further traversing is possible, then the value is a CID object linking to another IPLD Node. If it was possible to fully resolve the path, `value` is the value the `path` points to. So if you need the CID of the IPLD Node you’re currently at, just take the `value` of the previously returned IPLD Node. --- package.json | 1 + src/index.js | 156 ++++++++++------------------- src/util.js | 32 ++++++ test/basics.js | 35 +++---- test/format-support.js | 40 +++----- test/ipld-all.js | 16 +-- test/ipld-bitcoin.js | 130 +++++++++++++----------- test/ipld-dag-cbor.js | 221 ++++++++++++++++------------------------- test/ipld-dag-pb.js | 163 ++++++++++++++---------------- test/ipld-eth-block.js | 138 +++++++++++++------------ test/ipld-eth.js | 31 +++--- test/ipld-git.js | 165 +++++++++++++++++------------- test/ipld-zcash.js | 130 +++++++++++++----------- 13 files changed, 616 insertions(+), 642 deletions(-) create mode 100644 src/util.js diff --git a/package.json b/package.json index 1837b6c..fd5f7ba 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "aegir": "^18.2.1", "bitcoinjs-lib": "^4.0.2", "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "dirty-chai": "^2.0.1", "ethereumjs-block": "^2.1.0", "ipfs-block-service": "~0.15.2", diff --git a/src/index.js b/src/index.js index 69dc5a6..c0fc840 100644 --- a/src/index.js +++ b/src/index.js @@ -3,9 +3,6 @@ const Block = require('ipfs-block') const pull = require('pull-stream') const CID = require('cids') -const doUntil = require('async/doUntil') -const joinPath = require('path').join -const osPathSep = require('path').sep const pullDeferSource = require('pull-defer').source const pullTraverse = require('pull-traverse') const map = require('async/map') @@ -14,6 +11,7 @@ const mergeOptions = require('merge-options') const ipldDagCbor = require('ipld-dag-cbor') const ipldDagPb = require('ipld-dag-pb') const ipldRaw = require('ipld-raw') +const { fancyIterator } = require('./util') function noop () {} @@ -62,111 +60,76 @@ class IPLDResolver { } } - get (cid, path, options, callback) { - if (typeof path === 'function') { - callback = path - path = undefined - } - - if (typeof options === 'function') { - callback = options - options = {} + /** + * Retrieves IPLD Nodes along the `path` that is rooted at `cid`. + * + * @param {CID} cid - the CID the resolving starts. + * @param {string} path - the path that should be resolved. + * @returns {Iterable.>} - Returns an async iterator of all the IPLD Nodes that were traversed during the path resolving. Every element is an object with these fields: + * - `remainderPath`: the part of the path that wasn’t resolved yet. + * - `value`: the value where the resolved path points to. If further traversing is possible, then the value is a CID object linking to another IPLD Node. If it was possible to fully resolve the path, value is the value the path points to. So if you need the CID of the IPLD Node you’re currently at, just take the value of the previously returned IPLD Node. + */ + resolve (cid, path) { + if (!CID.isCID(cid)) { + throw new Error('`cid` argument must be a CID') } - - // this removes occurrences of ./, //, ../ - // makes sure that path never starts with ./ or / - // path.join is OS specific. Need to convert back to POSIX format. - if (typeof path === 'string') { - path = joinPath('/', path) - .substr(1) - .split(osPathSep) - .join('/') + if (typeof path !== 'string') { + throw new Error('`path` argument must be a string') } - if (path === '' || !path) { - return this._get(cid, (err, node) => { - if (err) { - return callback(err) - } - callback(null, { - value: node, - remainderPath: '', - cid - }) - }) - } - - let value + const next = () => { + // End iteration if there isn't a CID to follow anymore + if (cid === null) { + return Promise.resolve({ done: true }) + } - doUntil( - (cb) => { + return new Promise((resolve, reject) => { this._getFormat(cid.codec, (err, format) => { - if (err) return cb(err) + if (err) { + return reject(err) + } // get block // use local resolver // update path value this.bs.get(cid, (err, block) => { if (err) { - return cb(err) + return reject(err) } format.resolver.resolve(block.data, path, (err, result) => { if (err) { - return cb(err) + return reject(err) } - value = result.value + + // Prepare for the next iteration if there is a `remainderPath` path = result.remainderPath - cb() + let value = result.value + // NOTE vmx 2018-11-29: Not all IPLD Formats return links as + // CIDs yet. Hence try to convert old style links to CIDs + if (Object.keys(value).length === 1 && '/' in value) { + value = new CID(value['/']) + } + if (CID.isCID(value)) { + cid = value + } else { + cid = null + } + + return resolve({ + done: false, + value: { + remainderPath: path, + value + } + }) }) }) }) - }, - () => { - const endReached = !path || path === '' || path === '/' - const isTerminal = value && !IPLDResolver._maybeCID(value) - - if ((endReached && isTerminal) || options.localResolve) { - cid = IPLDResolver._maybeCID(value) || cid - - return true - } else { - value = IPLDResolver._maybeCID(value) - // continue traversing - if (value) { - cid = value - } - return false - } - }, - (err, results) => { - if (err) { - return callback(err) - } - return callback(null, { - value: value, - remainderPath: path, - cid - }) - } - ) - } - - getStream (cid, path, options) { - const deferred = pullDeferSource() - - this.get(cid, path, options, (err, result) => { - if (err) { - return deferred.resolve( - pull.error(err) - ) - } - deferred.resolve( - pull.values([result]) - ) - }) + }) + } - return deferred + return fancyIterator(next) } /** @@ -347,25 +310,6 @@ class IPLDResolver { /* */ /* internals */ /* */ - - _get (cid, callback) { - waterfall([ - (cb) => this._getFormat(cid.codec, cb), - (format, cb) => this.bs.get(cid, (err, block) => { - if (err) return cb(err) - cb(null, format, block) - }), - (format, block, cb) => { - format.util.deserialize(block.data, (err, deserialized) => { - if (err) { - return cb(err) - } - cb(null, deserialized) - }) - } - ], callback) - } - _getFormat (codec, callback) { if (this.resolvers[codec]) { return callback(null, this.resolvers[codec]) diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..1e86375 --- /dev/null +++ b/src/util.js @@ -0,0 +1,32 @@ +'use strict' + +exports.first = async (iterator) => { + for await (const value of iterator) { + return value + } +} + +exports.last = async (iterator) => { + let value + for await (value of iterator) { + // Intentionally empty + } + return value +} + +exports.all = async (iterator) => { + const values = [] + for await (const value of iterator) { + values.push(value) + } + return values +} + +exports.fancyIterator = (next) => { + const iterator = { next } + iterator[Symbol.asyncIterator] = function () { return this } + iterator.first = () => exports.first(iterator) + iterator.last = () => exports.last(iterator) + iterator.all = () => exports.all(iterator) + return iterator +} diff --git a/test/basics.js b/test/basics.js index 42ad2a8..1bcfea0 100644 --- a/test/basics.js +++ b/test/basics.js @@ -3,8 +3,10 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') +const chaiAsProised = require('chai-as-promised') const expect = chai.expect chai.use(dirtyChai) +chai.use(chaiAsProised) const BlockService = require('ipfs-block-service') const CID = require('cids') const multihash = require('multihashes') @@ -33,29 +35,28 @@ module.exports = (repo) => { }) describe('validation', () => { - it('get - errors on unknown resolver', (done) => { + it('resolve - errors on unknown resolver', async () => { const bs = new BlockService(repo) const r = new IPLDResolver({ blockService: bs }) // choosing a format that is not supported const cid = new CID(1, 'base1', multihash.encode(Buffer.from('abcd', 'hex'), 'sha1')) - r.get(cid, '/', {}, (err, result) => { - expect(err).to.exist() - expect(err.message).to.eql('No resolver found for codec "base1"') - done() - }) + const result = r.resolve(cid, '') + await expect(result.next()).to.be.rejectedWith( + 'No resolver found for codec "base1"') }) - it('_get - errors on unknown resolver', (done) => { - const bs = new BlockService(repo) - const r = new IPLDResolver({ blockService: bs }) - // choosing a format that is not supported - const cid = new CID(1, 'base1', multihash.encode(Buffer.from('abcd', 'hex'), 'sha1')) - r.get(cid, (err, result) => { - expect(err).to.exist() - expect(err.message).to.eql('No resolver found for codec "base1"') - done() - }) - }) + // TODO vmx 2018-11-29 Change this test to use `get()`. + // it('_get - errors on unknown resolver', (done) => { + // const bs = new BlockService(repo) + // const r = new IPLDResolver({ blockService: bs }) + // // choosing a format that is not supported + // const cid = new CID(1, 'base1', multihash.encode(Buffer.from('abcd', 'hex'), 'sha1')) + // r.get(cid, (err, result) => { + // expect(err).to.exist() + // expect(err.message).to.eql('No resolver found for codec "base1"') + // done() + // }) + // } it('put - errors on unknown resolver', (done) => { const bs = new BlockService(repo) diff --git a/test/format-support.js b/test/format-support.js index be05e1e..0cfb9da 100644 --- a/test/format-support.js +++ b/test/format-support.js @@ -3,8 +3,10 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') +const chaiAsProised = require('chai-as-promised') const expect = chai.expect chai.use(dirtyChai) +chai.use(chaiAsProised) const BlockService = require('ipfs-block-service') const dagCBOR = require('ipld-dag-cbor') @@ -28,21 +30,19 @@ module.exports = (repo) => { }) describe('Dynamic format loading', () => { - it('should fail to dynamically load format', (done) => { + it('should fail to dynamically load format', async () => { const bs = new BlockService(repo) const resolver = new IPLDResolver({ blockService: bs, formats: [] }) - resolver.get(cid, '/', (err) => { - expect(err).to.exist() - expect(err.message).to.equal('No resolver found for codec "dag-cbor"') - done() - }) + const result = resolver.resolve(cid, '') + await expect(result.next()).to.be.rejectedWith( + 'No resolver found for codec "dag-cbor"') }) - it('should fail to dynamically load format via loadFormat option', (done) => { + it('should fail to dynamically load format via loadFormat option', async () => { const errMsg = 'BOOM' + Date.now() const bs = new BlockService(repo) const resolver = new IPLDResolver({ @@ -54,14 +54,11 @@ module.exports = (repo) => { } }) - resolver.get(cid, '/', (err) => { - expect(err).to.exist() - expect(err.message).to.equal(errMsg) - done() - }) + const result = resolver.resolve(cid, '') + await expect(result.next()).to.be.rejectedWith(errMsg) }) - it('should dynamically load missing format', (done) => { + it('should dynamically load missing format', async () => { const bs = new BlockService(repo) const resolver = new IPLDResolver({ blockService: bs, @@ -72,14 +69,12 @@ module.exports = (repo) => { } }) - resolver.get(cid, '/', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(data) - done() - }) + const result = resolver.resolve(cid, '') + const node = await result.first() + expect(node.value).to.eql(data) }) - it('should not dynamically load format added statically', (done) => { + it('should not dynamically load format added statically', async () => { const bs = new BlockService(repo) const resolver = new IPLDResolver({ blockService: bs, @@ -89,11 +84,8 @@ module.exports = (repo) => { } }) - resolver.get(cid, '/', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(data) - done() - }) + const result = resolver.resolve(cid, '') + await result.next() }) }) }) diff --git a/test/ipld-all.js b/test/ipld-all.js index a75e50a..18b8443 100644 --- a/test/ipld-all.js +++ b/test/ipld-all.js @@ -61,12 +61,16 @@ describe('IPLD Resolver for dag-cbor + dag-pb', () => { ], done) }) - it('resolve through different formats', (done) => { - resolver.get(cidCbor, 'pb/Data', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(Buffer.from('I am inside a Protobuf')) - done() - }) + it('resolve through different formats', async () => { + const result = resolver.resolve(cidCbor, 'pb/Data') + + const node1 = await result.first() + expect(node1.remainderPath).to.eql('Data') + expect(node1.value).to.eql(cidPb) + + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql(Buffer.from('I am inside a Protobuf')) }) it('does not store nodes when onlyHash is passed', (done) => { diff --git a/test/ipld-bitcoin.js b/test/ipld-bitcoin.js index f3421f1..a97e7a7 100644 --- a/test/ipld-bitcoin.js +++ b/test/ipld-bitcoin.js @@ -107,16 +107,17 @@ module.exports = (repo) => { }, done) }) - it('resolver._get', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - expect(node1.version).to.eql(result.value.version) - done() - }) - }) - }) + // TODO vmx 2018-11-30 Change this test to use `get()`. + // it('resolver._get', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // expect(node1.version).to.eql(result.value.version) + // done() + // }) + // }) + // }) }) describe('public api', () => { @@ -150,62 +151,75 @@ module.exports = (repo) => { }) }) - it('root path (same as get)', (done) => { - resolver.get(cid1, '/', (err, result) => { - expect(err).to.not.exist() - - ipldBitcoin.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) + // TODO vmx 2018-11-30: Implement getting the whole object properly + // it('root path (same as get)', (done) => { + // resolver.get(cid1, '/', (err, result) => { + // expect(err).to.not.exist() + // + // ipldBitcoin.util.cid(result.value, (err, cid) => { + // expect(err).to.not.exist() + // expect(cid).to.eql(cid1) + // done() + // }) + // }) + // }) + + it('resolves value within 1st node scope', async () => { + const result = resolver.resolve(cid1, 'version') + const node = await result.first() + expect(node.remainderPath).to.eql('') + expect(node.value).to.eql(1) }) - it('value within 1st node scope', (done) => { - resolver.get(cid1, 'version', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(1) - done() - }) - }) + it('resolves value within nested scope (1 level)', async () => { + const result = resolver.resolve(cid2, 'parent/version') - it('value within nested scope (1 level)', (done) => { - resolver.get(cid2, 'parent/version', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(1) - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('version') + expect(node1.value).to.eql(cid1) - it('value within nested scope (2 levels)', (done) => { - resolver.get(cid3, 'parent/parent/version', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(1) - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql(1) }) - it('resolver.remove', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - expect(result.value.version).to.eql(1) - remove() - }) - }) + it('resolves value within nested scope (2 levels)', async () => { + const result = resolver.resolve(cid3, 'parent/parent/version') - function remove () { - resolver.remove(cid1, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err) => { - expect(err).to.exist() - done() - }) - }) - } + const node1 = await result.first() + expect(node1.remainderPath).to.eql('parent/version') + expect(node1.value).to.eql(cid2) + + const node2 = await result.first() + expect(node2.remainderPath).to.eql('version') + expect(node2.value).to.eql(cid1) + + const node3 = await result.first() + expect(node3.remainderPath).to.eql('') + expect(node3.value).to.eql(1) }) + + // // TODO vmx 2018-11-30: remove this `get()` call with the new `get()` + // it('resolver.remove', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // expect(result.value.version).to.eql(1) + // remove() + // }) + // }) + // + // function remove () { + // resolver.remove(cid1, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err) => { + // expect(err).to.exist() + // done() + // }) + // }) + // } + // }) }) }) } diff --git a/test/ipld-dag-cbor.js b/test/ipld-dag-cbor.js index eacc3b9..afba4d8 100644 --- a/test/ipld-dag-cbor.js +++ b/test/ipld-dag-cbor.js @@ -3,8 +3,10 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') +const chaiAsProised = require('chai-as-promised') const expect = chai.expect chai.use(dirtyChai) +chai.use(chaiAsProised) const BlockService = require('ipfs-block-service') const dagCBOR = require('ipld-dag-cbor') const series = require('async/series') @@ -91,16 +93,17 @@ module.exports = (repo) => { }, done) }) - it('resolver._get', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver._get(cid1, (err, node) => { - expect(err).to.not.exist() - expect(node1).to.eql(node) - done() - }) - }) - }) + // TODO vmx 2018-11-30 Change this test to use `get()`. + // it('resolver._get', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver._get(cid1, (err, node) => { + // expect(err).to.not.exist() + // expect(node1).to.eql(node) + // done() + // }) + // }) + // }) }) describe('public api', () => { @@ -134,129 +137,70 @@ module.exports = (repo) => { }) }) - it('resolver.get just CID', (done) => { - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - - dagCBOR.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) - }) - - it('resolver.get root path', (done) => { - resolver.get(cid1, '/', (err, result) => { - expect(err).to.not.exist() - - dagCBOR.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) - }) - - it('resolver.get relative path `.` (same as get /)', (done) => { - resolver.get(cid1, '.', (err, result) => { - expect(err).to.not.exist() - - dagCBOR.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) + // TODO vmx 2018-11-30: Implement getting the whole object properly + // it('resolver.get root path', (done) => { + // resolver.get(cid1, '/', (err, result) => { + // expect(err).to.not.exist() + // + // dagCBOR.util.cid(result.value, (err, cid) => { + // expect(err).to.not.exist() + // expect(cid).to.eql(cid1) + // done() + // }) + // }) + // }) + + it('resolves value within 1st node scope', async () => { + const result = resolver.resolve(cid1, 'someData') + const node = await result.first() + expect(node.remainderPath).to.eql('') + expect(node.value).to.eql('I am 1') }) - it('resolver.get relative path `./` (same as get /)', (done) => { - resolver.get(cid1, './', (err, result) => { - expect(err).to.not.exist() + it('resolves value within nested scope (0 level)', async () => { + const result = resolver.resolve(cid2, 'one') - dagCBOR.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) - }) - - it('resolver.get relative path `./one/someData` (same as get one/someData)', (done) => { - resolver.get(cid2, './one/someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('') + expect(node1.value).to.eql(cid1) - it('resolver.get relative path `one/./someData` (same as get one/someData)', (done) => { - resolver.get(cid2, 'one/./someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql({ someData: 'I am 1' }) }) - it('resolver.get double slash at the beginning `//one/someData` (same as get one/someData)', (done) => { - resolver.get(cid2, '//one/someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - done() - }) - }) + it('resolves value within nested scope (1 level)', async () => { + const result = resolver.resolve(cid2, 'one/someData') - it('resolver.get double slash in the middle `one//someData` (same as get one/someData)', (done) => { - resolver.get(cid2, 'one//someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('someData') + expect(node1.value).to.eql(cid1) - it('resolver.get value within 1st node scope', (done) => { - resolver.get(cid1, 'someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql('I am 1') }) - it('resolver.get value within nested scope (0 level)', (done) => { - resolver.get(cid2, 'one', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql({ - someData: 'I am 1' - }) - done() - }) - }) + it('resolves value within nested scope (2 levels)', async () => { + const result = resolver.resolve(cid3, 'two/one/someData') - it('resolver.get value within nested scope (1 level)', (done) => { - resolver.get(cid2, 'one/someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('one/someData') + expect(node1.value).to.eql(cid2) - it('resolver.get value within nested scope (2 levels)', (done) => { - resolver.get(cid3, 'two/one/someData', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('I am 1') - expect(result.remainderPath).to.eql('') - expect(result.cid).to.deep.eql(cid1) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('someData') + expect(node2.value).to.eql(cid1) - done() - }) + const node3 = await result.first() + expect(node3.remainderPath).to.eql('') + expect(node3.value).to.eql('I am 1') }) - it('resolver.get calls callback for unavailable path', (done) => { - resolver.get(cid3, `foo/${Date.now()}`, (err) => { - expect(err).to.exist() - expect(err.message).to.contain('path not available') - done() - }) + it('fails resolving unavailable path', async () => { + const result = resolver.resolve(cid3, `foo/${Date.now()}`) + await expect(result.next()).to.be.rejectedWith( + 'path not available at root') }) it('resolver.tree', (done) => { @@ -330,26 +274,27 @@ module.exports = (repo) => { ) }) - it('resolver.remove', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - expect(node1).to.eql(result.value) - remove() - }) - }) - - function remove () { - resolver.remove(cid1, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err) => { - expect(err).to.exist() - done() - }) - }) - } - }) + // // TODO vmx 2018-11-30: remove this `get()` call with the new `get()` + // it('resolver.remove', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // expect(node1).to.eql(result.value) + // remove() + // }) + // }) + // + // function remove () { + // resolver.remove(cid1, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err) => { + // expect(err).to.exist() + // done() + // }) + // }) + // } + // }) }) }) } diff --git a/test/ipld-dag-pb.js b/test/ipld-dag-pb.js index bfadb8e..efffcfe 100644 --- a/test/ipld-dag-pb.js +++ b/test/ipld-dag-pb.js @@ -147,15 +147,16 @@ module.exports = (repo) => { }, done) }) - it('resolver._get', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver._get(cid1, (err, node) => { - expect(err).to.not.exist() - done() - }) - }) - }) + // TODO vmx 2018-11-29 Change this test to use `get()`. + // it('resolver._get', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver._get(cid1, (err, node) => { + // expect(err).to.not.exist() + // done() + // }) + // }) + // }) }) describe('public api', () => { @@ -189,98 +190,84 @@ module.exports = (repo) => { }) }) - it('resolver.get just CID', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (done)) - }) - }) + // TODO vmx 2018-11-29: Change this test to use the new `get()` + // it('resolver.get with empty path', (done) => { + // resolver.get(cid1, '/', (err, result) => { + // expect(err).to.not.exist() + // + // dagPB.util.cid(result.value, (err, cid) => { + // expect(err).to.not.exist() + // expect(cid).to.eql(cid1) + // done() + // }) + // }) + // }) - it('resolver.getStream', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - pull( - resolver.getStream(cid1), - pull.collect(done) - ) - }) + it('resolves a value within 1st node scope', async () => { + const result = resolver.resolve(cid1, 'Data') + const node = await result.first() + expect(node.remainderPath).to.eql('') + expect(node.value).to.eql(Buffer.from('I am 1')) }) - it('resolver.get root path', (done) => { - resolver.get(cid1, '/', (err, result) => { - expect(err).to.not.exist() + it('resolves a value within nested scope (1 level)', async () => { + const result = resolver.resolve(cid2, 'Links/0/Hash/Data') - dagPB.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('Data') + expect(node1.value).to.eql(cid1) - it('resolver.get value within 1st node scope', (done) => { - resolver.get(cid1, 'Data', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(Buffer.from('I am 1')) - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql(Buffer.from('I am 1')) }) - it('resolver.get value within nested scope (1 level)', (done) => { - resolver.get(cid2, 'Links/0/Hash/Data', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(Buffer.from('I am 1')) - done() - }) - }) + it('resolves value within nested scope (2 levels)', async () => { + const result = resolver.resolve(cid3, 'Links/1/Hash/Links/0/Hash/Data') - it('resolver.get value within nested scope (2 levels)', (done) => { - resolver.get(cid3, 'Links/1/Hash/Links/0/Hash/Data', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(Buffer.from('I am 1')) - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('Links/0/Hash/Data') + expect(node1.value).to.eql(cid2) - it('resolver.get with option localResolve: true', (done) => { - resolver.get(cid3, 'Links/1/Hash/Links/0/Hash/Data', { localResolve: true }, (err, result) => { - expect(err).to.not.exist() - expect(result.remainderPath).to.equal('Links/0/Hash/Data') - expect(result.value).to.eql({ - '/': 'QmS149H7EbyMuZ2wtEF1sAd7gPwjj4rKAorweAjKMkxr8D' - }) - expect(result.cid).to.deep.equal(cid2) - done() - }) - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('Data') + expect(node2.value).to.eql(cid1) - it('resolver.get value within nested scope (1 level) returns cid of node traversed to', (done) => { - resolver.get(cid2, 'Links/0/Hash/Data', (err, result) => { - expect(err).to.not.exist() - expect(result.cid).to.deep.equal(cid1) - done() - }) + const node3 = await result.first() + expect(node3.remainderPath).to.eql('') + expect(node3.value).to.eql(Buffer.from('I am 1')) }) - it('resolver.remove', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, node) => { - expect(err).to.not.exist() - remove() - }) - }) + // TODO vmx 2018-11-29: Think about if every returned node should contain + // a `cid` field or not + // it('resolver.get value within nested scope (1 level) returns cid of node traversed to', (done) => { + // resolver.get(cid2, 'Links/0/Hash/Data', (err, result) => { + // expect(err).to.not.exist() + // expect(result.cid).to.deep.equal(cid1) + // done() + // }) + // }) - function remove () { - resolver.remove(cid1, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err) => { - expect(err).to.exist() - done() - }) - }) - } - }) + // TODO vmx 2018-11-29: remove this `get()` call with the new `get()` + // it('resolver.remove', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, node) => { + // expect(err).to.not.exist() + // remove() + // }) + // }) + // + // function remove () { + // resolver.remove(cid1, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err) => { + // expect(err).to.exist() + // done() + // }) + // }) + // } + // }) }) }) } diff --git a/test/ipld-eth-block.js b/test/ipld-eth-block.js index bf86d16..1231380 100644 --- a/test/ipld-eth-block.js +++ b/test/ipld-eth-block.js @@ -95,18 +95,19 @@ module.exports = (repo) => { }, done) }) - it('resolver._get', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - expect(node1.number.toString('hex')).to.eql('01') - expect(node1.raw).to.eql(result.value.raw) - expect(node1.hash()).to.eql(result.value.hash()) - done() - }) - }) - }) + // TODO vmx 2018-11-30 Change this test to use `get()`. + // it('resolver._get', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // expect(node1.number.toString('hex')).to.eql('01') + // expect(node1.raw).to.eql(result.value.raw) + // expect(node1.hash()).to.eql(result.value.hash()) + // done() + // }) + // }) + // }) }) describe('public api', () => { @@ -140,64 +141,77 @@ module.exports = (repo) => { }) }) - it('root path (same as get)', (done) => { - resolver.get(cid1, '/', (err, result) => { - expect(err).to.not.exist() - - ipldEthBlock.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) + // TODO vmx 2018-11-30: Implement getting the whole object properly + // it('root path (same as get)', (done) => { + // resolver.get(cid1, '/', (err, result) => { + // expect(err).to.not.exist() + // + // ipldEthBlock.util.cid(result.value, (err, cid) => { + // expect(err).to.not.exist() + // expect(cid).to.eql(cid1) + // done() + // }) + // }) + // }) + + it('resolves value within 1st node scope', async () => { + const result = resolver.resolve(cid1, 'number') + const node = await result.first() + expect(node.remainderPath).to.eql('') + expect(node.value.toString('hex')).to.eql('01') }) - it('value within 1st node scope', (done) => { - resolver.get(cid1, 'number', (err, result) => { - expect(err).to.not.exist() - expect(result.value.toString('hex')).to.eql('01') - done() - }) - }) + it('resolves value within nested scope (1 level)', async () => { + const result = resolver.resolve(cid2, 'parent/number') - it('value within nested scope (1 level)', (done) => { - resolver.get(cid2, 'parent/number', (err, result) => { - expect(err).to.not.exist() - expect(result.value.toString('hex')).to.eql('01') - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('number') + expect(node1.value).to.eql(cid1) - it('value within nested scope (2 levels)', (done) => { - resolver.get(cid3, 'parent/parent/number', (err, result) => { - expect(err).to.not.exist() - expect(result.value.toString('hex')).to.eql('01') - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value.toString('hex')).to.eql('01') }) - it('resolver.remove', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - const node = result.value - expect(node1.raw).to.eql(node.raw) - expect(node1.hash()).to.eql(node.hash()) - remove() - }) - }) + it('resolves value within nested scope (2 levels)', async () => { + const result = resolver.resolve(cid3, 'parent/parent/number') - function remove () { - resolver.remove(cid1, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err) => { - expect(err).to.exist() - done() - }) - }) - } + const node1 = await result.first() + expect(node1.remainderPath).to.eql('parent/number') + expect(node1.value).to.eql(cid2) + + const node2 = await result.first() + expect(node2.remainderPath).to.eql('number') + expect(node2.value).to.eql(cid1) + + const node3 = await result.first() + expect(node3.remainderPath).to.eql('') + expect(node3.value.toString('hex')).to.eql('01') }) + + // TODO vmx 2018-11-30: remove this `get()` call with the new `get()` + // it('resolver.remove', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // const node = result.value + // expect(node1.raw).to.eql(node.raw) + // expect(node1.hash()).to.eql(node.hash()) + // remove() + // }) + // }) + // + // function remove () { + // resolver.remove(cid1, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err) => { + // expect(err).to.exist() + // done() + // }) + // }) + // } + // }) }) }) } diff --git a/test/ipld-eth.js b/test/ipld-eth.js index 9a02004..0e1579e 100644 --- a/test/ipld-eth.js +++ b/test/ipld-eth.js @@ -93,23 +93,24 @@ module.exports = (repo) => { } }) - describe('resolver.get', () => { - it('block-to-block', (done) => { - resolver.get(ethObjs.child.cid, '/parent', (err, result) => { - expect(err).to.not.exist() - expect(result.remainderPath).to.equal('') - expect(result.value.number.toString('hex')).to.equal('302516') - done() - }) + describe('resolver.resolve', () => { + it('block-to-block', async () => { + const result = resolver.resolve(ethObjs.child.cid, 'parent') + + const node1 = await result.first() + expect(node1.remainderPath).to.eql('') + + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value.number.toString('hex')).to.eql('302516') }) - it('block-to-account resolve', (done) => { - resolver.get(ethObjs.child.cid, '/parent/state/0/0/0/0/1/7/2/7/8/a/1/e/6/e/9/6/3/5/e/1/a/3/f/1/1/e/b/0/2/2/d/a/1/f/5/7/e/a/0/0/4/d/8/5/2/d/9/d/1/9/4/2/d/4/3/6/0/8/5/4/0/4/7/1/nonce', (err, result) => { - expect(err).to.not.exist() - expect(result.value.toString('hex'), '03') - expect(result.remainderPath).to.equal('') - done() - }) + it('block-to-account resolve', async () => { + const result = resolver.resolve(ethObjs.child.cid, + 'parent/state/0/0/0/0/1/7/2/7/8/a/1/e/6/e/9/6/3/5/e/1/a/3/f/1/1/e/b/0/2/2/d/a/1/f/5/7/e/a/0/0/4/d/8/5/2/d/9/d/1/9/4/2/d/4/3/6/0/8/5/4/0/4/7/1/nonce') + const node = await result.last() + expect(node.value.toString('hex'), '03') + expect(node.remainderPath).to.equal('') }) }) }) diff --git a/test/ipld-git.js b/test/ipld-git.js index 059b0f3..529986f 100644 --- a/test/ipld-git.js +++ b/test/ipld-git.js @@ -162,16 +162,17 @@ module.exports = (repo) => { }, done) }) - it('resolver._get', (done) => { - resolver.put(blobNode, { cid: blobCid }, (err) => { - expect(err).to.not.exist() - resolver.get(blobCid, (err, result) => { - expect(err).to.not.exist() - expect(blobNode.toString('hex')).to.eql(result.value.toString('hex')) - done() - }) - }) - }) + // TODO vmx 2018-11-30 Change this test to use `get()`. + // it('resolver._get', (done) => { + // resolver.put(blobNode, { cid: blobCid }, (err) => { + // expect(err).to.not.exist() + // resolver.get(blobCid, (err, result) => { + // expect(err).to.not.exist() + // expect(blobNode.toString('hex')).to.eql(result.value.toString('hex')) + // done() + // }) + // }) + // }) }) describe('public api', () => { @@ -205,79 +206,103 @@ module.exports = (repo) => { }) }) - it('resolver.get root path', (done) => { - resolver.get(blobCid, '/', (err, result) => { - expect(err).to.not.exist() + // TODO vmx 2018-11-30: Implement getting the whole object properly + // it('resolver.get empty path', (done) => { + // resolver.get(blobCid, '', (err, result) => { + // expect(err).to.not.exist() + // + // ipldGit.util.cid(result.value, (err, cid) => { + // expect(err).to.not.exist() + // expect(cid).to.eql(blobCid) + // done() + // }) + // }) + // }) - ipldGit.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(blobCid) - done() - }) - }) + it('resolves value within 1st node scope', async () => { + const result = resolver.resolve(commitCid, 'message') + const node = await result.first() + expect(node.remainderPath).to.eql('') + expect(node.value).to.eql('Initial commit\n') }) - it('value within 1st node scope', (done) => { - resolver.get(commitCid, 'message', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('Initial commit\n') - done() - }) - }) + it('resolves value within nested node scope (commit/tree)', async () => { + const result = resolver.resolve(commitCid, 'tree/somefile/mode') - it('value within nested node scope (commit/tree)', (done) => { - resolver.get(commitCid, 'tree/somefile/mode', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql('100644') - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('somefile/mode') + expect(node1.value).to.eql(treeCid) - it('value within nested node scope (commit/tree/blob)', (done) => { - resolver.get(commitCid, 'tree/somefile/hash', (err, result) => { - expect(err).to.not.exist() - expect(blobNode.toString('hex')).to.eql(result.value.toString('hex')) - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql('100644') }) - it('value within nested node scope (commit/commit/tree/blob)', (done) => { - resolver.get(commit2Cid, 'parents/0/tree/somefile/hash', (err, result) => { - expect(err).to.not.exist() - expect(blobNode.toString('hex')).to.eql(result.value.toString('hex')) - done() - }) + it('resolves value within nested node scope (commit/tree/blob)', async () => { + const result = resolver.resolve(commitCid, 'tree/somefile/hash') + + const node1 = await result.first() + expect(node1.remainderPath).to.eql('somefile/hash') + expect(node1.value).to.eql(treeCid) + + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql(blobCid) + + const node3 = await result.first() + expect(node3.remainderPath).to.eql('') + expect(node3.value).to.eql(blobNode) }) - it('value within nested node scope (tag/commit/commit/tree/blob)', (done) => { - resolver.get(tagCid, 'object/parents/0/tree/somefile/hash', (err, result) => { - expect(err).to.not.exist() - expect(blobNode.toString('hex')).to.eql(result.value.toString('hex')) - done() - }) + it('resolves value within nested node scope (commit/commit/tree/blob)', async () => { + const result = resolver.resolve(commit2Cid, 'parents/0/tree/somefile/hash') + + const node1 = await result.first() + expect(node1.remainderPath).to.eql('tree/somefile/hash') + expect(node1.value).to.eql(commitCid) + + // The nodes in between were already tested by some other test + const last = await result.last() + expect(last.remainderPath).to.eql('') + expect(last.value).to.eql(blobNode) }) - it('resolver.remove', (done) => { - resolver.put(blobNode, { cid: blobCid }, (err) => { - expect(err).to.not.exist() - resolver.get(blobCid, (err, result) => { - expect(err).to.not.exist() - const node = result.value - expect(blobNode.toString('hex')).to.eql(node.toString('hex')) - remove() - }) - }) + it('resolves value within nested node scope (tag/commit/commit/tree/blob)', async () => { + const result = resolver.resolve(tagCid, + 'object/parents/0/tree/somefile/hash') - function remove () { - resolver.remove(blobCid, (err) => { - expect(err).to.not.exist() - resolver.get(blobCid, (err) => { - expect(err).to.exist() - done() - }) - }) - } + const node1 = await result.first() + expect(node1.remainderPath).to.eql('parents/0/tree/somefile/hash') + expect(node1.value).to.eql(commit2Cid) + + // The nodes in between were already tested by some other test + const last = await result.last() + expect(last.remainderPath).to.eql('') + expect(last.value).to.eql(blobNode) }) + + // // TODO vmx 2018-11-30: remove this `get()` call with the new `get()` + // it('resolver.remove', (done) => { + // resolver.put(blobNode, { cid: blobCid }, (err) => { + // expect(err).to.not.exist() + // resolver.get(blobCid, (err, result) => { + // expect(err).to.not.exist() + // const node = result.value + // expect(blobNode.toString('hex')).to.eql(node.toString('hex')) + // remove() + // }) + // }) + // + // function remove () { + // resolver.remove(blobCid, (err) => { + // expect(err).to.not.exist() + // resolver.get(blobCid, (err) => { + // expect(err).to.exist() + // done() + // }) + // }) + // } + // }) }) }) } diff --git a/test/ipld-zcash.js b/test/ipld-zcash.js index c8bba30..0980e5d 100644 --- a/test/ipld-zcash.js +++ b/test/ipld-zcash.js @@ -112,16 +112,17 @@ module.exports = (repo) => { }, done) }) - it('resolver._get', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - expect(node1.version).to.eql(result.value.version) - done() - }) - }) - }) + // // TODO vmx 2018-11-30 Change this test to use `get()`. + // it('resolver._get', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // expect(node1.version).to.eql(result.value.version) + // done() + // }) + // }) + // }) }) describe('public api', () => { @@ -155,62 +156,75 @@ module.exports = (repo) => { }) }) - it('root path (same as get)', (done) => { - resolver.get(cid1, '/', (err, result) => { - expect(err).to.not.exist() - - ipldZcash.util.cid(result.value, (err, cid) => { - expect(err).to.not.exist() - expect(cid).to.eql(cid1) - done() - }) - }) + // // TODO vmx 2018-11-30: Implement getting the whole object properly + // it('root path (same as get)', (done) => { + // resolver.get(cid1, '/', (err, result) => { + // expect(err).to.not.exist() + // + // ipldZcash.util.cid(result.value, (err, cid) => { + // expect(err).to.not.exist() + // expect(cid).to.eql(cid1) + // done() + // }) + // }) + // }) + + it('resolves value within 1st node scope', async () => { + const result = resolver.resolve(cid1, 'version') + const node = await result.first() + expect(node.remainderPath).to.eql('') + expect(node.value).to.eql(1) }) - it('value within 1st node scope', (done) => { - resolver.get(cid1, 'version', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(1) - done() - }) - }) + it('resolves value within nested scope (1 level)', async () => { + const result = resolver.resolve(cid2, 'parent/version') - it('value within nested scope (1 level)', (done) => { - resolver.get(cid2, 'parent/version', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(1) - done() - }) - }) + const node1 = await result.first() + expect(node1.remainderPath).to.eql('version') + expect(node1.value).to.eql(cid1) - it('value within nested scope (2 levels)', (done) => { - resolver.get(cid3, 'parent/parent/version', (err, result) => { - expect(err).to.not.exist() - expect(result.value).to.eql(1) - done() - }) + const node2 = await result.first() + expect(node2.remainderPath).to.eql('') + expect(node2.value).to.eql(1) }) - it('resolver.remove', (done) => { - resolver.put(node1, { cid: cid1 }, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err, result) => { - expect(err).to.not.exist() - expect(result.value.version).to.eql(1) - remove() - }) - }) + it('resolves value within nested scope (2 levels)', async () => { + const result = resolver.resolve(cid3, 'parent/parent/version') - function remove () { - resolver.remove(cid1, (err) => { - expect(err).to.not.exist() - resolver.get(cid1, (err) => { - expect(err).to.exist() - done() - }) - }) - } + const node1 = await result.first() + expect(node1.remainderPath).to.eql('parent/version') + expect(node1.value).to.eql(cid2) + + const node2 = await result.first() + expect(node2.remainderPath).to.eql('version') + expect(node2.value).to.eql(cid1) + + const node3 = await result.first() + expect(node3.remainderPath).to.eql('') + expect(node3.value).to.eql(1) }) + + // // TODO vmx 2018-11-30: remove this `get()` call with the new `get()` + // it('resolver.remove', (done) => { + // resolver.put(node1, { cid: cid1 }, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err, result) => { + // expect(err).to.not.exist() + // expect(result.value.version).to.eql(1) + // remove() + // }) + // }) + // + // function remove () { + // resolver.remove(cid1, (err) => { + // expect(err).to.not.exist() + // resolver.get(cid1, (err) => { + // expect(err).to.exist() + // done() + // }) + // }) + // } + // }) }) }) }