Skip to content

Commit

Permalink
feat: add dag-json resolver (#397)
Browse files Browse the repository at this point in the history
Basically the same as `dag-cbor`...allows the exporter to `walkPath` and export `dag-json` encoded data.
  • Loading branch information
Alan Shaw authored Jan 16, 2024
1 parent 54de7bc commit 1b40e3e
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/ipfs-unixfs-exporter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
},
"dependencies": {
"@ipld/dag-cbor": "^9.0.0",
"@ipld/dag-json": "^10.1.7",
"@ipld/dag-pb": "^4.0.0",
"@multiformats/murmur3": "^2.0.0",
"err-code": "^3.0.1",
Expand Down
67 changes: 67 additions & 0 deletions packages/ipfs-unixfs-exporter/src/resolvers/dag-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import * as dagJson from '@ipld/dag-json'
import errCode from 'err-code'
import { CID } from 'multiformats/cid'
import type { Resolver } from '../index.js'

const resolve: Resolver = async (cid, name, path, toResolve, resolve, depth, blockstore, options) => {
const block = await blockstore.get(cid, options)
const object = dagJson.decode<any>(block)
let subObject = object
let subPath = path

while (toResolve.length > 0) {
const prop = toResolve[0]

if (prop in subObject) {
// remove the bit of the path we have resolved
toResolve.shift()
subPath = `${subPath}/${prop}`

const subObjectCid = CID.asCID(subObject[prop])
if (subObjectCid != null) {
return {
entry: {
type: 'object',
name,
path,
cid,
node: block,
depth,
size: BigInt(block.length),
content: async function * () {
yield object
}
},
next: {
cid: subObjectCid,
name: prop,
path: subPath,
toResolve
}
}
}

subObject = subObject[prop]
} else {
// cannot resolve further
throw errCode(new Error(`No property named ${prop} found in dag-json node ${cid}`), 'ERR_NO_PROP')
}
}

return {
entry: {
type: 'object',
name,
path,
cid,
node: block,
depth,
size: BigInt(block.length),
content: async function * () {
yield object
}
}
}
}

export default resolve
3 changes: 3 additions & 0 deletions packages/ipfs-unixfs-exporter/src/resolvers/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as dagCbor from '@ipld/dag-cbor'
import * as dagJson from '@ipld/dag-json'
import * as dagPb from '@ipld/dag-pb'
import errCode from 'err-code'
import * as raw from 'multiformats/codecs/raw'
import { identity } from 'multiformats/hashes/identity'
import dagCborResolver from './dag-cbor.js'
import dagJsonResolver from './dag-json.js'
import identifyResolver from './identity.js'
import rawResolver from './raw.js'
import dagPbResolver from './unixfs-v1/index.js'
Expand All @@ -13,6 +15,7 @@ const resolvers: Record<number, Resolver> = {
[dagPb.code]: dagPbResolver,
[raw.code]: rawResolver,
[dagCbor.code]: dagCborResolver,
[dagJson.code]: dagJsonResolver,
[identity.code]: identifyResolver
}

Expand Down
34 changes: 34 additions & 0 deletions packages/ipfs-unixfs-exporter/test/exporter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-env mocha */

import * as dagCbor from '@ipld/dag-cbor'
import * as dagJson from '@ipld/dag-json'
import * as dagPb from '@ipld/dag-pb'
import { expect } from 'aegir/chai'
import { MemoryBlockstore } from 'blockstore-core'
Expand Down Expand Up @@ -1008,6 +1009,39 @@ describe('exporter', () => {
return expect(first(exported.content())).to.eventually.deep.equal(node)
})

it('errors when exporting a non-existent key from a json node', async () => {
const node = {
foo: 'bar'
}

const jsonBlock = dagJson.encode(node)
const cid = CID.createV1(dagJson.code, await sha256.digest(jsonBlock))
await block.put(cid, jsonBlock)

try {
await exporter(`${cid}/baz`, block)
} catch (err: any) {
expect(err.code).to.equal('ERR_NO_PROP')
}
})

it('exports a json node', async () => {
const node = {
foo: 'bar'
}

const jsonBlock = dagJson.encode(node)
const cid = CID.createV1(dagJson.code, await sha256.digest(jsonBlock))
await block.put(cid, jsonBlock)
const exported = await exporter(`${cid}`, block)

if (exported.type !== 'object') {
throw new Error('Unexpected type')
}

return expect(first(exported.content())).to.eventually.deep.equal(node)
})

it('errors when exporting a node with no resolver', async () => {
const cid = CID.create(1, 0x78, CID.parse('zdj7WkRPAX9o9nb9zPbXzwG7JEs78uyhwbUs8JSUayB98DWWY').multihash)

Expand Down

0 comments on commit 1b40e3e

Please sign in to comment.