Skip to content

Commit a247b50

Browse files
achingbrainvasco-santossimonovic86
authored
feat: enable custom formats for dag put and get (#3347)
Ensures we can use custom/extra IPLD formats with `ipfs.dag.get` and `ipfs.dag.put` over HTTP as well as directly into core. Adds an example to show how to do it. The basic idea is you configure your node with the extra formats and pass that node into the http server, so instead of having the IPLD format logic in the http method endpoint and in core it's just in core. The http client works the same as it did before. Co-authored-by: Vasco Santos <[email protected]> Co-authored-by: Janko Simonovic <[email protected]>
1 parent 549b955 commit a247b50

File tree

4 files changed

+72
-55
lines changed

4 files changed

+72
-55
lines changed

src/dag/get.js

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,29 @@
11
'use strict'
22

3-
const dagPB = require('ipld-dag-pb')
4-
const dagCBOR = require('ipld-dag-cbor')
5-
const raw = require('ipld-raw')
63
const configure = require('../lib/configure')
4+
const multicodec = require('multicodec')
5+
const loadFormat = require('../lib/ipld-formats')
76

8-
const resolvers = {
9-
'dag-cbor': dagCBOR.resolver,
10-
'dag-pb': dagPB.resolver,
11-
raw: raw.resolver
12-
}
13-
14-
module.exports = configure((api, options) => {
15-
const getBlock = require('../block/get')(options)
16-
const dagResolve = require('./resolve')(options)
7+
module.exports = configure((api, opts) => {
8+
const getBlock = require('../block/get')(opts)
9+
const dagResolve = require('./resolve')(opts)
10+
const load = loadFormat(opts.ipld)
1711

1812
/**
1913
* @type {import('..').Implements<import('ipfs-core/src/components/dag/get')>}
2014
*/
2115
const get = async (cid, options = {}) => {
2216
const resolved = await dagResolve(cid, options)
2317
const block = await getBlock(resolved.cid, options)
24-
const dagResolver = resolvers[resolved.cid.codec]
2518

26-
if (!dagResolver) {
27-
throw Object.assign(
28-
new Error(`Missing IPLD format "${resolved.cid.codec}"`),
29-
{ missingMulticodec: resolved.cid.codec }
30-
)
31-
}
19+
const codecName = multicodec.getName(resolved.cid.code)
20+
const format = await load(codecName)
3221

33-
if (resolved.cid.codec === 'raw' && !resolved.remainderPath) {
22+
if (resolved.cid.code === multicodec.RAW && !resolved.remainderPath) {
3423
resolved.remainderPath = '/'
3524
}
3625

37-
return dagResolver.resolve(block.data, resolved.remainderPath)
26+
return format.resolver.resolve(block.data, resolved.remainderPath)
3827
}
3928

4029
return get

src/dag/put.js

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
'use strict'
22

3-
const dagCBOR = require('ipld-dag-cbor')
4-
const dagPB = require('ipld-dag-pb')
5-
const ipldRaw = require('ipld-raw')
63
const CID = require('cids')
74
const multihash = require('multihashes')
85
const configure = require('../lib/configure')
@@ -11,19 +8,10 @@ const toUrlSearchParams = require('../lib/to-url-search-params')
118
const { anySignal } = require('any-signal')
129
const AbortController = require('native-abort-controller')
1310
const multicodec = require('multicodec')
11+
const loadFormat = require('../lib/ipld-formats')
1412

1513
module.exports = configure((api, opts) => {
16-
const formats = {
17-
[multicodec.DAG_PB]: dagPB,
18-
[multicodec.DAG_CBOR]: dagCBOR,
19-
[multicodec.RAW]: ipldRaw
20-
}
21-
22-
const ipldOptions = (opts && opts.ipld) || {}
23-
const configuredFormats = (ipldOptions && ipldOptions.formats) || []
24-
configuredFormats.forEach(format => {
25-
formats[format.codec] = format
26-
})
14+
const load = loadFormat(opts.ipld)
2715

2816
/**
2917
* @type {import('..').Implements<import('ipfs-core/src/components/dag/put')>}
@@ -39,7 +27,7 @@ module.exports = configure((api, opts) => {
3927
const cid = new CID(options.cid)
4028
options = {
4129
...options,
42-
format: cid.codec,
30+
format: multicodec.getName(cid.code),
4331
hashAlg: multihash.decode(cid.multihash).name
4432
}
4533
delete options.cid
@@ -52,24 +40,7 @@ module.exports = configure((api, opts) => {
5240
...options
5341
}
5442

55-
const number = multicodec.getNumber(settings.format)
56-
let format = formats[number]
57-
58-
if (!format) {
59-
if (opts && opts.ipld && opts.ipld.loadFormat) {
60-
// @ts-ignore - loadFormat expect string but it could be a number
61-
format = await opts.ipld.loadFormat(settings.format)
62-
}
63-
64-
if (!format) {
65-
throw new Error('Format unsupported - please add support using the options.ipld.formats or options.ipld.loadFormat options')
66-
}
67-
}
68-
69-
if (!format.util || !format.util.serialize) {
70-
throw new Error('Format does not support utils.serialize function')
71-
}
72-
43+
const format = await load(settings.format)
7344
const serialized = format.util.serialize(dagNode)
7445

7546
// allow aborting requests on body errors

src/lib/ipld-formats.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use strict'
2+
3+
const dagPB = require('ipld-dag-pb')
4+
const dagCBOR = require('ipld-dag-cbor')
5+
const raw = require('ipld-raw')
6+
const multicodec = require('multicodec')
7+
8+
const noop = () => {}
9+
10+
/**
11+
* @typedef {import('cids')} CID
12+
*/
13+
14+
/**
15+
* Return an object containing supported IPLD Formats
16+
*
17+
* @param {object} [options] - IPLD options passed to the http client constructor
18+
* @param {Array} [options.formats] - A list of IPLD Formats to use
19+
* @param {Function} [options.loadFormat] - An async function that can load a format when passed a codec number
20+
* @returns {Function}
21+
*/
22+
module.exports = ({ formats = [], loadFormat = noop } = {}) => {
23+
formats = formats || []
24+
loadFormat = loadFormat || noop
25+
26+
const configuredFormats = {
27+
[multicodec.DAG_PB]: dagPB,
28+
[multicodec.DAG_CBOR]: dagCBOR,
29+
[multicodec.RAW]: raw
30+
}
31+
32+
formats.forEach(format => {
33+
configuredFormats[format.codec] = format
34+
})
35+
36+
/**
37+
* Attempts to load an IPLD format for the passed CID
38+
*
39+
* @param {string} codec - The code to load the format for
40+
* @returns {Promise<object>} - An IPLD format
41+
*/
42+
const loadResolver = async (codec) => {
43+
const number = multicodec.getNumber(codec)
44+
const format = configuredFormats[number] || await loadFormat(codec)
45+
46+
if (!format) {
47+
throw Object.assign(
48+
new Error(`Missing IPLD format "${codec}"`),
49+
{ missingMulticodec: codec }
50+
)
51+
}
52+
53+
return format
54+
}
55+
56+
return loadResolver
57+
}

test/dag.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('.dag', function () {
7373
it('should error when putting node with esoteric format', () => {
7474
const node = uint8ArrayFromString('some data')
7575

76-
return expect(ipfs.dag.put(node, { format: 'git-raw', hashAlg: 'sha2-256' })).to.eventually.be.rejectedWith(/Format unsupported/)
76+
return expect(ipfs.dag.put(node, { format: 'git-raw', hashAlg: 'sha2-256' })).to.eventually.be.rejectedWith(/Missing IPLD format/)
7777
})
7878

7979
it('should attempt to load an unsupported format', async () => {

0 commit comments

Comments
 (0)