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

Commit f3071e7

Browse files
committed
feat: add support for ipns and recursive to ipfs resolve
This PR add IPNS support to resolve, makes the recursive option true by default and reworks the tests. Jsdocs were add to the resolve methods. Two interface-core config profile tests needed to be skipped because js-ipfs doesn't support them yet needs: ipfs-inactive/interface-js-ipfs-core#504
1 parent 63eb5db commit f3071e7

File tree

7 files changed

+82
-162
lines changed

7 files changed

+82
-162
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@
7474
"bs58": "^4.0.1",
7575
"buffer-peek-stream": "^1.0.1",
7676
"byteman": "^1.3.5",
77-
"callbackify": "^1.1.0",
7877
"cid-tool": "~0.3.0",
7978
"cids": "~0.7.1",
8079
"class-is": "^1.1.0",
@@ -151,6 +150,7 @@
151150
"peer-id": "~0.12.3",
152151
"peer-info": "~0.15.0",
153152
"progress": "^2.0.1",
153+
"promise-nodeify": "^3.0.1",
154154
"promisify-es6": "^1.0.3",
155155
"protons": "^1.0.1",
156156
"pull-abortable": "^4.1.1",
@@ -190,7 +190,7 @@
190190
"execa": "^2.0.3",
191191
"form-data": "^2.5.0",
192192
"hat": "0.0.3",
193-
"interface-ipfs-core": "~0.107.3",
193+
"interface-ipfs-core": "ipfs/interface-js-ipfs-core#feat/resolve-ipns-recursive",
194194
"ipfsd-ctl": "^0.43.0",
195195
"libp2p-websocket-star": "~0.10.2",
196196
"ncp": "^2.0.0",

src/cli/commands/resolve.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ module.exports = {
2020
}
2121
},
2222

23-
handler ({ getIpfs, print, name, recursive, cidBase, resolve }) {
23+
handler ({ getIpfs, name, recursive, cidBase, resolve }) {
2424
resolve((async () => {
2525
const ipfs = await getIpfs()
2626
const res = await ipfs.resolve(name, { recursive, cidBase })
27-
print(res)
27+
return res
2828
})())
2929
}
3030
}

src/core/components/resolve.js

+58-40
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,82 @@
11
'use strict'
22

3-
const promisify = require('promisify-es6')
43
const isIpfs = require('is-ipfs')
5-
const setImmediate = require('async/setImmediate')
64
const CID = require('cids')
5+
const nodeify = require('promise-nodeify')
76
const { cidToString } = require('../../utils/cid')
87

