From f3081ab12a28d84869740bb38a20bd294e05aa40 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Thu, 2 Aug 2018 10:57:41 -0700 Subject: [PATCH] test: add tests and docs for ipfs.resolve (#332) License: MIT Signed-off-by: Alan Shaw --- SPEC/MISCELLANEOUS.md | 71 ++++++++++++++++++++ js/src/miscellaneous/index.js | 3 +- js/src/miscellaneous/resolve.js | 113 ++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 js/src/miscellaneous/resolve.js diff --git a/SPEC/MISCELLANEOUS.md b/SPEC/MISCELLANEOUS.md index a45a4af0..b2b57d0d 100644 --- a/SPEC/MISCELLANEOUS.md +++ b/SPEC/MISCELLANEOUS.md @@ -7,6 +7,7 @@ * [ping](#ping) * [pingPullStream](#pingpullstream) * [pingReadableStream](#pingreadablestream) +* [resolve](#resolve) #### `id` @@ -242,6 +243,76 @@ stream.on('data', (res) => { A great source of [examples][] can be found in the tests for this API. +#### `resolve` + +> Resolve the value of names to IPFS + +There are a number of mutable name protocols that can link among themselves and into IPNS. For example IPNS references can (currently) point at an IPFS object, and DNS links can point at other DNS links, IPNS entries, or IPFS objects. This command accepts any of these identifiers and resolves them to the referenced item. + +##### `Go` **WIP** + +##### `JavaScript` - ipfs.resolve(name, [options], [callback]) + +Where: + +- `name` (string): The name to resolve +- `options` is an optional object that might include the following properties: + - `recursive` (boolean, default false): Resolve until the result is an IPFS name + +`callback` must follow `function (err, res) {}` signature, where `err` is an error if the operation was not successful. `res` is a string, the resolved name. + +If no `callback` is passed, a promise is returned. + +**Examples:** + +Resolve the value of your identity: + +```JavaScript +const name = '/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy' + +ipfs.resolve(name, (err, res) => { + if (err) { + throw err + } + console.log(res) // /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj +}) +``` + +Resolve the value of another name recursively: + +```JavaScript +const name = '/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n' + +// Where: +// /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n +// ...resolves to: +// /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy +// ...which in turn resolves to: +// /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + +ipfs.resolve(name, { recursive: true }, (err, res) => { + if (err) { + throw err + } + console.log(res) // /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj +}) +``` + +Resolve the value of an IPFS path: + +```JavaScript +const name = '/ipfs/QmeZy1fGbwgVSrqbfh9fKQrAWgeyRnj7h8fsHS1oy3k99x/beep/boop' + +ipfs.resolve(name, (err, res) => { + if (err) { + throw err + } + console.log(res) // /ipfs/QmYRMjyvAiHKN9UTi8Bzt1HUspmSRD8T8DwxfSMzLgBon1 +}) +``` + +A great source of [examples][] can be found in the tests for this API. + [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/js/src/miscellaneous [rs]: https://www.npmjs.com/package/readable-stream [ps]: https://www.npmjs.com/package/pull-stream diff --git a/js/src/miscellaneous/index.js b/js/src/miscellaneous/index.js index b3a5dc60..82f7f08e 100644 --- a/js/src/miscellaneous/index.js +++ b/js/src/miscellaneous/index.js @@ -5,7 +5,8 @@ const tests = { id: require('./id'), version: require('./version'), dns: require('./dns'), - stop: require('./stop') + stop: require('./stop'), + resolve: require('./resolve') } module.exports = createSuite(tests) diff --git a/js/src/miscellaneous/resolve.js b/js/src/miscellaneous/resolve.js new file mode 100644 index 00000000..f9a0f3c9 --- /dev/null +++ b/js/src/miscellaneous/resolve.js @@ -0,0 +1,113 @@ +/* eslint-env mocha */ +'use strict' + +const isIpfs = require('is-ipfs') +const loadFixture = require('aegir/fixtures') +const hat = require('hat') +const { getDescribe, getIt, expect } = require('../utils/mocha') + +module.exports = (createCommon, options) => { + const describe = getDescribe(options) + const it = getIt(options) + const common = createCommon() + + describe('.resolve', () => { + let ipfs + + before(function (done) { + // CI takes longer to instantiate the daemon, so we need to increase the + // timeout for the before step + this.timeout(60 * 1000) + + common.setup((err, factory) => { + expect(err).to.not.exist() + factory.spawnNode((err, node) => { + expect(err).to.not.exist() + ipfs = node + done() + }) + }) + }) + + after((done) => { + common.teardown(done) + }) + + it('should resolve an IPFS hash', (done) => { + const content = loadFixture('js/test/fixtures/testfile.txt', 'interface-ipfs-core') + + ipfs.files.add(content, (err, res) => { + expect(err).to.not.exist() + expect(isIpfs.cid(res[0].hash)).to.be.true() + + ipfs.resolve(`/ipfs/${res[0].hash}`, (err, path) => { + expect(err).to.not.exist() + expect(path).to.equal(`/ipfs/${res[0].hash}`) + done() + }) + }) + }) + + // Test resolve turns /ipfs/QmRootHash/path/to/file into /ipfs/QmFileHash + it('should resolve an IPFS path link', (done) => { + const path = '/path/to/testfile.txt' + const content = loadFixture('js/test/fixtures/testfile.txt', 'interface-ipfs-core') + + ipfs.files.add([{ path, content }], { wrapWithDirectory: true }, (err, res) => { + expect(err).to.not.exist() + + const rootHash = res.find(r => r.path === '').hash + const fileHash = res.find(r => r.path === path).hash + + ipfs.resolve(`/ipfs/${rootHash}${path}`, (err, path) => { + expect(err).to.not.exist() + expect(path).to.equal(`/ipfs/${fileHash}`) + done() + }) + }) + }) + + it('should not resolve an IPFS path non-link', (done) => { + const content = { path: { to: { file: hat() } } } + const options = { format: 'dag-cbor', hashAlg: 'sha2-256' } + + ipfs.dag.put(content, options, (err, cid) => { + expect(err).to.not.exist() + + const path = `/ipfs/${cid.toBaseEncodedString()}/path/to/file` + ipfs.resolve(path, (err, path) => { + expect(err).to.exist() + expect(err.message).to.equal('found non-link at given path') + done() + }) + }) + }) + + // Test resolve turns /ipns/domain.com into /ipfs/QmHash + it('should resolve an IPNS DNS link', function (done) { + this.timeout(20 * 1000) + + ipfs.resolve('/ipns/ipfs.io', (err, path) => { + expect(err).to.not.exist() + expect(isIpfs.ipfsPath(path)).to.be.true() + done() + }) + }) + + // Test resolve turns /ipns/QmPeerHash into /ipns/domain.com into /ipfs/QmHash + it('should resolve IPNS link recursively', function (done) { + this.timeout(2 * 60 * 1000) + + ipfs.name.publish('/ipns/ipfs.io', { resolve: false }, (err, res) => { + expect(err).to.not.exist() + + ipfs.resolve(`/ipns/${res.name}`, { recursive: true }, (err, res) => { + expect(err).to.not.exist() + expect(res).to.not.equal('/ipns/ipfs.io') + expect(isIpfs.ipfsPath(res)).to.be.true() + done() + }) + }) + }) + }) +}