Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit cd41a3c

Browse files
hugomrdiasAlan Shaw
authored and
Alan Shaw
committed
feat: add support to resolve dns to ipns (#458)
* 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
1 parent ca9a6e0 commit cd41a3c

File tree

5 files changed

+166
-105
lines changed

5 files changed

+166
-105
lines changed

src/miscellaneous/dns.js

+4-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ module.exports = (createCommon, options) => {
88
const it = getIt(options)
99
const common = createCommon()
1010

11-
describe('.dns', () => {
11+
describe('.dns', function () {
12+
this.timeout(10 * 1000)
13+
this.retries(3)
1214
let ipfs
1315

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

33-
it('should resolve a DNS link', function (done) {
34-
this.timeout(20 * 1000)
35-
this.retries(3)
36-
37-
ipfs.dns('ipfs.io', { r: true }, (err, path) => {
38-
expect(err).to.not.exist()
39-
expect(path).to.exist()
40-
done()
41-
})
42-
})
43-
44-
// skipping because there is an error in https://ipfs.io/api/v0/dns?arg=ipfs.io
45-
// unskip when it is resolved and the new version released: https://github.com/ipfs/go-ipfs/issues/6086
46-
it.skip('should non-recursively resolve ipfs.io', () => {
35+
it('should non-recursively resolve ipfs.io', () => {
4736
return ipfs.dns('ipfs.io', { recursive: false }).then(res => {
4837
// matches pattern /ipns/<ipnsaddress>
4938
expect(res).to.match(/\/ipns\/.+$/)

src/name/publish.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = (createCommon, options) => {
1212
const it = getIt(options)
1313
const common = createCommon()
1414

15-
describe('.name.publish', function () {
15+
describe('.name.publish offline', function () {
1616
const keyName = hat()
1717
let ipfs
1818
let nodeId
@@ -43,7 +43,7 @@ module.exports = (createCommon, options) => {
4343

4444
const value = fixture.cid
4545

46-
ipfs.name.publish(value, (err, res) => {
46+
ipfs.name.publish(value, { 'allow-offline': true }, (err, res) => {
4747
expect(err).to.not.exist()
4848
expect(res).to.exist()
4949
expect(res.name).to.equal(nodeId)
@@ -53,6 +53,13 @@ module.exports = (createCommon, options) => {
5353
})
5454
})
5555

56+
it('should publish correctly with the lifetime option and resolve', async () => {
57+
const [{ path }] = await ipfs.add(Buffer.from('should publish correctly with the lifetime option and resolve'))
58+
await ipfs.name.publish(path, { 'allow-offline': true, resolve: false, lifetime: '2h' })
59+
60+
return expect(await ipfs.name.resolve(`/ipns/${nodeId}`)).to.eq(`/ipfs/${path}`)
61+
})
62+
5663
it('should publish correctly when the file was not added but resolve is disabled', function (done) {
5764
this.timeout(50 * 1000)
5865

@@ -62,7 +69,8 @@ module.exports = (createCommon, options) => {
6269
resolve: false,
6370
lifetime: '1m',
6471
ttl: '10s',
65-
key: 'self'
72+
key: 'self',
73+
'allow-offline': true
6674
}
6775

6876
ipfs.name.publish(value, options, (err, res) => {
@@ -83,7 +91,8 @@ module.exports = (createCommon, options) => {
8391
resolve: false,
8492
lifetime: '24h',
8593
ttl: '10s',
86-
key: keyName
94+
key: keyName,
95+
'allow-offline': true
8796
}
8897

8998
ipfs.key.gen(keyName, { type: 'rsa', size: 2048 }, function (err, key) {

src/name/resolve.js

+125-82
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,20 @@
22
/* eslint-env mocha */
33
'use strict'
44

5-
const hat = require('hat')
6-
7-
const { fixture } = require('./utils')
85
const { spawnNodeWithId } = require('../utils/spawn')
96
const { getDescribe, getIt, expect } = require('../utils/mocha')
7+
const delay = require('../utils/delay')
108

119
module.exports = (createCommon, options) => {
1210
const describe = getDescribe(options)
1311
const it = getIt(options)
14-
const common = createCommon()
1512

16-
describe('.name.resolve', function () {
17-
const keyName = hat()
13+
describe('.name.resolve offline', function () {
14+
const common = createCommon()
1815
let ipfs
1916
let nodeId
20-
let keyId
2117

2218
before(function (done) {
23-
// CI takes longer to instantiate the daemon, so we need to increase the
24-
// timeout for the before step
25-
this.timeout(60 * 1000)
26-
2719
common.setup((err, factory) => {
2820
expect(err).to.not.exist()
2921

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

3325
ipfs = node
3426
nodeId = node.peerId.id
35-
36-
ipfs.add(fixture.data, { pin: false }, done)
27+
done()
3728
})
3829
})
3930
})
4031

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

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

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

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

52-
ipfs.name.resolve(nodeId, (err, res) => {
53-
expect(err).to.not.exist()
54-
expect(res).to.exist()
55-
expect(res.path).to.equal(`/ipfs/${value}`)
42+
return expect(await ipfs.name.resolve(`/ipns/${keyId}`))
43+
.to.eq(`/ipfs/${path}`)
44+
})
5645

57-
done()
58-
})
59-
})
46+
it('should resolve a record recursive === false', async () => {
47+
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive === false'))
48+
await ipfs.name.publish(path, { 'allow-offline': true })
49+
return expect(await ipfs.name.resolve(`/ipns/${nodeId}`, { recursive: false }))
50+
.to.eq(`/ipfs/${path}`)
6051
})
6152

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

65-
const value = fixture.cid
66-
const publishOptions = {
67-
resolve: true,
68-
lifetime: '1ms',
69-
ttl: '10s',
70-
key: 'self'
71-
}
56+
const { id: keyId } = await ipfs.key.gen('key-name', { type: 'rsa', size: 2048 })
7257

73-
ipfs.name.publish(value, publishOptions, (err, res) => {
74-
expect(err).to.not.exist()
75-
expect(res).to.exist()
76-
77-
// guarantee that the record has an expired validity.
78-
setTimeout(function () {
79-
ipfs.name.resolve(nodeId, (err, res) => {
80-
expect(err).to.exist()
81-
expect(err.message).to.equal('record has expired')
82-
expect(res).to.not.exist()
83-
84-
done()
85-
})
86-
}, 1)
87-
})
58+
await ipfs.name.publish(path, { 'allow-offline': true })
59+
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name' })
60+
61+
return expect(await ipfs.name.resolve(`/ipns/${keyId}`, { recursive: true }))
62+
.to.eq(`/ipfs/${path}`)
63+
})
64+
65+
it('should resolve a record default options with remainder', async () => {
66+
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record default options with remainder'))
67+
68+
const { id: keyId } = await ipfs.key.gen('key-name-remainder-default', { type: 'rsa', size: 2048 })
69+
70+
await ipfs.name.publish(path, { 'allow-offline': true })
71+
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name-remainder-default' })
72+
73+
return expect(await ipfs.name.resolve(`/ipns/${keyId}/remainder/file.txt`))
74+
.to.eq(`/ipfs/${path}/remainder/file.txt`)
75+
})
76+
77+
it('should resolve a record recursive === false with remainder', async () => {
78+
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive = false with remainder'))
79+
await ipfs.name.publish(path, { 'allow-offline': true })
80+
return expect(await ipfs.name.resolve(`/ipns/${nodeId}/remainder/file.txt`, { recursive: false }))
81+
.to.eq(`/ipfs/${path}/remainder/file.txt`)
8882
})
8983

90-
it('should recursively resolve to an IPFS hash', function (done) {
91-
this.timeout(100 * 1000)
84+
it('should resolve a record recursive === true with remainder', async () => {
85+
const [{ path }] = await ipfs.add(Buffer.from('should resolve a record recursive = true with remainder'))
86+
87+
const { id: keyId } = await ipfs.key.gen('key-name-remainder', { type: 'rsa', size: 2048 })
9288

93-
const value = fixture.cid
89+
await ipfs.name.publish(path, { 'allow-offline': true })
90+
await ipfs.name.publish(`/ipns/${nodeId}`, { 'allow-offline': true, key: 'key-name-remainder' })
91+
92+
return expect(await ipfs.name.resolve(`/ipns/${keyId}/remainder/file.txt`, { recursive: true }))
93+
.to.eq(`/ipfs/${path}/remainder/file.txt`)
94+
})
95+
96+
it('should not get the entry if its validity time expired', async () => {
9497
const publishOptions = {
95-
resolve: false,
96-
lifetime: '24h',
98+
lifetime: '100ms',
9799
ttl: '10s',
98-
key: 'self'
100+
'allow-offline': true
99101
}
100102

101-
// Generate new key
102-
ipfs.key.gen(keyName, { type: 'rsa', size: 2048 }, (err, key) => {
103-
expect(err).to.not.exist()
103+
// we add new data instead of re-using fixture to make sure lifetime handling doesn't break
104+
const [{ path }] = await ipfs.add(Buffer.from('should not get the entry if its validity time expired'))
105+
await ipfs.name.publish(path, publishOptions)
106+
await delay(500)
107+
// go only has 1 possible error https://github.com/ipfs/go-ipfs/blob/master/namesys/interface.go#L51
108+
// so here we just expect an Error and don't match the error type to expiration
109+
try {
110+
await ipfs.name.resolve(nodeId)
111+
} catch (error) {
112+
expect(error).to.exist()
113+
}
114+
})
115+
})
116+
117+
describe('.name.resolve dns', function () {
118+
const common = createCommon()
119+
let ipfs
120+
this.retries(3)
104121

105-
keyId = key.id
122+
before(function (done) {
123+
common.setup((err, factory) => {
124+
expect(err).to.not.exist()
106125

107-
// publish ipfs
108-
ipfs.name.publish(value, publishOptions, (err, res) => {
126+
spawnNodeWithId(factory, (err, node) => {
109127
expect(err).to.not.exist()
110-
expect(res).to.exist()
111128

112-
publishOptions.key = keyName
129+
ipfs = node
130+
done()
131+
})
132+
})
133+
})
113134

114-
// publish ipns with the generated key
115-
ipfs.name.publish(`/ipns/${nodeId}`, publishOptions, (err, res) => {
116-
expect(err).to.not.exist()
117-
expect(res).to.exist()
135+
after((done) => common.teardown(done))
118136

119-
const resolveOptions = {
120-
nocache: false,
121-
recursive: true
122-
}
137+
it('should resolve /ipns/ipfs.io', async () => {
138+
return expect(await ipfs.name.resolve('/ipns/ipfs.io'))
139+
.to.match(/\/ipfs\/.+$/)
140+
})
123141

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

130-
done()
131-
})
132-
})
133-
})
134-
})
147+
it('should resolve /ipns/ipfs.io recursive === true', async () => {
148+
return expect(await ipfs.name.resolve('/ipns/ipfs.io', { recursive: true }))
149+
.to.match(/\/ipfs\/.+$/)
150+
})
151+
152+
it('should resolve /ipns/ipfs.io with remainder', async () => {
153+
return expect(await ipfs.name.resolve('/ipns/ipfs.io/images/ipfs-logo.svg'))
154+
.to.match(/\/ipfs\/.+\/images\/ipfs-logo.svg$/)
155+
})
156+
157+
it('should resolve /ipns/ipfs.io with remainder recursive === false', async () => {
158+
return expect(await ipfs.name.resolve('/ipns/ipfs.io/images/ipfs-logo.svg', { recursive: false }))
159+
.to.match(/\/ipns\/.+\/images\/ipfs-logo.svg$/)
160+
})
161+
162+
it('should resolve /ipns/ipfs.io with remainder recursive === true', async () => {
163+
return expect(await ipfs.name.resolve('/ipns/ipfs.io/images/ipfs-logo.svg', { recursive: true }))
164+
.to.match(/\/ipfs\/.+\/images\/ipfs-logo.svg$/)
165+
})
166+
167+
it('should fail to resolve /ipns/ipfs.a', async () => {
168+
try {
169+
await ipfs.name.resolve('ipfs.a')
170+
} catch (error) {
171+
expect(error).to.exist()
172+
}
173+
})
174+
175+
it('should resolve ipns path with hamt-shard recursive === true', async () => {
176+
return expect(await ipfs.name.resolve('/ipns/tr.wikipedia-on-ipfs.org/wiki/Anasayfa.html', { recursive: true }))
177+
.to.match(/\/ipfs\/.+$/)
135178
})
136179
})
137180
}

src/pubsub/subscribe.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ module.exports = (createCommon, options) => {
5151
expect(msg.data.toString()).to.equal('hi')
5252
expect(msg).to.have.property('seqno')
5353
expect(Buffer.isBuffer(msg.seqno)).to.eql(true)
54-
expect(msg).to.have.property('topicIDs').eql([topic])
54+
expect(msg.topicIDs[0]).to.eq(topic)
5555
expect(msg).to.have.property('from', ipfs1.peerId.id)
5656

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

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

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

141141
ipfs1.pubsub.unsubscribe(topic, handler, (err) => {

0 commit comments

Comments
 (0)