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

Commit 8b737b1

Browse files
committed
feat: implementation of the new put() function
BREAKING CHANGE: The API of `put()` changes. The API docs for it: > Stores the given IPLD Nodes of a recognized IPLD Format. - `nodes` (`Iterable<Object>`): deserialized IPLD nodes that should be inserted. - `format` (`multicodec`, required): the multicodec of the format that IPLD Node should be encoded in. - `options` is applied to any of the `nodes` and is an object with the following properties: - `hashAlg` (`multicodec`, default: hash algorithm of the given multicodec): the hashing algorithm that is used to calculate the CID. - `cidVersion` (`boolean`, default: 1): the CID version to use. - `onlyHash` (`boolean`, default: false): if true the serialized form of the IPLD Node will not be passed to the underlying block store. Returns an async iterator with the CIDs of the serialized IPLD Nodes.
1 parent d797667 commit 8b737b1

12 files changed

+279
-390
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
"multicodec": "~0.5.0",
6565
"pull-defer": "~0.2.3",
6666
"pull-stream": "^3.6.9",
67-
"pull-traverse": "^1.0.3"
67+
"pull-traverse": "^1.0.3",
68+
"typical": "^3.0.0"
6869
},
6970
"contributors": [
7071
"Alan Shaw <[email protected]>",

src/index.js

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const ipldDagCbor = require('ipld-dag-cbor')
1212
const ipldDagPb = require('ipld-dag-pb')
1313
const ipldRaw = require('ipld-raw')
1414
const multicodec = require('multicodec')
15+
const typical = require('typical')
1516
const { fancyIterator } = require('./util')
1617

1718
function noop () {}
@@ -170,40 +171,82 @@ class IPLDResolver {
170171
})
171172
}
172173

