Skip to content
This repository was archived by the owner on Sep 28, 2021. It is now read-only.

Add support for CIDv1 and Base32 #9

Merged
merged 6 commits into from
Sep 28, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -18,7 +18,10 @@
## Usage

This project consists on creating a HTTP response from an IPFS Hash. This response can be a file, a directory list view or the entry point of a web page.

### Creating HTTP Response

This project creates a HTTP response for an IPFS Path. This response can be a file, a HTML with directory listing or the entry point of a web page.

```js
const { getResponse } = require('ipfs-http-response')
@@ -29,24 +32,31 @@ getResponse(ipfsNode, ipfsPath)
})
```

This module also exports the used ipfs resolver, which should be used when the response needs to be customized.
### Using protocol-agnostic resolver

This module also exports the used ipfs `resolver`, which should be used when the response needs to be customized or non-HTTP transport is used:

```js
const { resolver } = require('ipfs-http-response')

resolver.multihash(ipfsNode, ipfsPath)
resolver.cid(ipfsNode, ipfsPath)
.then((result) => {
...
})
```

If `ipfsPath` points at a directory, `resolver.cid` will throw Error `This dag node is a directory` with a `cid` attribute that can be passed to `resolver.directory`:


```js
const { resolver } = require('ipfs-http-response')

resolver.directory(node, path, multihash)
resolver.directory(ipfsNode, ipfsPath, cid)
.then((result) => {
...
})
```

`result` will be either a `string` with HTML directory listing or an array with CIDs of `index` pages present in inspected directory.

