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 for ipns and recursive to ipfs resolve
Browse files Browse the repository at this point in the history
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
  • Loading branch information
hugomrdias committed Sep 3, 2019
1 parent 4661f83 commit 8b55e6b
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 144 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
"bs58": "^4.0.1",
"buffer-peek-stream": "^1.0.1",
"byteman": "^1.3.5",
"callbackify": "^1.1.0",
"cid-tool": "~0.3.0",
"cids": "~0.7.1",
"class-is": "^1.1.0",
Expand Down Expand Up @@ -155,6 +154,7 @@
"peer-id": "~0.12.3",
"peer-info": "~0.15.0",
"progress": "^2.0.1",
"promise-nodeify": "^3.0.1",
"promisify-es6": "^1.0.3",
"protons": "^1.0.1",
"pull-abortable": "^4.1.1",
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ module.exports = {
}
},

handler ({ getIpfs, print, name, recursive, cidBase, resolve }) {
handler ({ getIpfs, name, recursive, cidBase, resolve }) {
resolve((async () => {
const ipfs = await getIpfs()
const res = await ipfs.resolve(name, { recursive, cidBase })
print(res)
return res
})())
}
}
98 changes: 58 additions & 40 deletions src/core/components/resolve.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,82 @@
'use strict'

const promisify = require('promisify-es6')
const isIpfs = require('is-ipfs')
const setImmediate = require('async/setImmediate')
const CID = require('cids')
const nodeify = require('promise-nodeify')
const { cidToString } = require('../../utils/cid')

module.exports = (self) => {
return promisify(async (name, opts, cb) => {
if (typeof opts === 'function') {
cb = opts
opts = {}
}
/**
* @typedef { import("../index") } IPFS
*/

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

/** @typedef {(err: Error, path: string) => void} ResolveCallback */

/**
* @callback ResolveWrapper - This wrapper adds support for callbacks and promises
* @param {string} name - Path to resolve
* @param {ResolveOptions} opts - Options for resolve
* @param {ResolveCallback} [cb] - Optional callback function
* @returns {Promise<string> | void} - When callback is provided nothing is returned
*/

/**
* IPFS Resolve factory
*
* @param {IPFS} ipfs
* @returns {ResolveWrapper}
*/
module.exports = (ipfs) => {
/**
* IPFS Resolve - Resolve the value of names to IPFS
*
* @param {String} name
* @param {ResolveOptions} [opts={}]
* @returns {Promise<string>}
*/
const resolve = async (name, opts = {}) => {
if (!isIpfs.path(name)) {
return setImmediate(() => cb(new Error('invalid argument ' + name)))
throw new Error('invalid argument ' + name)
}

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

const split = name.split('/') // ['', 'ipfs', 'hash', ...path]
const cid = new CID(split[2])
const [, , hash, ...rest] = name.split('/') // ['', 'ipfs', 'hash', ...path]
const cid = new CID(hash)

if (split.length === 3) {
return setImmediate(() => cb(null, `/ipfs/${cidToString(cid, { base: opts.cidBase })}`))
// nothing to resolve return the input
if (rest.length === 0) {
return `/ipfs/${cidToString(cid, { base: opts.cidBase })}`
}

const path = split.slice(3).join('/')

const results = self._ipld.resolve(cid, path)
const path = rest.join('/')
const results = ipfs._ipld.resolve(cid, path)
let value = cid
let remainderPath = path
try {
for await (const result of results) {
if (result.remainderPath === '') {
// Use values from previous iteration if the value isn't a CID
if (CID.isCID(result.value)) {
value = result.value
remainderPath = ''
}

if (result.value && CID.isCID(result.value.Hash)) {
value = result.value.Hash
remainderPath = ''
}

break
}

for await (const result of results) {
if (CID.isCID(result.value)) {
value = result.value
remainderPath = result.remainderPath
}
} catch (error) {
return cb(error)
}
return cb(null, `/ipfs/${cidToString(value, { base: opts.cidBase })}${remainderPath ? '/' + remainderPath : ''}`)
})

return `/ipfs/${cidToString(value, { base: opts.cidBase })}${remainderPath ? '/' + remainderPath : ''}`
}

return (name, opts = {}, cb) => {
if (typeof opts === 'function') {
cb = opts
opts = {}
}
return nodeify(resolve(name, opts), cb)
}
}
2 changes: 1 addition & 1 deletion src/http/api/resources/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = {
async handler (request, h) {
const { ipfs } = request.server.app
const name = request.query.arg
const recursive = request.query.r || request.query.recursive || false
const recursive = request.query.r || request.query.recursive || true
const cidBase = request.query['cid-base']

log(name, { recursive, cidBase })
Expand Down
74 changes: 0 additions & 74 deletions test/cli/resolve.js

This file was deleted.

26 changes: 0 additions & 26 deletions test/http-api/inject/resolve.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/* eslint-env mocha */
/* eslint max-nested-callbacks: ["error", 8] */
'use strict'

const expect = require('chai').expect
const FormData = require('form-data')
const streamToPromise = require('stream-to-promise')
const multibase = require('multibase')

module.exports = (http) => {
describe('resolve', () => {
Expand All @@ -15,30 +13,6 @@ module.exports = (http) => {
api = http.api._httpApi._apiServers[0]
})

it('should resolve a path and return a base2 encoded CID', async () => {
const form = new FormData()
form.append('data', Buffer.from('TEST' + Date.now()))
const headers = form.getHeaders()

const payload = await streamToPromise(form)
let res = await api.inject({
method: 'POST',
url: '/api/v0/add',
headers: headers,
payload: payload
})
expect(res.statusCode).to.equal(200)
const hash = JSON.parse(res.result).Hash

res = await api.inject({
method: 'POST',
url: `/api/v0/resolve?arg=/ipfs/${hash}&cid-base=base2`
})

expect(res.statusCode).to.equal(200)
expect(multibase.isEncoded(res.result.Path.replace('/ipfs/', ''))).to.deep.equal('base2')
})

it('should not resolve a path for invalid cid-base option', async () => {
const form = new FormData()
form.append('data', Buffer.from('TEST' + Date.now()))
Expand Down

0 comments on commit 8b55e6b

Please sign in to comment.