diff --git a/src/core/components/resolve.js b/src/core/components/resolve.js index bd23a33b20..8354f3f796 100644 --- a/src/core/components/resolve.js +++ b/src/core/components/resolve.js @@ -34,17 +34,15 @@ module.exports = (self) => { const path = split.slice(3).join('/') - resolve(cid, path, (err, cid) => { + resolve(cid, path, (err, cid, remainder) => { if (err) return cb(err) - if (!cid) return cb(new Error('found non-link at given path')) - cb(null, `/ipfs/${cidToString(cid, { base: opts.cidBase })}`) + cb(null, `/ipfs/${cidToString(cid, { base: opts.cidBase })}${remainder ? '/' + remainder : ''}`) }) }) // Resolve the given CID + path to a CID. function resolve (cid, path, callback) { - let value - + let value, remainder doUntil( (cb) => { self.block.get(cid, (err, block) => { @@ -59,28 +57,27 @@ module.exports = (self) => { r.resolver.resolve(block.data, path, (err, result) => { if (err) return cb(err) value = result.value - path = result.remainderPath + remainder = result.remainderPath cb() }) }) }, () => { - const endReached = !path || path === '/' - - if (endReached) { - return true - } - - if (value) { + if (value && value['/']) { + // If we've hit a CID, replace the current CID. cid = new CID(value['/']) + path = remainder + } else { + // We've hit a value. Return the current CID and the remaining path. + return true } - return false + // Continue resolving unless the path is empty. + return !path || path === '/' }, (err) => { if (err) return callback(err) - if (value && value['/']) return callback(null, new CID(value['/'])) - callback() + callback(null, cid, path) } ) } diff --git a/test/core/resolve.spec.js b/test/core/resolve.spec.js new file mode 100644 index 0000000000..7950e0daa9 --- /dev/null +++ b/test/core/resolve.spec.js @@ -0,0 +1,57 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('resolve', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 }, + config: { Bootstrap: [] } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + it('should resolve an IPFS path non-link', (done) => { + const content = { path: { to: { file: 'foobar' } } } + const options = { format: 'dag-cbor', hashAlg: 'sha2-256' } + + ipfs.dag.put(content, options, (err, cid) => { + expect(err).to.not.exist() + + // FIXME: This should be /ipld/... but that's not supported yet. + const path = `/ipfs/${cid.toBaseEncodedString()}/path/to/file` + ipfs.resolve(path, (err, resolved) => { + expect(err).to.not.exist() + expect(resolved).to.equal(path) + done() + }) + }) + }) +})