![ipfs-http-response usage](docs/ipfs-http-response.png "ipfs-http-response usage")
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@
"lint": "aegir lint",
"release": "aegir release --target node",
"build": "aegir build",
"test": "aegir test -t node"
"test": "aegir test -t node",
"test:node": "aegir test -t node"
},
"pre-push": [
"lint",
@@ -31,23 +32,23 @@
"homepage": "https://github.com/ipfs/js-ipfs-http-response#readme",
"dependencies": {
"async": "^2.6.0",
"cids": "^0.5.3",
"cids": "~0.5.5",
"debug": "^3.1.0",
"file-type": "^8.0.0",
"filesize": "^3.6.1",
"get-stream": "^3.0.0",
"ipfs-unixfs": "^0.1.14",
"ipfs-unixfs": "~0.1.14",
"mime-types": "^2.1.18",
"multihashes": "^0.4.13",
"multihashes": "~0.4.13",
"promisify-es6": "^1.0.3",
"stream-to-blob": "^1.0.1"
},
"devDependencies": {
"aegir": "^13.1.0",
"chai": "^4.1.2",
"dirty-chai": "^2.0.1",
"ipfs": "^0.28.2",
"ipfsd-ctl": "^0.36.0"
"ipfs": "~0.32.2",
"ipfsd-ctl": "~0.39.1"
},
"contributors": [
"André Cruz <andremiguelcruz@msn.com>",
18 changes: 7 additions & 11 deletions src/dir-view/index.js
Original file line number Diff line number Diff line change
@@ -5,21 +5,20 @@ const filesize = require('filesize')
const mainStyle = require('./style')
const pathUtil = require('../utils/path')

function getParentDirectoryURL (originalParts) {
const parts = originalParts.slice()

function getParentHref (path) {
const parts = pathUtil.cidArray(path).slice()
if (parts.length > 1) {
parts.pop()
// drop the last segment in a safe way that works for both paths and urls
return path.replace(`/${parts.pop()}`, '')
}

return [ '', 'ipfs' ].concat(parts).join('/')
return path
}

function buildFilesList (path, links) {
const rows = links.map((link) => {
let row = [
`<div class="ipfs-icon ipfs-_blank">&nbsp;</div>`,
`<a href="${pathUtil.joinURLParts(path, link.name)}">${link.name}</a>`,
`<a href="${path}${path.endsWith('/') ? '' : '/'}${link.name}">${link.name}</a>`,
filesize(link.size)
]

@@ -32,9 +31,6 @@ function buildFilesList (path, links) {
}

function buildTable (path, links) {
const parts = pathUtil.splitPath(path)
const parentDirectoryURL = getParentDirectoryURL(parts)

return `
<table class="table table-striped">
<tbody>
@@ -43,7 +39,7 @@ function buildTable (path, links) {
<div class="ipfs-icon ipfs-_blank">&nbsp;</div>
</td>
<td class="padding">
<a href="${parentDirectoryURL}">..</a>
<a href="${getParentHref(path)}">..</a>
</td>
<td></td>
</tr>
7 changes: 4 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ const resolver = require('./resolver')
const pathUtils = require('./utils/path')
const detectContentType = require('./utils/content-type')

// TODO: pass path and add Etag and X-Ipfs-Path + tests
const header = (status = 200, statusText = 'OK', headers = {}) => ({
status,
statusText,
@@ -25,7 +26,7 @@ const response = (ipfsNode, ipfsPath) => {
// switch case with true feels so wrong.
switch (true) {
case (errorString === 'Error: This dag node is a directory'):
resolver.directory(node, path, error.fileName)
resolver.directory(node, path, error.cid)
.then((content) => {
// dir render
if (typeof content === 'string') {
@@ -59,9 +60,9 @@ const response = (ipfsNode, ipfsPath) => {
resolve(Response.redirect(pathUtils.removeTrailingSlash(ipfsPath)))
}

resolver.multihash(ipfsNode, ipfsPath)
resolver.cid(ipfsNode, ipfsPath)
.then((resolvedData) => {
const readableStream = ipfsNode.files.catReadableStream(resolvedData.multihash)
const readableStream = ipfsNode.files.catReadableStream(resolvedData.cid)
const responseStream = new stream.PassThrough({ highWaterMark: 1 })
readableStream.pipe(responseStream)

106 changes: 75 additions & 31 deletions src/resolver.js
Original file line number Diff line number Diff line change
@@ -18,14 +18,21 @@ function getIndexFiles (links) {
'index.htm',
'index.shtml'
]

return links.filter((link) => INDEX_HTML_FILES.indexOf(link.name) !== -1)
// directory
let indexes = links.filter((link) => INDEX_HTML_FILES.indexOf(link.name) !== -1)
if (indexes.length) {
return indexes
}
// hamt-sharded-directory uses a 2 char prefix
return links.filter((link) => {
return link.name.length > 2 && INDEX_HTML_FILES.indexOf(link.name.substring(2)) !== -1
})
}

const directory = promisify((ipfs, path, multihash, callback) => {
mh.validate(mh.fromB58String(multihash))
const directory = promisify((ipfs, path, cid, callback) => {
cid = new CID(cid)

ipfs.object.get(multihash, { enc: 'base58' }, (err, dagNode) => {
ipfs.object.get(cid.buffer, (err, dagNode) => {
if (err) {
return callback(err)
}
@@ -41,17 +48,20 @@ const directory = promisify((ipfs, path, multihash, callback) => {
})
})

const multihash = promisify((ipfs, path, callback) => {
const parts = pathUtil.splitPath(path)
let firstMultihash = parts.shift()
const cid = promisify((ipfs, path, callback) => {
const parts = pathUtil.cidArray(path)
let firstCid = parts.shift()
let currentCid

// TODO: replace below with ipfs.resolve(path, {recursive: true})
// (requires changes to js-ipfs/js-ipfs-api)

reduce(
parts,
firstMultihash,
firstCid,
(memo, item, next) => {
try {
currentCid = new CID(mh.fromB58String(memo))
currentCid = new CID(memo)
} catch (err) {
return next(err)
}
@@ -65,56 +75,90 @@ const multihash = promisify((ipfs, path, callback) => {
}

const dagNode = result.value
// find multihash of requested named-file in current dagNode's links
let multihashOfNextFile
// find multihash/cid of requested named-file in current dagNode's links
let cidOfNextFile
const nextFileName = item

for (let link of dagNode.links) {
if (link.name === nextFileName) {
// found multihash of requested named-file
multihashOfNextFile = mh.toB58String(link.multihash)
log('found multihash: ', multihashOfNextFile)
break
try {
for (let link of dagNode.links) {
if (link.name === nextFileName) {
// found multihash/cid of requested named-file
try {
// assume a Buffer with a valid CID
// (cid is allowed instead of multihash since https://github.com/ipld/js-ipld-dag-pb/pull/80)
cidOfNextFile = new CID(link.multihash)
} catch (err) {
// fallback to multihash
cidOfNextFile = new CID(mh.toB58String(link.multihash))
}
break
}
}
} catch (err) {
return next(err)
}

if (!multihashOfNextFile) {
return next(new Error(`no link named "${nextFileName}" under ${memo}`))
if (!cidOfNextFile) {
const missingLinkErr = new Error(`no link named "${nextFileName}" under ${memo}`)
missingLinkErr.parentDagNode = memo
missingLinkErr.missingLinkName = nextFileName
return next(missingLinkErr)
}

next(null, multihashOfNextFile)
next(null, cidOfNextFile)
})
}, (err, result) => {
}, (err, cid) => {
if (err) {
return callback(err)
}

let cid
try {
cid = new CID(mh.fromB58String(result))
cid = new CID(cid)
} catch (err) {
return callback(err)
}

if (cid.codec === 'raw') {
// no need for additional lookup, its raw data
callback(null, { cid })
}

ipfs.dag.get(cid, (err, dagResult) => {
if (err) {
return callback(err)
}

let dagDataObj = Unixfs.unmarshal(dagResult.value.data)
if (dagDataObj.type === 'directory') {
let isDirErr = new Error('This dag node is a directory')
// add memo (last multihash) as a fileName so it can be used by directory
isDirErr.fileName = result
return callback(isDirErr)
try {
let dagDataObj = Unixfs.unmarshal(dagResult.value.data)
// There are at least two types of directories:
// - "directory"
// - "hamt-sharded-directory" (example: QmT5NvUtoM5nWFfrQdVrFtvGfKFmG7AHE8P34isapyhCxX)
if (dagDataObj.type === 'directory' || dagDataObj.type === 'hamt-sharded-directory') {
let isDirErr = new Error('This dag node is a directory')
// store memo of last multihash so it can be used by directory
isDirErr.cid = isDirErr.fileName = cid
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backward compatibility: js-ipfs uses fileName field, so we are keeping it here for now.
When ipfs-http-response is released with these changes, will PR js-ipfs to switch from fileName to cid

isDirErr.dagDirType = dagDataObj.type
return callback(isDirErr)
}
} catch (err) {
return callback(err)
}

callback(null, { multihash: result })
callback(null, { cid })
})
})
})

const multihash = promisify((ipfs, path, callback) => {
// deprecated, use 'cid' instead
// (left for backward-compatibility)
cid(ipfs, path)
.then((result) => { callback(null, { multihash: mh.toB58String(result.cid.multihash) }) })
.catch((err) => { callback(err) })
})

module.exports = {
directory: directory,
cid: cid,
multihash: multihash
}
17 changes: 13 additions & 4 deletions src/utils/path.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
'use strict'

/* eslint-disable no-unused-vars */
function splitPath (path) {

// Converts path or url to an array starting at CID
function cidArray (path) {
if (path[path.length - 1] === '/') {
path = path.substring(0, path.length - 1)
}

return path.substring(6).split('/')
// skip /ipxs/ prefix
if (path.match(/^\/ip[fn]s\//)) {
path = path.substring(6)
}
// skip ipxs:// protocol
if (path.match(/^ip[fn]s:\/\//)) {
path = path.substring(7)
}
return path.split('/')
}

function removeLeadingSlash (url) {
@@ -40,7 +49,7 @@ function joinURLParts (...urls) {
}

module.exports = {
splitPath,
cidArray,
removeLeadingSlash,
removeTrailingSlash,
removeSlashFromBothEnds,
203 changes: 191 additions & 12 deletions test/index.spec.js
Original file line number Diff line number Diff line change
@@ -10,13 +10,14 @@ const loadFixture = require('aegir/fixtures')
const ipfs = require('ipfs')
const DaemonFactory = require('ipfsd-ctl')
const getStream = require('get-stream')
const CID = require('cids')

const { getResponse } = require('../src')
const makeWebResponseEnv = require('./utils/web-response-env')

const df = DaemonFactory.create({ type: 'proc', exec: ipfs })

describe('resolve file', function () {
describe('resolve file (CIDv0)', function () {
let ipfs = null
let ipfsd = null

@@ -34,19 +35,20 @@ describe('resolve file', function () {
ipfsd = _ipfsd
ipfs = ipfsd.api

ipfs.files.add(file.data, (err, filesAdded) => {
ipfs.files.add(file.data, {cidVersion: 0}, (err, filesAdded) => {
expect(err).to.not.exist()
expect(filesAdded).to.have.length(1)

const retrievedFile = filesAdded[0]
expect(retrievedFile.hash).to.equal(file.cid)
expect(new CID(retrievedFile.hash)).to.deep.equal(new CID(file.cid))
expect(retrievedFile.size, 'ipfs.files.add result size should not be smaller than input buffer').greaterThan(file.data.length)

done()
})
})
})

it('should resolve a multihash', async () => {
it('should resolve a CIDv0', async () => {
const res = await getResponse(ipfs, `/ipfs/${file.cid}`)

expect(res).to.exist()
@@ -59,7 +61,50 @@ describe('resolve file', function () {
})
})

describe('resolve directory', function () {
describe('resolve file (CIDv1)', function () {
let ipfs = null
let ipfsd = null

const file = {
cid: 'zb2rhdTDKmCQD2a9x2TfLR61M3s7RmESzwV5mqgnakXQbm5gp',
data: loadFixture('test/fixtures/testfile.txt')
}

before(function (done) {
this.timeout(20 * 1000)
Object.assign(global, makeWebResponseEnv())

df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

ipfs.files.add(file.data, {cidVersion: 1}, (err, filesAdded) => {
expect(err).to.not.exist()
expect(filesAdded).to.have.length(1)
const retrievedFile = filesAdded[0]
expect(new CID(retrievedFile.hash)).to.deep.equal(new CID(file.cid))
// expect(retrievedFile.size, 'ipfs.files.add result size should not be smaller than input buffer').greaterThan(file.data.length)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we receive the file size with CIDv1?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope :) there is a bug and size is undefined – i reported it in ipfs/js-ipfs#1585
It does not impact what this lib does, data is added correctly by js-ipfs, so I commented it out for now.


done()
})
})
})

it('should resolve a CIDv1', async () => {
const res = await getResponse(ipfs, `/ipfs/${file.cid}`)

expect(res).to.exist()
expect(res.status).to.equal(200)

const contents = await getStream(res.body)
const expectedContents = loadFixture('test/fixtures/testfile.txt').toString()

expect(contents).to.equal(expectedContents)
})
})

describe('resolve directory (CIDv0)', function () {
let ipfs = null
let ipfsd = null

@@ -90,12 +135,16 @@ describe('resolve directory', function () {
content('holmes.txt')
]

ipfs.files.add(dirs, (err, res) => {
ipfs.files.add(dirs, {cidVersion: 0}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]

expect(root.path).to.equal('test-folder')
expect(root.hash).to.equal(directory.cid)
expect(new CID(root.hash)).to.deep.equal(new CID(directory.cid))

expect(res[0].size, 'ipfs.files.add 1st result size should not be smaller than 1st input buffer').greaterThan(dirs[0].content.length)
expect(res[1].size, 'ipfs.files.add 2nd result size should not be smaller than 2nd input buffer').greaterThan(dirs[1].content.length)

done()
})
})
@@ -116,9 +165,87 @@ describe('resolve directory', function () {

expect(contents).to.equal(expectedContents)
})

it('should return the holmes.txt file', async () => {
const res = await getResponse(ipfs, `/ipfs/${directory.cid}/holmes.txt`, directory.cid)

const contents = await getStream(res.body)
const expectedContents = loadFixture('test/fixtures/test-folder/holmes.txt').toString()

expect(contents).to.equal(expectedContents)
})
})

describe('resolve web page', function () {
describe('resolve directory (CIDv1)', function () {
let ipfs = null
let ipfsd = null

const directory = {
cid: 'zdj7WggpWuCD8yN57uSxoVJPZr371E75q8m4FmZoCvhBJzGvP',
files: {
'pp.txt': Buffer.from(loadFixture('test/fixtures/test-folder/pp.txt')),
'holmes.txt': loadFixture('test/fixtures/test-folder/holmes.txt')
}
}

before(function (done) {
this.timeout(20 * 1000)
Object.assign(global, makeWebResponseEnv())

df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

const content = (name) => ({
path: `test-folder/${name}`,
content: directory.files[name]
})

const dirs = [
content('pp.txt'),
content('holmes.txt')
]

ipfs.files.add(dirs, {cidVersion: 1}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]
expect(root.path).to.equal('test-folder')
// expect(res[0].size, 'ipfs.files.add 1st result size should not be smaller than 1st input buffer').greaterThan(dirs[0].content.length)
// expect(res[1].size, 'ipfs.files.add 2nd result size should not be smaller than 2nd input buffer').greaterThan(dirs[1].content.length)
expect(new CID(root.hash)).to.deep.equal(new CID(directory.cid))
done()
})
})
})

it('should return the list of files of a directory', async () => {
const res = await getResponse(ipfs, `/ipfs/${directory.cid}`, directory.cid)

expect(res.status).to.equal(200)
expect(res.body).to.match(/<html>/)
})

it('should return the pp.txt file', async () => {
const res = await getResponse(ipfs, `/ipfs/${directory.cid}/pp.txt`, directory.cid)

const contents = await getStream(res.body)
const expectedContents = loadFixture('test/fixtures/test-folder/pp.txt').toString()

expect(contents).to.equal(expectedContents)
})

it('should return the holmes.txt file', async () => {
const res = await getResponse(ipfs, `/ipfs/${directory.cid}/holmes.txt`, directory.cid)

const contents = await getStream(res.body)
const expectedContents = loadFixture('test/fixtures/test-folder/holmes.txt').toString()

expect(contents).to.equal(expectedContents)
})
})

describe('resolve web page (CIDv0)', function () {
let ipfs = null
let ipfsd = null

@@ -151,12 +278,63 @@ describe('resolve web page', function () {
content('index.html')
]

ipfs.files.add(dirs, (err, res) => {
ipfs.files.add(dirs, {cidVersion: 0}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]

expect(root.path).to.equal('test-site')
expect(root.hash).to.equal(webpage.cid)
expect(new CID(root.hash)).to.deep.equal(new CID(webpage.cid))
done()
})
})
})

it('should return the entry point of a web page when a trying to fetch a directory containing a web page', async () => {
const res = await getResponse(ipfs, `/ipfs/${webpage.cid}`, webpage.cid)

expect(res.status).to.equal(302)
expect(res.headers.get('Location')).to.equal(`/ipfs/${webpage.cid}/index.html`)
})
})

describe('resolve web page (CIDv1)', function () {
let ipfs = null
let ipfsd = null

const webpage = {
cid: 'zdj7WYcfiUZa2wBeD9G2Jg9jqHx3Wh8nRsBNdVSWwsZ7XE62V',
files: {
'pp.txt': loadFixture('test/fixtures/test-site/pp.txt'),
'holmes.txt': loadFixture('test/fixtures/test-site/holmes.txt'),
'index.html': loadFixture('test/fixtures/test-site/index.html')
}
}

before(function (done) {
this.timeout(20 * 1000)
Object.assign(global, makeWebResponseEnv())

df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

const content = (name) => ({
path: `test-site/${name}`,
content: webpage.files[name]
})

const dirs = [
content('pp.txt'),
content('holmes.txt'),
content('index.html')
]

ipfs.files.add(dirs, {cidVersion: 1}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]
expect(root.path).to.equal('test-site')
expect(new CID(root.hash)).to.deep.equal(new CID(webpage.cid))
done()
})
})
@@ -170,6 +348,7 @@ describe('resolve web page', function () {
})
})

// TODO: move mime-types to separate test file
describe('mime-types', () => {
let ipfs = null
let ipfsd = null
@@ -207,12 +386,12 @@ describe('mime-types', () => {
content('index.html')
]

ipfs.files.add(dirs, (err, res) => {
ipfs.files.add(dirs, {cidVersion: 0}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]

expect(root.path).to.equal('test-mime-types')
expect(root.hash).to.equal(webpage.cid)
expect(new CID(root.hash)).to.deep.equal(new CID(webpage.cid))
done()
})
})
212 changes: 199 additions & 13 deletions test/resolver.spec.js
Original file line number Diff line number Diff line change
@@ -9,12 +9,14 @@ chai.use(dirtyChai)
const loadFixture = require('aegir/fixtures')
const ipfs = require('ipfs')
const DaemonFactory = require('ipfsd-ctl')
const CID = require('cids')
const mh = require('multihashes')

const ipfsResolver = require('../src/resolver')

const df = DaemonFactory.create({ type: 'proc', exec: ipfs })

describe('resolve file', function () {
describe('resolve file (CIDv0)', function () {
let ipfs = null
let ipfsd = null

@@ -30,12 +32,12 @@ describe('resolve file', function () {
ipfsd = _ipfsd
ipfs = ipfsd.api

ipfs.files.add(file.data, (err, filesAdded) => {
ipfs.files.add(file.data, {cidVersion: 0}, (err, filesAdded) => {
expect(err).to.not.exist()
expect(filesAdded).to.have.length(1)

const retrievedFile = filesAdded[0]
expect(retrievedFile.hash).to.equal(file.cid)
expect(new CID(retrievedFile.hash)).to.deep.equal(new CID(file.cid))

done()
})
@@ -46,13 +48,73 @@ describe('resolve file', function () {
const res = await ipfsResolver.multihash(ipfs, `/ipfs/${file.cid}`)

expect(res).to.exist()
const expectedCid = new CID(file.cid)
expect(res).to.deep.include({
multihash: file.cid
multihash: mh.toB58String(expectedCid.multihash)
})
})

it('should resolve a cid', async () => {
const res = await ipfsResolver.cid(ipfs, `/ipfs/${file.cid}`)

expect(res).to.exist()
const expectedCid = new CID(file.cid)
expect(res).to.deep.include({
cid: expectedCid
})
})
})

describe('resolve file (CIDv1)', function () {
let ipfs = null
let ipfsd = null

const file = {
cid: 'zb2rhdTDKmCQD2a9x2TfLR61M3s7RmESzwV5mqgnakXQbm5gp',
data: loadFixture('test/fixtures/testfile.txt')
}

before(function (done) {
this.timeout(20 * 1000)
df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

ipfs.files.add(file.data, {cidVersion: 1}, (err, filesAdded) => {
expect(err).to.not.exist()
expect(filesAdded).to.have.length(1)
// console.log('ipfs.files.add result', filesAdded)
const retrievedFile = filesAdded[0]
expect(new CID(retrievedFile.hash)).to.deep.equal(new CID(file.cid))
// expect(retrievedFile.size, 'ipfs.files.add result size should not be smaller than input buffer').greaterThan(file.data.length)
done()
})
})
})

it('should resolve a multihash', async () => {
const res = await ipfsResolver.multihash(ipfs, `/ipfs/${file.cid}`)

expect(res).to.exist()
const expectedCid = new CID(file.cid)
expect(res).to.deep.include({
multihash: mh.toB58String(expectedCid.multihash)
})
})

it('should resolve a cid', async () => {
const res = await ipfsResolver.cid(ipfs, `/ipfs/${file.cid}`)

expect(res).to.exist()
const expectedCid = new CID(file.cid)
expect(res).to.deep.include({
cid: expectedCid
})
})
})

describe('resolve directory', function () {
describe('resolve directory (CIDv0)', function () {
let ipfs = null
let ipfsd = null

@@ -81,35 +143,97 @@ describe('resolve directory', function () {
content('holmes.txt')
]

ipfs.files.add(dirs, (err, res) => {
ipfs.files.add(dirs, {cidVersion: 0}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]

expect(root.path).to.equal('test-folder')
expect(root.hash).to.equal(directory.cid)
expect(new CID(root.hash)).to.deep.equal(new CID(directory.cid))
done()
})
})
})

it('should throw an error when trying to fetch a directory', async () => {
try {
const res = await ipfsResolver.multihash(ipfs, `/ipfs/${directory.cid}`)
const res = await ipfsResolver.cid(ipfs, `/ipfs/${directory.cid}`)

expect(res).to.not.exist()
} catch (err) {
expect(err.toString()).to.equal('Error: This dag node is a directory')
}
})

it('should return the list of files of a directory', async () => {
it('should return HTML listing of files of a directory', async () => {
const res = await ipfsResolver.directory(ipfs, `/ipfs/${directory.cid}`, directory.cid)

expect(res).to.exist()
expect(res).to.include('</html>')
})
})

describe('resolve directory (CIDv1)', function () {
let ipfs = null
let ipfsd = null

const directory = {
cid: 'zdj7WggpWuCD8yN57uSxoVJPZr371E75q8m4FmZoCvhBJzGvP',
files: {
'pp.txt': loadFixture('test/fixtures/test-folder/pp.txt'),
'holmes.txt': loadFixture('test/fixtures/test-folder/holmes.txt')
}
}

before(function (done) {
this.timeout(20 * 1000)
df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

const content = (name) => ({
path: `test-folder/${name}`,
content: directory.files[name]
})

const dirs = [
content('pp.txt'),
content('holmes.txt')
]

ipfs.files.add(dirs, {cidVersion: 1}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]
// console.log('ipfs.files.add result', res)
expect(root.path).to.equal('test-folder')
// expect(res[0].size, 'ipfs.files.add 1st result size should not be smaller than 1st input buffer').greaterThan(dirs[0].content.length)
// expect(res[1].size, 'ipfs.files.add 2nd result size should not be smaller than 2nd input buffer').greaterThan(dirs[1].content.length)
expect(new CID(root.hash)).to.deep.equal(new CID(directory.cid))
done()
})
})
})

it('should throw an error when trying to fetch a directory', async () => {
try {
const res = await ipfsResolver.cid(ipfs, `/ipfs/${directory.cid}`)

expect(res).to.not.exist()
} catch (err) {
expect(err.toString()).to.equal('Error: This dag node is a directory')
}
})

it('should return HTML listing of files of a directory', async () => {
const res = await ipfsResolver.directory(ipfs, `/ipfs/${directory.cid}`, directory.cid)
expect(res).to.exist()
expect(res).to.include('pp.txt')
expect(res).to.include('holmes.txt')
expect(res).to.include('</html>')
})
})

describe('resolve web page', function () {
describe('resolve web page (CIDv0)', function () {
let ipfs = null
let ipfsd = null

@@ -140,20 +264,82 @@ describe('resolve web page', function () {
content('index.html')
]

ipfs.files.add(dirs, (err, res) => {
ipfs.files.add(dirs, {cidVersion: 0}, (err, res) => {
expect(err).to.not.exist()
const root = res[res.length - 1]

expect(root.path).to.equal('test-site')
expect(root.hash).to.equal(webpage.cid)
expect(new CID(root.hash)).to.deep.equal(new CID(webpage.cid))
done()
})
})
})

it('should throw an error when trying to fetch a directory containing a web page', async () => {
try {
const res = await ipfsResolver.cid(ipfs, `/ipfs/${webpage.cid}`)

expect(res).to.not.exist()
} catch (err) {
expect(err.toString()).to.equal('Error: This dag node is a directory')
}
})

it('should return the entry point of a web page when a trying to fetch a directory containing a web page', async () => {
const res = await ipfsResolver.directory(ipfs, `/ipfs/${webpage.cid}`, webpage.cid)

expect(res).to.exist()
expect(res[0]).to.deep.include({
name: 'index.html'
})
})
})

describe('resolve web page (CIDv1)', function () {
let ipfs = null
let ipfsd = null

const webpage = {
cid: 'zdj7WYcfiUZa2wBeD9G2Jg9jqHx3Wh8nRsBNdVSWwsZ7XE62V',
files: {
'pp.txt': loadFixture('test/fixtures/test-site/pp.txt'),
'holmes.txt': loadFixture('test/fixtures/test-site/holmes.txt'),
'index.html': loadFixture('test/fixtures/test-site/index.html')
}
}

before(function (done) {
this.timeout(20 * 1000)
df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

const content = (name) => ({
path: `test-site/${name}`,
content: webpage.files[name]
})

const dirs = [
content('pp.txt'),
content('holmes.txt'),
content('index.html')
]

ipfs.files.add(dirs, {cidVersion: 1}, (err, res) => {
expect(err).to.not.exist()
// console.log(res)
const root = res[res.length - 1]
expect(root.path).to.equal('test-site')
expect(new CID(root.hash)).to.deep.equal(new CID(webpage.cid))
done()
})
})
})

it('should throw an error when trying to fetch a directory containing a web page', async () => {
try {
const res = await ipfsResolver.multihash(ipfs, `/ipfs/${webpage.cid}`)
const res = await ipfsResolver.cid(ipfs, `/ipfs/${webpage.cid}`)

expect(res).to.not.exist()
} catch (err) {