Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 71d4718

Browse files
committed
feat!: update ipfs for DAG GET API to match [email protected]
* Don't print new-line character * Add --output-codec and allow printing of arbitrary output codecs * Default to dag-json * Remove funky old protonode wrapper type for dag-pb * Keep data-enc but only enable it if you're printing a Bytes node
1 parent 5158b21 commit 71d4718

File tree

2 files changed

+116
-76
lines changed

2 files changed

+116
-76
lines changed
Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import parseDuration from 'parse-duration'
22
import { toCidAndPath } from 'ipfs-core-utils/to-cid-and-path'
33
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
4-
import {
5-
stripControlCharacters,
6-
makeEntriesPrintable,
7-
escapeControlCharacters
8-
} from '../../utils.js'
94
import * as dagPB from '@ipld/dag-pb'
105
import * as dagCBOR from '@ipld/dag-cbor'
116
import * as dagJSON from '@ipld/dag-json'
127
import * as raw from 'multiformats/codecs/raw'
138

9+
/**
10+
* @template T
11+
* @typedef {import('multiformats/codecs/interface').BlockCodec<number, T>} BlockCodec
12+
*/
13+
14+
const codecs = [dagCBOR, dagJSON, dagPB, raw].reduce((/** @type {Record<string, BlockCodec<any>>} */ m, codec) => {
15+
m[codec.name] = codec
16+
return m
17+
}, /** @type {Record<string, BlockCodec<any>>} */ {})
18+
1419
export default {
1520
command: 'get <cid path>',
1621

@@ -21,16 +26,16 @@ export default {
2126
type: 'boolean',
2227
default: false
2328
},
24-
'cid-base': {
25-
describe: 'Number base to display CIDs in.',
29+
'output-codec': {
30+
describe: 'Codec to encode data in before displaying.',
2631
type: 'string',
27-
default: 'base58btc'
32+
choices: ['dag-json', 'dag-cbor', 'dag-pb', 'raw'],
33+
default: 'dag-json'
2834
},
2935
'data-enc': {
30-
describe: 'String encoding to display data in.',
36+
describe: 'String encoding to display raw node data in if using "raw" output-codec.',
3137
type: 'string',
32-
choices: ['base16', 'base64', 'base58btc'],
33-
default: 'base64'
38+
choices: ['base16', 'base64', 'base58btc']
3439
},
3540
timeout: {
3641
type: 'string',
@@ -42,12 +47,12 @@ export default {
4247
* @param {object} argv
4348
* @param {import('../../types').Context} argv.ctx
4449
* @param {string} argv.cidpath
45-
* @param {string} argv.cidBase
50+
* @param {'dag-json' | 'dag-cbor' | 'dag-pb' | 'raw'} argv.outputCodec
4651
* @param {'base16' | 'base64' | 'base58btc'} argv.dataEnc
4752
* @param {boolean} argv.localResolve
4853
* @param {number} argv.timeout
4954
*/
50-
async handler ({ ctx: { ipfs, print }, cidpath, cidBase, dataEnc, localResolve, timeout }) {
55+
async handler ({ ctx: { ipfs, print }, cidpath, dataEnc, outputCodec, localResolve, timeout }) {
5156
const options = {
5257
localResolve,
5358
timeout
@@ -74,27 +79,26 @@ export default {
7479
}
7580

7681
const node = result.value
77-
const base = await ipfs.bases.getBase(cidBase)
78-
79-
// TODO: just plain dag-json output by default, or use output-codec
80-
if (cid.code === dagPB.code) {
81-
/** @type {import('@ipld/dag-pb').PBNode} */
82-
const dagNode = node
8382

84-
print(JSON.stringify({
85-
data: dagNode.Data ? uint8ArrayToString(node.Data, dataEnc) : undefined,
86-
links: (dagNode.Links || []).map(link => ({
87-
Name: stripControlCharacters(link.Name),
88-
Size: link.Tsize,
89-
Cid: { '/': link.Hash.toString(base.encoder) }
90-
}))
91-
}))
92-
} else if (cid.code === raw.code) {
93-
print(uint8ArrayToString(node, dataEnc))
94-
} else if (cid.code === dagCBOR.code || cid.code === dagJSON.code) {
95-
print(JSON.stringify(makeEntriesPrintable(node, base)))
83+
if (outputCodec === 'raw') {
84+
if (!(node instanceof Uint8Array)) {
85+
print('dag get cannot print a non-bytes node as "raw"')
86+
return
87+
}
88+
if (dataEnc) {
89+
print(uint8ArrayToString(node, dataEnc), false)
90+
} else {
91+
print.write(node)
92+
}
9693
} else {
97-
print(escapeControlCharacters(node.toString()))
94+
const codec = codecs[outputCodec]
95+
if (!codec) {
96+
print(`unsupported codec "${outputCodec}"`)
97+
return
98+
}
99+
// TODO choose codec
100+
const output = codec.encode(node)
101+
print(output, false)
98102
}
99103
}
100104
}

packages/ipfs-cli/test/dag.spec.js

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,10 @@ describe('dag', () => {
4747
}
4848

4949
ipfs.dag.get.withArgs(rawCid, defaultOptions).returns(result)
50-
ipfs.bases.getBase.withArgs('base58btc').returns(base58btc)
5150

52-
const out = await cli(`dag get ${rawCid} --data-enc base16`, { ipfs })
51+
const out = await cli(`dag get ${rawCid} --output-codec raw --data-enc base16`, { ipfs })
5352

54-
expect(out).to.equal(uint8ArrayToString(result.value, 'base16') + '\n')
53+
expect(out).to.equal(uint8ArrayToString(result.value, 'base16'))
5554
})
5655

5756
it('should get a dag-pb node', async () => {
@@ -67,14 +66,13 @@ describe('dag', () => {
6766
}
6867

6968
ipfs.dag.get.withArgs(dagPbCid, defaultOptions).returns(result)
70-
ipfs.bases.getBase.withArgs('base58btc').returns(base58btc)
7169

7270
const out = await cli(`dag get ${dagPbCid}`, { ipfs })
7371

74-
expect(out).to.equal(`{"data":"AAED","links":[{"Name":"foo","Size":10,"Cid":{"/":"${dagCborCid.toString(base58btc)}"}}]}\n`)
72+
expect(out).to.equal(`{"Data":{"/":{"bytes":"AAED"}},"Links":[{"Hash":{"/":"${dagCborCid.toString()}"},"Name":"foo","Tsize":10}]}`)
7573
})
7674

77-
it('should get a dag-pb node and specify data encoding', async () => {
75+
it('should get a dag-pb node as dag-pb', async () => {
7876
const result = {
7977
value: {
8078
Data: Buffer.from([0, 1, 3]),
@@ -87,14 +85,13 @@ describe('dag', () => {
8785
}
8886

8987
ipfs.dag.get.withArgs(dagPbCid, defaultOptions).returns(result)
90-
ipfs.bases.getBase.withArgs('base58btc').returns(base58btc)
9188

92-
const out = await cli(`dag get ${dagPbCid} --data-enc base16`, { ipfs })
89+
const out = await cli(`dag get ${dagPbCid} --output-codec dag-pb`, { ipfs, raw: true })
9390

94-
expect(out).to.equal(`{"data":"000103","links":[{"Name":"foo","Size":10,"Cid":{"/":"${dagCborCid.toString(base58btc)}"}}]}\n`)
91+
expect(out).to.deep.equal(Buffer.from('122d0a2401711220b80784f97f67ad80d52575d643044ffb37b20f8d4db32ae59e47b1ac68df20e01203666f6f180a0a03000103', 'hex'))
9592
})
9693

97-
it('should get a dag-pb node and specify CID encoding', async () => {
94+
it('should get a dag-pb node as dag-cbor', async () => {
9895
const result = {
9996
value: {
10097
Data: Buffer.from([0, 1, 3]),
@@ -107,11 +104,55 @@ describe('dag', () => {
107104
}
108105

109106
ipfs.dag.get.withArgs(dagPbCid, defaultOptions).returns(result)
110-
ipfs.bases.getBase.withArgs('base64').returns(base64)
111107

112-
const out = await cli(`dag get ${dagPbCid} --cid-base base64`, { ipfs })
108+
const out = await cli(`dag get ${dagPbCid} --output-codec dag-cbor`, { ipfs, raw: true })
109+
110+
expect(out).to.deep.equal(Buffer.from('a2644461746143000103654c696e6b7381a36448617368d82a58250001711220b80784f97f67ad80d52575d643044ffb37b20f8d4db32ae59e47b1ac68df20e0644e616d6563666f6f655473697a650a', 'hex'))
111+
})
112+
113+
it('should fail to get a non bytes node with "raw"', async () => {
114+
const result = {
115+
value: {
116+
Data: Buffer.from([0, 1, 3]),
117+
Links: [{
118+
Hash: dagCborCid,
119+
Name: 'foo',
120+
Tsize: 10
121+
}]
122+
}
123+
}
124+
125+
ipfs.dag.get.withArgs(dagPbCid, defaultOptions).returns(result)
126+
127+
const out = await cli(`dag get ${dagPbCid} --output-codec raw --data-enc base16`, { ipfs })
113128

114-
expect(out).to.equal(`{"data":"AAED","links":[{"Name":"foo","Size":10,"Cid":{"/":"${dagCborCid.toString(base64)}"}}]}\n`)
129+
expect(out).to.equal('dag get cannot print a non-bytes node as "raw"\n')
130+
})
131+
132+
it('should get a bytes node of a non-bytes block with "raw"', async () => {
133+
// in this instance we're pretending to path into a 'Data' property of a dag-pb block
134+
const result = {
135+
value: Buffer.from([0, 1, 3])
136+
}
137+
138+
ipfs.dag.get.withArgs(dagPbCid, { ...defaultOptions, path: '/Data' }).returns(result)
139+
140+
const out = await cli(`dag get ${dagPbCid}/Data --output-codec raw --data-enc base16`, { ipfs })
141+
142+
expect(out).to.equal('000103')
143+
})
144+
145+
it('should get raw bytes without data encoding', async () => {
146+
// in this instance we're pretending to path into a 'Data' property of a dag-pb block
147+
const result = {
148+
value: Buffer.from([0, 1, 3])
149+
}
150+
151+
ipfs.dag.get.withArgs(rawCid, defaultOptions).returns(result)
152+
153+
const out = await cli(`dag get ${rawCid} --output-codec raw`, { ipfs })
154+
155+
expect(out).to.equal(Buffer.from([0, 1, 3]).toString())
115156
})
116157

117158
it('should get a dag-cbor node', async () => {
@@ -122,43 +163,39 @@ describe('dag', () => {
122163
}
123164

124165
ipfs.dag.get.withArgs(dagCborCid, defaultOptions).returns(result)
125-
ipfs.bases.getBase.withArgs('base58btc').returns(base58btc)
126166

127167
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
128168

129-
expect(out).to.equal('{"foo":"bar"}\n')
169+
expect(out).to.equal('{"foo":"bar"}')
130170
})
131171

132-
it('should get a dag-cbor node with a nested CID', async () => {
172+
it('should get a dag-cbor node as dag-cbor', async () => {
133173
const result = {
134174
value: {
135-
foo: 'bar',
136-
baz: dagPbCid
175+
foo: 'bar'
137176
}
138177
}
139178

140179
ipfs.dag.get.withArgs(dagCborCid, defaultOptions).returns(result)
141-
ipfs.bases.getBase.withArgs('base58btc').returns(base58btc)
142180

143-
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
181+
const out = await cli(`dag get ${dagCborCid} --output-codec dag-cbor`, { ipfs, raw: true })
144182

145-
expect(out).to.equal(`{"foo":"bar","baz":{"/":"${dagPbCid}"}}\n`)
183+
expect(out).to.deep.equal(Buffer.from('a163666f6f63626172', 'hex'))
146184
})
147185

148-
it('should get a dag-cbor node with a nested CID and change the encoding', async () => {
186+
it('should get a dag-cbor node with a nested CID', async () => {
149187
const result = {
150188
value: {
151189
foo: 'bar',
152-
baz: rawCid
190+
baz: dagPbCid
153191
}
154192
}
155193

156194
ipfs.dag.get.withArgs(dagCborCid, defaultOptions).returns(result)
157-
ipfs.bases.getBase.withArgs('base64').returns(base64)
158195

159-
const out = await cli(`dag get ${dagCborCid} --cid-base=base64`, { ipfs })
196+
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
160197

161-
expect(out).to.equal(`{"foo":"bar","baz":{"/":"${rawCid.toString(base64)}"}}\n`)
198+
expect(out).to.equal(`{"baz":{"/":"${dagPbCid}"},"foo":"bar"}`)
162199
})
163200

164201
it('should get a node with a deep path', async () => {
@@ -172,9 +209,9 @@ describe('dag', () => {
172209
path
173210
}).returns(result)
174211

175-
const out = await cli(`dag get ${rawCid}${path} --data-enc base16`, { ipfs })
212+
const out = await cli(`dag get ${rawCid}${path} --output-codec raw --data-enc base16`, { ipfs })
176213

177-
expect(out).to.be.eql(uint8ArrayToString(result.value, 'base16') + '\n')
214+
expect(out).to.be.eql(uint8ArrayToString(result.value, 'base16'))
178215
})
179216

180217
it('should get a node with a deep path and an ipfs prefix', async () => {
@@ -188,9 +225,9 @@ describe('dag', () => {
188225
path
189226
}).returns(result)
190227

191-
const out = await cli(`dag get /ipfs/${rawCid}${path} --data-enc base16`, { ipfs })
228+
const out = await cli(`dag get /ipfs/${rawCid}${path} --output-codec raw --data-enc base16`, { ipfs })
192229

193-
expect(out).to.be.eql(uint8ArrayToString(result.value, 'base16') + '\n')
230+
expect(out).to.be.eql(uint8ArrayToString(result.value, 'base16'))
194231
})
195232

196233
it('should get a node with local resolve', async () => {
@@ -203,11 +240,11 @@ describe('dag', () => {
203240
localResolve: true
204241
}).returns(result)
205242

206-
const out = await cli(`dag get ${rawCid} --local-resolve --data-enc base16`, { ipfs })
243+
const out = await cli(`dag get ${rawCid} --local-resolve --output-codec raw --data-enc base16`, { ipfs })
207244

208245
expect(out).to.include('resolving path within the node only\n')
209246
expect(out).to.include('remainder path: n/a\n')
210-
expect(out).to.include(uint8ArrayToString(result.value, 'base16') + '\n')
247+
expect(out).to.include(uint8ArrayToString(result.value, 'base16'))
211248
})
212249

213250
it('should get a node with a timeout', async () => {
@@ -220,9 +257,9 @@ describe('dag', () => {
220257
timeout: 1000
221258
}).returns(result)
222259

223-
const out = await cli(`dag get ${rawCid} --timeout=1s --data-enc base16`, { ipfs })
260+
const out = await cli(`dag get ${rawCid} --timeout=1s --output-codec raw --data-enc base16`, { ipfs })
224261

225-
expect(out).to.be.eql(uint8ArrayToString(result.value, 'base16') + '\n')
262+
expect(out).to.be.eql(uint8ArrayToString(result.value, 'base16'))
226263
})
227264

228265
it('should strip control characters from dag-pb nodes', async () => {
@@ -237,14 +274,13 @@ describe('dag', () => {
237274
}
238275

239276
ipfs.dag.get.withArgs(dagPbCid, defaultOptions).returns(result)
240-
ipfs.bases.getBase.withArgs('base58btc').returns(base58btc)
241277

242278
const out = await cli(`dag get ${dagPbCid}`, { ipfs })
243279

244-
expect(out).to.equal(`{"links":[{"Name":"foo.txt","Size":9000,"Cid":{"/":"${dagPbCid.toString(base58btc)}"}}]}\n`)
280+
expect(out).to.equal(`{"Links":[{"Hash":{"/":"${dagPbCid.toString(base58btc)}"},"Name":"foo\\b\\n\\t.txt","Tsize":9000}]}`)
245281
})
246282

247-
it('should strip control characters from dag-cbor nodes', async () => {
283+
it('should not strip control characters from dag-cbor nodes', async () => {
248284
const result = {
249285
value: {
250286
'lo\nl': 'ok\t'
@@ -255,10 +291,10 @@ describe('dag', () => {
255291

256292
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
257293

258-
expect(out).to.equal('{"lol":"ok"}\n')
294+
expect(out).to.equal('{"lo\\nl":"ok\\t"}')
259295
})
260296

261-
it('should strip control characters from dag-cbor string nodes', async () => {
297+
it('should not strip control characters from dag-cbor string nodes', async () => {
262298
const result = {
263299
value: 'lo\nl'
264300
}
@@ -267,10 +303,10 @@ describe('dag', () => {
267303

268304
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
269305

270-
expect(out).to.equal('"lol"\n')
306+
expect(out).to.equal('"lo\\nl"')
271307
})
272308

273-
it('should strip control characters from dag-cbor array nodes', async () => {
309+
it('should not strip control characters from dag-cbor array nodes', async () => {
274310
const result = {
275311
value: ['lo\nl']
276312
}
@@ -279,10 +315,10 @@ describe('dag', () => {
279315

280316
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
281317

282-
expect(out).to.equal('["lol"]\n')
318+
expect(out).to.equal('["lo\\nl"]')
283319
})
284320

285-
it('should strip control characters from dag-cbor nested array nodes', async () => {
321+
it('should not strip control characters from dag-cbor nested array nodes', async () => {
286322
const result = {
287323
value: {
288324
'lo\nl': ['ok\t']
@@ -293,7 +329,7 @@ describe('dag', () => {
293329

294330
const out = await cli(`dag get ${dagCborCid}`, { ipfs })
295331

296-
expect(out).to.equal('{"lol":["ok"]}\n')
332+
expect(out).to.equal('{"lo\\nl":["ok\\t"]}')
297333
})
298334
})
299335

0 commit comments

Comments
 (0)