This repository has been archived by the owner on Mar 10, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 298
[WIP] feat(dag): add IPLD to dag.get #755
Open
alanshaw
wants to merge
3
commits into
master
Choose a base branch
from
feat/dag-get-with-ipld
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
'use strict' | ||
|
||
const dagPB = require('ipld-dag-pb') | ||
const dagCBOR = require('ipld-dag-cbor') | ||
const promisify = require('promisify-es6') | ||
const CID = require('cids') | ||
const waterfall = require('async/waterfall') | ||
const IPLDResolver = require('ipld') | ||
const explain = require('explain-error') | ||
const dagPB = require('ipld-dag-pb/src/ipfs') | ||
const ipfsPath = require('../utils/ipfs-path') | ||
const block = require('../block') | ||
|
||
module.exports = (send) => { | ||
const blockGet = block(send).get | ||
|
||
return promisify((cid, path, options, callback) => { | ||
if (typeof path === 'function') { | ||
callback = path | ||
|
@@ -22,31 +24,20 @@ module.exports = (send) => { | |
options = options || {} | ||
path = path || '' | ||
|
||
if (CID.isCID(cid)) { | ||
cid = cid.toBaseEncodedString() | ||
try { | ||
const res = ipfsPath(cid) | ||
cid = res.cid | ||
path = res.path || path | ||
} catch (err) { | ||
return callback(err) | ||
} | ||
|
||
waterfall([ | ||
cb => { | ||
send({ | ||
path: 'dag/resolve', | ||
args: cid + '/' + path, | ||
qs: options | ||
}, cb) | ||
}, | ||
(resolved, cb) => { | ||
block(send).get(new CID(resolved['Cid']['/']), (err, ipfsBlock) => { | ||
cb(err, ipfsBlock, resolved['RemPath']) | ||
}) | ||
}, | ||
(ipfsBlock, path, cb) => { | ||
if (ipfsBlock.cid.codec === 'dag-cbor') { | ||
dagCBOR.resolver.resolve(ipfsBlock.data, path, cb) | ||
} | ||
if (ipfsBlock.cid.codec === 'dag-pb') { | ||
dagPB.resolver.resolve(ipfsBlock.data, path, cb) | ||
} | ||
} | ||
], callback) | ||
IPLDResolver.inMemory((err, ipld) => { | ||
if (err) return callback(explain(err, 'failed to create IPLD resolver')) | ||
ipld.support.rm(dagPB.resolver.multicodec) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a comment here, why this is needed? It's clear to me, but I don't think it's clear when you read the code for the first time. |
||
ipld.support.add(dagPB.resolver.multicodec, dagPB.resolver, dagPB.util) | ||
ipld.bs.setExchange({ get: blockGet }) | ||
ipld.get(cid, path, options, callback) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
'use strict' | ||
|
||
const CID = require('cids') | ||
const explain = require('explain-error') | ||
|
||
// Parse an `input` as an IPFS path and return an object of it's component parts. | ||
// | ||
// `input` can be: | ||
// | ||
// `String` | ||
// * an IPFS path like `/ipfs/Qmf1JJkBEk7nSdYZJqumJVREE1bMZS7uMm6DQFxRxWShwD/file.txt` | ||
// * an IPNS path like `/ipns/yourdomain.name/file.txt` | ||
// * a CID like `Qmf1JJkBEk7nSdYZJqumJVREE1bMZS7uMm6DQFxRxWShwD` | ||
// * a CID and path like `Qmf1JJkBEk7nSdYZJqumJVREE1bMZS7uMm6DQFxRxWShwD/file.txt` | ||
// `CID` - a CID instance | ||
// `Buffer` - a Buffer CID | ||
// | ||
// The return value is an object with the following properties: | ||
// | ||
// * `cid: CID` - the content identifier | ||
// * `path: String` - the path component of the dweb path (the bit after the cid) | ||
module.exports = (input) => { | ||
let cid, path | ||
|
||
if (Buffer.isBuffer(input) || CID.isCID(input)) { | ||
try { | ||
cid = new CID(input) | ||
} catch (err) { | ||
throw explain(err, 'invalid CID') | ||
} | ||
|
||
path = '' | ||
} else if (Object.prototype.toString.call(input) === '[object String]') { | ||
// Ensure leading slash | ||
if (input[0] !== '/') { | ||
input = `/${input}` | ||
} | ||
|
||
// Remove trailing slash | ||
if (input[input.length - 1] === '/') { | ||
input = input.slice(0, -1) | ||
} | ||
|
||
const parts = input.split('/') | ||
|
||
if (parts[1] === 'ipfs') { | ||
try { | ||
cid = new CID(parts[2]) | ||
} catch (err) { | ||
throw explain(err, `invalid CID: ${parts[2]}`) | ||
} | ||
|
||
path = parts.slice(3).join('/') | ||
} else { | ||
// Is parts[1] a CID? | ||
try { | ||
cid = new CID(parts[1]) | ||
} catch (err) { | ||
throw new Error(`unknown namespace: ${parts[1]}`) | ||
} | ||
|
||
path = parts.slice(2).join('/') | ||
} | ||
|
||
// Ensure leading slash on non empty path | ||
if (path.length) { | ||
path = `/${path}` | ||
} | ||
} else { | ||
throw new Error('invalid path') // What even is this? | ||
} | ||
|
||
return { cid, path } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* eslint-env mocha */ | ||
'use strict' | ||
|
||
const chai = require('chai') | ||
const dirtyChai = require('dirty-chai') | ||
const expect = chai.expect | ||
chai.use(dirtyChai) | ||
const CID = require('cids') | ||
const ipfsPath = require('../src/utils/ipfs-path') | ||
|
||
describe('utils/ipfs-path', () => { | ||
it('should parse input as string CID', () => { | ||
const input = 'QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn' | ||
const { cid, path } = ipfsPath(input) | ||
|
||
expect(cid.toBaseEncodedString()).to.equal('QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn') | ||
expect(path).to.equal('') | ||
}) | ||
|
||
it('should parse input as buffer CID', () => { | ||
const input = Buffer.from('017012207252523e6591fb8fe553d67ff55a86f84044b46a3e4176e10c58fa529a4aabd5', 'hex') | ||
const { cid, path } = ipfsPath(input) | ||
|
||
expect(cid.toBaseEncodedString()).to.equal('zdj7Wd8AMwqnhJGQCbFxBVodGSBG84TM7Hs1rcJuQMwTyfEDS') | ||
expect(path).to.equal('') | ||
}) | ||
|
||
it('should parse input as CID instance', () => { | ||
const input = new CID('zdpuArHMUAYi3VtD3f7iSkXxYK9xo687SoNf5stAQNCMzd77k') | ||
const { cid, path } = ipfsPath(input) | ||
|
||
expect(cid.equals(input)).to.equal(true) | ||
expect(path).to.equal('') | ||
}) | ||
|
||
it('should parse input as string with path and without namespace', () => { | ||
const input = 'QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn/path/to' | ||
const { cid, path } = ipfsPath(input) | ||
|
||
expect(cid.toBaseEncodedString()).to.equal('QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn') | ||
expect(path).to.equal('/path/to') | ||
}) | ||
|
||
it('should parse input as string without leading slash', () => { | ||
const input = 'ipfs/QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn/path/to' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this actually be supported? I prefer having strict rules and fail early. The rule would be: path with namespaces start with a slash, if there isn't a slash it's a CID. |
||
const { cid, path } = ipfsPath(input) | ||
|
||
expect(cid.toBaseEncodedString()).to.equal('QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn') | ||
expect(path).to.equal('/path/to') | ||
}) | ||
|
||
it('should parse input as string with trailing slash', () => { | ||
const input = '/ipfs/QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn/path/to/' | ||
const { cid, path } = ipfsPath(input) | ||
|
||
expect(cid.toBaseEncodedString()).to.equal('QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn') | ||
expect(path).to.equal('/path/to') | ||
}) | ||
|
||
it('should throw on unknown namespace', () => { | ||
const input = '/junk/stuff' | ||
expect(() => ipfsPath(input)).to.throw('unknown namespace: junk') | ||
}) | ||
|
||
it('should throw on invalid CID in string', () => { | ||
const input = '/ipfs/notACID/some/path' | ||
expect(() => ipfsPath(input)).to.throw('invalid CID') | ||
}) | ||
|
||
it('should throw on invalid CID in buffer', () => { | ||
const input = Buffer.from('notaCID') | ||
expect(() => ipfsPath(input)).to.throw('invalid CID') | ||
}) | ||
|
||
it('should throw on invalid path', () => { | ||
const input = 42 | ||
expect(() => ipfsPath(input)).to.throw('invalid path') | ||
}) | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additional test cases:
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been thinking that this should be done by passing the
ipfs.block.{get,put}
as the Block Service, rather than doing an inMemory resolver and then monkey patching the Block Service Instance with a fake exchange.You should be able to do:
or close