Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
feat: add support to resolve dns to ipns (#458)
Browse files Browse the repository at this point in the history
* feat: add support to resolve dns to ipns

This PR also normalizes `ipns name resolve` return value and offline options, to integrate better with http client and ipfs core.

* fix: fix feedback

* feat: add append remainder tests

* fix: chai chaining for some reason doesnt work

* fix: more pubsub tests fixes

* fix: cleanup dns tests

'should resolve a DNS link' is a duplicate plus the `r: true` isn't documented

'should non-recursively resolve ipfs.io' doesn't need to be skiped anymore

* fix: refactor tests

* fix: make recursive true by default

* chore: fix linter

* fix: skip some ping test cause of go

* fix: remove chai as promised

* fix: unskip ping tests
  • Loading branch information
hugomrdias authored and Alan Shaw committed Jun 20, 2019
1 parent ca9a6e0 commit cd41a3c
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 105 deletions.
19 changes: 4 additions & 15 deletions src/miscellaneous/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ module.exports = (createCommon, options) => {
const it = getIt(options)
const common = createCommon()

describe('.dns', () => {
describe('.dns', function () {
this.timeout(10 * 1000)
this.retries(3)
let ipfs

before(function (done) {
Expand All @@ -30,20 +32,7 @@ module.exports = (createCommon, options) => {
common.teardown(done)
})

it('should resolve a DNS link', function (done) {
this.timeout(20 * 1000)
this.retries(3)

ipfs.dns('ipfs.io', { r: true }, (err, path) => {
expect(err).to.not.exist()
expect(path).to.exist()
done()
})
})

// skipping because there is an error in https://ipfs.io/api/v0/dns?arg=ipfs.io
// unskip when it is resolved and the new version released: https://github.com/ipfs/go-ipfs/issues/6086
it.skip('should non-recursively resolve ipfs.io', () => {
it('should non-recursively resolve ipfs.io', () => {
return ipfs.dns('ipfs.io', { recursive: false }).then(res => {
// matches pattern /ipns/<ipnsaddress>
expect(res).to.match(/\/ipns\/.+$/)
Expand Down
17 changes: 13 additions & 4 deletions src/name/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = (createCommon, options) => {
const it = getIt(options)
const common = createCommon()

describe('.name.publish', function () {
describe('.name.publish offline', function () {
const keyName = hat()
let ipfs
let nodeId
Expand Down Expand Up @@ -43,7 +43,7 @@ module.exports = (createCommon, options) => {

const value = fixture.cid

ipfs.name.publish(value, (err, res) => {
ipfs.name.publish(value, { 'allow-offline': true }, (err, res) => {
expect(err).to.not.exist()
expect(res).to.exist()
expect(res.name).to.equal(nodeId)
Expand All @@ -53,6 +53,13 @@ module.exports = (createCommon, options) => {
})
})

it('should publish correctly with the lifetime option and resolve', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should publish correctly with the lifetime option and resolve'))
await ipfs.name.publish(path, { 'allow-offline': true, resolve: false, lifetime: '2h' })

return expect(await ipfs.name.resolve(`/ipns/${nodeId}`)).to.eq(`/ipfs/${path}`)
})

it('should publish correctly when the file was not added but resolve is disabled', function (done) {
this.timeout(50 * 1000)

Expand All @@ -62,7 +69,8 @@ module.exports = (createCommon, options) => {
resolve: false,
lifetime: '1m',
ttl: '10s',
key: 'self'
key: 'self',
'allow-offline': true
}

ipfs.name.publish(value, options, (err, res) => {
Expand All @@ -83,7 +91,8 @@ module.exports = (createCommon, options) => {
resolve: false,
lifetime: '24h',
ttl: '10s',
key: keyName
key: keyName,
'allow-offline': true
}

ipfs.key.gen(keyName, { type: 'rsa', size: 2048 }, function (err, key) {
Expand Down
207 changes: 125 additions & 82 deletions src/name/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,20 @@
/* eslint-env mocha */
'use strict'

const hat = require('hat')

const { fixture } = require('./utils')
const { spawnNodeWithId } = require('../utils/spawn')
const { getDescribe, getIt, expect } = require('../utils/mocha')
const delay = require('../utils/delay')

module.exports = (createCommon, options) => {
const describe = getDescribe(options)
const it = getIt(options)
const common = createCommon()

describe('.name.resolve', function () {
const keyName = hat()
describe('.name.resolve offline', function () {
const common = createCommon()
let ipfs
let nodeId
let keyId

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()

Expand All @@ -32,106 +24,157 @@ module.exports = (createCommon, options) => {

ipfs = node
nodeId = node.peerId.id

ipfs.add(fixture.data, { pin: false }, done)
done()
})
})
})

after((done) => common.teardown(done))

it('should resolve a record with the default params after a publish', function (done) {
this.timeout(50 * 1000)
it('should resolve a record default options', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record default options'))

const value = fixture.cid
const { id: keyId } = await ipfs.key.gen('key-name-default', { type: 'rsa', size: 2048 })

ipfs.name.publish(value, (err, res) => {
expect(err).to.not.exist()
expect(res).to.exist()
await ipfs.name.publish(path, { 'allow-offline': true })
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name-default' })

ipfs.name.resolve(nodeId, (err, res) => {
expect(err).to.not.exist()
expect(res).to.exist()
expect(res.path).to.equal(`/ipfs/${value}`)
return expect(await ipfs.name.resolve(`/ipns/${keyId}`))
.to.eq(`/ipfs/${path}`)
})

done()
})
})
it('should resolve a record recursive === false', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive === false'))
await ipfs.name.publish(path, { 'allow-offline': true })
return expect(await ipfs.name.resolve(`/ipns/${nodeId}`, { recursive: false }))
.to.eq(`/ipfs/${path}`)
})

it('should not get the entry if its validity time expired', function (done) {
this.timeout(50 * 1000)
it('should resolve a record recursive === true', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive === true'))

const value = fixture.cid
const publishOptions = {
resolve: true,
lifetime: '1ms',
ttl: '10s',
key: 'self'
}
const { id: keyId } = await ipfs.key.gen('key-name', { type: 'rsa', size: 2048 })

ipfs.name.publish(value, publishOptions, (err, res) => {
expect(err).to.not.exist()
expect(res).to.exist()

// guarantee that the record has an expired validity.
setTimeout(function () {
ipfs.name.resolve(nodeId, (err, res) => {
expect(err).to.exist()
expect(err.message).to.equal('record has expired')
expect(res).to.not.exist()

done()
})
}, 1)
})
await ipfs.name.publish(path, { 'allow-offline': true })
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name' })

return expect(await ipfs.name.resolve(`/ipns/${keyId}`, { recursive: true }))
.to.eq(`/ipfs/${path}`)
})

it('should resolve a record default options with remainder', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record default options with remainder'))

const { id: keyId } = await ipfs.key.gen('key-name-remainder-default', { type: 'rsa', size: 2048 })

await ipfs.name.publish(path, { 'allow-offline': true })
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name-remainder-default' })

return expect(await ipfs.name.resolve(`/ipns/${keyId}/remainder/file.txt`))
.to.eq(`/ipfs/${path}/remainder/file.txt`)
})

it('should resolve a record recursive === false with remainder', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive = false with remainder'))
await ipfs.name.publish(path, { 'allow-offline': true })
return expect(await ipfs.name.resolve(`/ipns/${nodeId}/remainder/file.txt`, { recursive: false }))
.to.eq(`/ipfs/${path}/remainder/file.txt`)
})

it('should recursively resolve to an IPFS hash', function (done) {
this.timeout(100 * 1000)
it('should resolve a record recursive === true with remainder', async () => {
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive = true with remainder'))

const { id: keyId } = await ipfs.key.gen('key-name-remainder', { type: 'rsa', size: 2048 })

const value = fixture.cid
await ipfs.name.publish(path, { 'allow-offline': true })
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name-remainder' })

return expect(await ipfs.name.resolve(`/ipns/${keyId}/remainder/file.txt`, { recursive: true }))
.to.eq(`/ipfs/${path}/remainder/file.txt`)
})

it('should not get the entry if its validity time expired', async () => {
const publishOptions = {
resolve: false,
lifetime: '24h',
lifetime: '100ms',
ttl: '10s',
key: 'self'
'allow-offline': true
}

// Generate new key
ipfs.key.gen(keyName, { type: 'rsa', size: 2048 }, (err, key) => {
expect(err).to.not.exist()
// we add new data instead of re-using fixture to make sure lifetime handling doesn't break
const [{ path }] = await ipfs.add(Buffer.from('should not get the entry if its validity time expired'))
await ipfs.name.publish(path, publishOptions)
await delay(500)
// go only has 1 possible error https://github.com/ipfs/go-ipfs/blob/master/namesys/interface.go#L51
// so here we just expect an Error and don't match the error type to expiration
try {
await ipfs.name.resolve(nodeId)
} catch (error) {
expect(error).to.exist()
}
})
})

describe('.name.resolve dns', function () {
const common = createCommon()
let ipfs
this.retries(3)

keyId = key.id
before(function (done) {
common.setup((err, factory) => {
expect(err).to.not.exist()

// publish ipfs
ipfs.name.publish(value, publishOptions, (err, res) => {
spawnNodeWithId(factory, (err, node) => {
expect(err).to.not.exist()
expect(res).to.exist()

publishOptions.key = keyName
ipfs = node
done()
})
})
})

// publish ipns with the generated key
ipfs.name.publish(`/ipns/${nodeId}`, publishOptions, (err, res) => {
expect(err).to.not.exist()
expect(res).to.exist()
after((done) => common.teardown(done))

const resolveOptions = {
nocache: false,
recursive: true
}
it('should resolve /ipns/ipfs.io', async () => {
return expect(await ipfs.name.resolve('/ipns/ipfs.io'))
.to.match(/\/ipfs\/.+$/)
})

// recursive resolve (will get ipns first, and will resolve again to find the ipfs)
ipfs.name.resolve(keyId, resolveOptions, (err, res) => {
expect(err).to.not.exist()
expect(res).to.exist()
expect(res.path).to.equal(`/ipfs/${value}`)
it('should resolve /ipns/ipfs.io recursive === false', async () => {
return expect(await ipfs.name.resolve('/ipns/ipfs.io', { recursive: false }))
.to.match(/\/ipns\/.+$/)
})

done()
})
})
})
})
it('should resolve /ipns/ipfs.io recursive === true', async () => {
return expect(await ipfs.name.resolve('/ipns/ipfs.io', { recursive: true }))
.to.match(/\/ipfs\/.+$/)
})

it('should resolve /ipns/ipfs.io with remainder', async () => {
return expect(await ipfs.name.resolve('/ipns/ipfs.io/images/ipfs-logo.svg'))
.to.match(/\/ipfs\/.+\/images\/ipfs-logo.svg$/)
})

it('should resolve /ipns/ipfs.io with remainder recursive === false', async () => {
return expect(await ipfs.name.resolve('/ipns/ipfs.io/images/ipfs-logo.svg', { recursive: false }))
.to.match(/\/ipns\/.+\/images\/ipfs-logo.svg$/)
})

it('should resolve /ipns/ipfs.io with remainder recursive === true', async () => {
return expect(await ipfs.name.resolve('/ipns/ipfs.io/images/ipfs-logo.svg', { recursive: true }))
.to.match(/\/ipfs\/.+\/images\/ipfs-logo.svg$/)
})

it('should fail to resolve /ipns/ipfs.a', async () => {
try {
await ipfs.name.resolve('ipfs.a')
} catch (error) {
expect(error).to.exist()
}
})

it('should resolve ipns path with hamt-shard recursive === true', async () => {
return expect(await ipfs.name.resolve('/ipns/tr.wikipedia-on-ipfs.org/wiki/Anasayfa.html', { recursive: true }))
.to.match(/\/ipfs\/.+$/)
})
})
}
8 changes: 4 additions & 4 deletions src/pubsub/subscribe.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module.exports = (createCommon, options) => {
expect(msg.data.toString()).to.equal('hi')
expect(msg).to.have.property('seqno')
expect(Buffer.isBuffer(msg.seqno)).to.eql(true)
expect(msg).to.have.property('topicIDs').eql([topic])
expect(msg.topicIDs[0]).to.eq(topic)
expect(msg).to.have.property('from', ipfs1.peerId.id)

ipfs1.pubsub.unsubscribe(topic, handler, (err) => {
Expand Down Expand Up @@ -79,7 +79,7 @@ module.exports = (createCommon, options) => {
expect(msg.data.toString()).to.equal('hi')
expect(msg).to.have.property('seqno')
expect(Buffer.isBuffer(msg.seqno)).to.eql(true)
expect(msg).to.have.property('topicIDs').eql([topic])
expect(msg.topicIDs[0]).to.eq(topic)
expect(msg).to.have.property('from', ipfs1.peerId.id)

ipfs1.pubsub.unsubscribe(topic, handler, (err) => {
Expand Down Expand Up @@ -107,7 +107,7 @@ module.exports = (createCommon, options) => {
expect(msg.data.toString()).to.equal('hi')
expect(msg).to.have.property('seqno')
expect(Buffer.isBuffer(msg.seqno)).to.eql(true)
expect(msg).to.have.property('topicIDs').eql([topic])
expect(msg.topicIDs[0]).to.eq(topic)
expect(msg).to.have.property('from', ipfs1.peerId.id)

ipfs1.pubsub.unsubscribe(topic, handler, (err) => {
Expand Down Expand Up @@ -135,7 +135,7 @@ module.exports = (createCommon, options) => {
expect(msg.data.toString()).to.equal('hi')
expect(msg).to.have.property('seqno')
expect(Buffer.isBuffer(msg.seqno)).to.eql(true)
expect(msg).to.have.property('topicIDs').eql([topic])
expect(msg.topicIDs[0]).to.eq(topic)
expect(msg).to.have.property('from', ipfs1.peerId.id)

ipfs1.pubsub.unsubscribe(topic, handler, (err) => {
Expand Down
Loading

0 comments on commit cd41a3c

Please sign in to comment.