173-
put (node, options, callback) {
174-
if (typeof options === 'function') {
175-
callback = options
176-
return setImmediate(() => callback(
177-
new Error('IPLDResolver.put requires options')
178-
))
174+
/**
175+
* Stores the given IPLD Nodes of a recognized IPLD Format.
176+
*
177+
* @param {Iterable.<Object>} nodes - Deserialized IPLD nodes that should be inserted.
178+
* @param {number} format - The multicodec of the format that IPLD Node should be encoded in.
179+
* @param {Object} [userOptions] - Options are applied to any of the `nodes` and is an object with the following properties.
180+
* @param {number} [userOtions.hashAlg=hash algorithm of the given multicodec] - The hashing algorithm that is used to calculate the CID.
181+
* @param {number} [userOptions.cidVersion=1]`- The CID version to use.
182+
* @param {boolean} [userOptions.onlyHash=false] - If true the serialized form of the IPLD Node will not be passed to the underlying block store.
183+
* @returns {Iterable.<Promise.<CID>>} - Returns an async iterator with the CIDs of the serialized IPLD Nodes.
184+
*/
185+
put (nodes, format, userOptions) {
186+
if (!typical.isIterable(nodes) || typical.isString(nodes) ||
187+
Buffer.isBuffer(nodes)) {
188+
throw new Error('`nodes` must be an iterable')
189+
}
190+
if (format === undefined) {
191+
throw new Error('`put` requires a format')
192+
}
193+
if (typeof format !== 'number') {
194+
throw new Error('`format` parameter must be number (multicodec)')
179195
}
180-
callback = callback || noop
181196

182-
if (options.cid && CID.isCID(options.cid)) {
183-
if (options.onlyHash) {
184-
return setImmediate(() => callback(null, options.cid))
185-
}
197+
let options
198+
let formatImpl
186199

187-
return this._put(options.cid, node, callback)
188-
}
200+
const next = () => {
201+
// End iteration if there are no more nodes to put
202+
if (nodes.length === 0) {
203+
return Promise.resolve({ done: true })
204+
}
189205

190-
// TODO vmx 2018-12-07: Make this async/await once `put()` returns a
191-
// Promise
192-
this._getFormat(options.format).then((format) => {
193-
format.util.cid(node, options, (err, cid) => {
194-
if (err) {
195-
return callback(err)
206+
return new Promise(async (resolve, reject) => {
207+
// Lazy load the options not when the iterator is initialized, but
208+
// when we hit the first iteration. This way the constructor can be
209+
// a synchronous function.
210+
if (options === undefined) {
211+
try {
212+
formatImpl = await this._getFormat(format)
213+
} catch (err) {
214+
return reject(err)
215+
}
216+
const defaultOptions = {
217+
hashAlg: formatImpl.defaultHashAlg,
218+
cidVersion: 1,
219+
onlyHash: false
220+
}
221+
options = mergeOptions(defaultOptions, userOptions)
196222
}
197223

198-
if (options.onlyHash) {
199-
return callback(null, cid)
224+
const node = nodes.shift()
225+
const cidOptions = {
226+
version: options.cidVersion,
227+
hashAlg: options.hashAlg,
228+
onlyHash: options.onlyHash
200229
}
230+
formatImpl.util.cid(node, cidOptions, (err, cid) => {
231+
if (err) {
232+
return reject(err)
233+
}
201234

202-
this._put(cid, node, callback)
235+
if (options.onlyHash) {
236+
return resolve({ done: false, value: cid })
237+
}
238+
239+
this._put(cid, node, (err, cid) => {
240+
if (err) {
241+
return reject(err)
242+
}
243+
return resolve({ done: false, value: cid })
244+
})
245+
})
203246
})
204-
}).catch((err) => {
205-
callback(err)
206-
})
247+
}
248+
249+
return fancyIterator(next)
207250
}
208251

209252
treeStream (cid, path, options) {

test/basics.js

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ chai.use(chaiAsProised)
1010
const BlockService = require('ipfs-block-service')
1111
const CID = require('cids')
1212
const multihash = require('multihashes')
13+
const multicodec = require('multicodec')
1314
const pull = require('pull-stream')
1415
const inMemory = require('ipld-in-memory')
1516

@@ -62,25 +63,19 @@ module.exports = (repo) => {
6263
// })
6364
// }
6465

65-
it('put - errors on unknown resolver', (done) => {
66+
it('put - errors on unknown resolver', async () => {
6667
const bs = new BlockService(repo)
6768
const r = new IPLDResolver({ blockService: bs })
6869
// choosing a format that is not supported
69-
r.put(null, { format: 'blake2b-8' }, (err, result) => {
70-
expect(err).to.exist()
71-
expect(err.message).to.eql('No resolver found for codec "blake2b-8"')
72-
done()
73-
})
70+
const result = r.put([null], multicodec.BLAKE2B_8)
71+
await expect(result.next()).to.be.rejectedWith(
72+
'No resolver found for codec "blake2b-8"')
7473
})
7574

76-
it('put - errors if no options', (done) => {
75+
it('put - errors if no format is provided', () => {
7776
const bs = new BlockService(repo)
7877
const r = new IPLDResolver({ blockService: bs })
79-
r.put(null, (err, result) => {
80-
expect(err).to.exist()
81-
expect(err.message).to.eql('IPLDResolver.put requires options')
82-
done()
83-
})
78+
expect(() => r.put([null])).to.be.throw('`put` requires a format')
8479
})
8580

8681
it('_put - errors on unknown resolver', (done) => {

test/format-support.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,14 @@ module.exports = (repo) => {
1717
describe('IPLD format support', () => {
1818
let data, cid
1919

20-
before((done) => {
20+
before(async () => {
2121
const bs = new BlockService(repo)
2222
const resolver = new IPLDResolver({ blockService: bs })
2323

2424
data = { now: Date.now() }
2525

26-
dagCBOR.util.cid(data, (err, c) => {
27-
expect(err).to.not.exist()
28-
cid = c
29-
resolver.put(data, { cid }, done)
30-
})
26+
const result = resolver.put([data], multicodec.DAG_CBOR)
27+
cid = await result.last()
3128
})
3229

3330
describe('Dynamic format loading', () => {

test/ipld-all.js

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const each = require('async/each')
1717
const waterfall = require('async/waterfall')
1818
const CID = require('cids')
1919
const inMemory = require('ipld-in-memory')
20+
const multicodec = require('multicodec')
2021

2122
const IPLDResolver = require('../src')
2223

@@ -52,10 +53,15 @@ describe('IPLD Resolver for dag-cbor + dag-pb', () => {
5253
cidCbor = cid
5354

5455
each([
55-
{ node: nodePb, cid: cidPb },
56-
{ node: nodeCbor, cid: cidCbor }
56+
{ node: nodePb, format: multicodec.DAG_PB, cidVersion: 0 },
57+
{ node: nodeCbor, format: multicodec.DAG_CBOR, cidVersion: 1 }
5758
], (nac, cb) => {
58-
resolver.put(nac.node, { cid: nac.cid }, cb)
59+
resolver.put([nac.node], nac.format, {
60+
cidVersion: nac.cidVersion
61+
}).first().then(
62+
() => cb(null),
63+
(error) => cb(error)
64+
)
5965
}, cb)
6066
}
6167
], done)
@@ -76,12 +82,17 @@ describe('IPLD Resolver for dag-cbor + dag-pb', () => {
7682
it('does not store nodes when onlyHash is passed', (done) => {
7783
waterfall([
7884
(cb) => dagPB.DAGNode.create(Buffer.from('Some data here'), cb),
79-
(node, cb) => resolver.put(node, {
80-
onlyHash: true,
81-
version: 1,
82-
hashAlg: 'sha2-256',
83-
format: 'dag-pb'
84-
}, cb),
85+
(node, cb) => {
86+
const result = resolver.put([node], multicodec.DAG_PB, {
87+
onlyHash: true,
88+
cidVersion: 1,
89+
hashAlg: multicodec.SHA2_256
90+
})
91+
result.first().then(
92+
(cid) => cb(null, cid),
93+
(error) => cb(error)
94+
)
95+
},
8596
(cid, cb) => resolver.bs._repo.blocks.has(cid, cb)
8697
], (error, result) => {
8798
if (error) {
@@ -93,30 +104,6 @@ describe('IPLD Resolver for dag-cbor + dag-pb', () => {
93104
})
94105
})
95106

96-
it('does not store nodes when onlyHash is passed and a CID is passed', (done) => {
97-
const cid = new CID('QmTmxQfEHbQzntsXPTU4ae2ZgBGwseBmS12AkZnKCkuf2G')
98-
99-
waterfall([
100-
(cb) => dagPB.DAGNode.create(Buffer.from('Some data here'), cb),
101-
(node, cb) => resolver.put(node, {
102-
onlyHash: true,
103-
cid
104-
}, cb),
105-
(cid2, cb) => {
106-
expect(cid2).to.equal(cid)
107-
108-
resolver.bs._repo.blocks.has(cid2, cb)
109-
}
110-
], (error, result) => {
111-
if (error) {
112-
return done(error)
113-
}
114-
115-
expect(result).to.be.false()
116-
done()
117-
})
118-
})
119-
120107
describe('getMany', () => {
121108
it('should return nodes correctly', (done) => {
122109
resolver.getMany([cidCbor, cidPb], (err, result) => {

test/ipld-bitcoin.js

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const BitcoinBlock = require('bitcoinjs-lib').Block
1111
const multihash = require('multihashes')
1212
const series = require('async/series')
1313
const each = require('async/each')
14-
const pull = require('pull-stream')
14+
const multicodec = require('multicodec')
1515

1616
const IPLDResolver = require('../src')
1717

@@ -83,16 +83,12 @@ module.exports = (repo) => {
8383
}
8484
], store)
8585

86-
function store () {
87-
pull(
88-
pull.values([
89-
{ node: node1, cid: cid1 },
90-
{ node: node2, cid: cid2 },
91-
{ node: node3, cid: cid3 }
92-
]),
93-
pull.asyncMap((nac, cb) => resolver.put(nac.node, { cid: nac.cid }, cb)),
94-
pull.onEnd(done)
95-
)
86+
async function store () {
87+
const nodes = [node1, node2, node3]
88+
const result = resolver.put(nodes, multicodec.BITCOIN_BLOCK)
89+
;[cid1, cid2, cid3] = await result.all()
90+
91+
done()
9692
}
9793
})
9894

@@ -121,34 +117,26 @@ module.exports = (repo) => {
121117
})
122118

123119
describe('public api', () => {
124-
it('resolver.put', (done) => {
125-
resolver.put(node1, { cid: cid1 }, done)
126-
})
127-
128-
it('resolver.put with format', (done) => {
129-
resolver.put(node1, { format: 'bitcoin-block' }, (err, cid) => {
130-
expect(err).to.not.exist()
131-
expect(cid).to.exist()
132-
expect(cid.version).to.equal(1)
133-
expect(cid.codec).to.equal('bitcoin-block')
134-
expect(cid.multihash).to.exist()
135-
const mh = multihash.decode(cid.multihash)
136-
expect(mh.name).to.equal('dbl-sha2-256')
137-
done()
138-
})
120+
it('resolver.put with format', async () => {
121+
const result = resolver.put([node1], multicodec.BITCOIN_BLOCK)
122+
const cid = await result.first()
123+
expect(cid.version).to.equal(1)
124+
expect(cid.codec).to.equal('bitcoin-block')
125+
expect(cid.multihash).to.exist()
126+
const mh = multihash.decode(cid.multihash)
127+
expect(mh.name).to.equal('dbl-sha2-256')
139128
})
140129

141-
it('resolver.put with format + hashAlg', (done) => {
142-
resolver.put(node1, { format: 'bitcoin-block', hashAlg: 'sha3-512' }, (err, cid) => {
143-
expect(err).to.not.exist()
144-
expect(cid).to.exist()
145-
expect(cid.version).to.equal(1)
146-
expect(cid.codec).to.equal('bitcoin-block')
147-
expect(cid.multihash).to.exist()
148-
const mh = multihash.decode(cid.multihash)
149-
expect(mh.name).to.equal('sha3-512')
150-
done()
130+
it('resolver.put with format + hashAlg', async () => {
131+
const result = resolver.put([node1], multicodec.BITCOIN_BLOCK, {
132+
hashAlg: multicodec.SHA3_512
151133
})
134+
const cid = await result.first()
135+
expect(cid.version).to.equal(1)
136+
expect(cid.codec).to.equal('bitcoin-block')
137+
expect(cid.multihash).to.exist()
138+
const mh = multihash.decode(cid.multihash)
139+
expect(mh.name).to.equal('sha3-512')
152140
})
153141

154142
// TODO vmx 2018-11-30: Implement getting the whole object properly

0 commit comments

Comments
 (0)