9-
module.exports = (self) => {
10-
return promisify(async (name, opts, cb) => {
11-
if (typeof opts === 'function') {
12-
cb = opts
13-
opts = {}
14-
}
8+
/**
9+
* @typedef { import("../index") } IPFS
10+
*/
1511

16-
opts = opts || {}
12+
/**
13+
* @typedef {Object} ResolveOptions
14+
* @prop {string} cidBase - Multibase codec name the CID in the resolved path will be encoded with
15+
* @prop {boolean} [recursive=true] - Resolve until the result is an IPFS name
16+
*
17+
*/
1718

19+
/** @typedef {(err: Error, path: string) => void} ResolveCallback */
20+
21+
/**
22+
* @callback ResolveWrapper - This wrapper adds support for callbacks and promises
23+
* @param {string} name - Path to resolve
24+
* @param {ResolveOptions} opts - Options for resolve
25+
* @param {ResolveCallback} [cb] - Optional callback function
26+
* @returns {Promise<string> | void} - When callback is provided nothing is returned
27+
*/
28+
29+
/**
30+
* IPFS Resolve factory
31+
*
32+
* @param {IPFS} ipfs
33+
* @returns {ResolveWrapper}
34+
*/
35+
module.exports = (ipfs) => {
36+
/**
37+
* IPFS Resolve - Resolve the value of names to IPFS
38+
*
39+
* @param {String} name
40+
* @param {ResolveOptions} [opts={}]
41+
* @returns {Promise<string>}
42+
*/
43+
const resolve = async (name, opts = {}) => {
1844
if (!isIpfs.path(name)) {
19-
return setImmediate(() => cb(new Error('invalid argument ' + name)))
45+
throw new Error('invalid argument ' + name)
2046
}
2147

22-
// TODO remove this and update subsequent code when IPNS is implemented
23-
if (!isIpfs.ipfsPath(name)) {
24-
return setImmediate(() => cb(new Error('resolve non-IPFS names is not implemented')))
48+
if (isIpfs.ipnsPath(name)) {
49+
name = await ipfs.name.resolve(name, opts)
2550
}
2651

27-
const split = name.split('/') // ['', 'ipfs', 'hash', ...path]
28-
const cid = new CID(split[2])
52+
const [, , hash, ...rest] = name.split('/') // ['', 'ipfs', 'hash', ...path]
53+
const cid = new CID(hash)
2954

30-
if (split.length === 3) {
31-
return setImmediate(() => cb(null, `/ipfs/${cidToString(cid, { base: opts.cidBase })}`))
55+
// nothing to resolve return the input
56+
if (rest.length === 0) {
57+
return `/ipfs/${cidToString(cid, { base: opts.cidBase })}`
3258
}
3359

34-
const path = split.slice(3).join('/')
35-
36-
const results = self._ipld.resolve(cid, path)
60+
const path = rest.join('/')
61+
const results = ipfs._ipld.resolve(cid, path)
3762
let value = cid
3863
let remainderPath = path
39-
try {
40-
for await (const result of results) {
41-
if (result.remainderPath === '') {
42-
// Use values from previous iteration if the value isn't a CID
43-
if (CID.isCID(result.value)) {
44-
value = result.value
45-
remainderPath = ''
46-
}
47-
48-
if (result.value && CID.isCID(result.value.Hash)) {
49-
value = result.value.Hash
50-
remainderPath = ''
51-
}
52-
53-
break
54-
}
5564

65+
for await (const result of results) {
66+
if (CID.isCID(result.value)) {
5667
value = result.value
5768
remainderPath = result.remainderPath
5869
}
59-
} catch (error) {
60-
return cb(error)
6170
}
62-
return cb(null, `/ipfs/${cidToString(value, { base: opts.cidBase })}${remainderPath ? '/' + remainderPath : ''}`)
63-
})
71+
72+
return `/ipfs/${cidToString(value, { base: opts.cidBase })}${remainderPath ? '/' + remainderPath : ''}`
73+
}
74+
75+
return (name, opts = {}, cb) => {
76+
if (typeof opts === 'function') {
77+
cb = opts
78+
opts = {}
79+
}
80+
return nodeify(resolve(name, opts), cb)
81+
}
6482
}

src/http/api/resources/resolve.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = {
2424
async handler (request, h) {
2525
const { ipfs } = request.server.app
2626
const name = request.query.arg
27-
const recursive = request.query.r || request.query.recursive || false
27+
const recursive = request.query.r || request.query.recursive || true
2828
const cidBase = request.query['cid-base']
2929

3030
log(name, { recursive, cidBase })

test/cli/resolve.js

-74
This file was deleted.

test/core/interface.spec.js

+19-17
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,20 @@ describe('interface-ipfs-core tests', function () {
1717
tests.bootstrap(defaultCommonFactory)
1818

1919
tests.config(defaultCommonFactory, {
20-
skip: [{
21-
name: 'should set a number',
22-
reason: 'Failing - needs to be fixed'
23-
}]
20+
skip: [
21+
{
22+
name: 'should set a number',
23+
reason: 'Failing - needs to be fixed'
24+
},
25+
{
26+
name: 'should output changes but not save them for dry run',
27+
reason: 'not implemented'
28+
},
29+
{
30+
name: 'should set a config profile',
31+
reason: 'not implemented'
32+
}
33+
]
2434
})
2535

2636
tests.dag(defaultCommonFactory)
@@ -81,19 +91,11 @@ describe('interface-ipfs-core tests', function () {
8191

8292
tests.miscellaneous(CommonFactory.create({
8393
// No need to stop, because the test suite does a 'stop' test.
84-
createTeardown: () => cb => cb()
85-
}), {
86-
skip: [
87-
{
88-
name: 'should resolve an IPNS DNS link',
89-
reason: 'TODO: IPNS resolve not yet implemented https://github.com/ipfs/js-ipfs/issues/1918'
90-
},
91-
{
92-
name: 'should resolve IPNS link recursively',
93-
reason: 'TODO: IPNS resolve not yet implemented https://github.com/ipfs/js-ipfs/issues/1918'
94-
}
95-
]
96-
})
94+
createTeardown: () => cb => cb(),
95+
spawnOptions: {
96+
args: ['--pass ipfs-is-awesome-software', '--offline']
97+
}
98+
}))
9799

98100
tests.name(CommonFactory.create({
99101
spawnOptions: {

test/http-api/inject/resolve.js

-26
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
/* eslint-env mocha */
2-
/* eslint max-nested-callbacks: ["error", 8] */
32
'use strict'
43

54
const expect = require('chai').expect
65
const FormData = require('form-data')
76
const streamToPromise = require('stream-to-promise')
8-
const multibase = require('multibase')
97

108
module.exports = (http) => {
119
describe('resolve', () => {
@@ -15,30 +13,6 @@ module.exports = (http) => {
1513
api = http.api._httpApi._apiServers[0]
1614
})
1715

18-
it('should resolve a path and return a base2 encoded CID', async () => {
19-
const form = new FormData()
20-
form.append('data', Buffer.from('TEST' + Date.now()))
21-
const headers = form.getHeaders()
22-
23-
const payload = await streamToPromise(form)
24-
let res = await api.inject({
25-
method: 'POST',
26-
url: '/api/v0/add',
27-
headers: headers,
28-
payload: payload
29-
})
30-
expect(res.statusCode).to.equal(200)
31-
const hash = JSON.parse(res.result).Hash
32-
33-
res = await api.inject({
34-
method: 'POST',
35-
url: `/api/v0/resolve?arg=/ipfs/${hash}&cid-base=base2`
36-
})
37-
38-
expect(res.statusCode).to.equal(200)
39-
expect(multibase.isEncoded(res.result.Path.replace('/ipfs/', ''))).to.deep.equal('base2')
40-
})
41-
4216
it('should not resolve a path for invalid cid-base option', async () => {
4317
const form = new FormData()
4418
form.append('data', Buffer.from('TEST' + Date.now()))

0 commit comments

Comments
 (0)