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

Commit cf772c9

Browse files
committed
feat: implement ipfs dag import [path...]
1 parent de2ab6e commit cf772c9

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

packages/ipfs-cli/src/commands/dag/export.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const NO_LINKS_CODECS = [
3535
module.exports = {
3636
command: 'export <root cid>',
3737

38-
describe: 'Streams the DAG beginning at the given root CID as a .car stream on stdout.',
38+
describe: 'Streams the DAG beginning at the given root CID as a CAR stream on stdout.',
3939

4040
builder: {
4141
timeout: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use strict'
2+
3+
const fs = require('fs')
4+
const { CarBlockIterator } = require('@ipld/car/iterator')
5+
const Block = require('ipld-block')
6+
const LegacyCID = require('cids')
7+
const { default: parseDuration } = require('parse-duration')
8+
const { cidToString } = require('ipfs-core-utils/src/cid')
9+
10+
/**
11+
* @typedef {import('ipfs-core-types').IPFS} IPFS
12+
* @typedef {import('multiformats/cid').CID} CID
13+
* @typedef {[CID, boolean][]} RootsStatus
14+
*/
15+
16+
module.exports = {
17+
command: 'import [path...]',
18+
19+
describe: 'Import the contents of one or more CARs from files or stdin',
20+
21+
builder: {
22+
'pin-roots': {
23+
type: 'boolean',
24+
default: true,
25+
describe: 'Pin optional roots listed in the CAR headers after importing.'
26+
},
27+
timeout: {
28+
type: 'string',
29+
coerce: parseDuration
30+
}
31+
},
32+
33+
/**
34+
* @param {object} argv
35+
* @param {import('../../types').Context} argv.ctx
36+
* @param {string[]} argv.path
37+
* @param {boolean} argv.pinRoots
38+
* @param {number} argv.timeout
39+
*/
40+
async handler ({ ctx: { ipfs, print, getStdin }, path, pinRoots, timeout }) {
41+
let count = 0
42+
/** @type {RootsStatus} */
43+
let pinRootStatus = []
44+
if (path) { // files
45+
for await (const file of path) {
46+
print(`importing from ${file}...`)
47+
const { rootStatus, blockCount } = await importCar(ipfs, fs.createReadStream(file), timeout)
48+
pinRootStatus = pinRootStatus.concat(rootStatus)
49+
count += blockCount
50+
}
51+
} else { // stdin
52+
print('importing CAR from stdin...')
53+
const { rootStatus, blockCount } = await importCar(ipfs, getStdin(), timeout)
54+
pinRootStatus = pinRootStatus.concat(rootStatus)
55+
count += blockCount
56+
}
57+
58+
print(`imported ${count} blocks`)
59+
60+
if (pinRoots) {
61+
for (const [cid, status] of pinRootStatus) {
62+
if (!status) {
63+
print(`got malformed CAR, not pinning nonexistent root ${cid.toString()}`)
64+
}
65+
}
66+
const pinCids = pinRootStatus
67+
.filter(([_, status]) => status)
68+
.map(([cid]) => ({ cid: new LegacyCID(cid.bytes) }))
69+
for await (const cid of ipfs.pin.addAll(pinCids)) {
70+
print(`pinned root ${cidToString(cid)}`)
71+
}
72+
}
73+
}
74+
}
75+
76+
/**
77+
* @param {IPFS} ipfs
78+
* @param {AsyncIterable<Uint8Array>} inStream
79+
* @param {number} timeout
80+
* @returns {Promise<{rootStatus: RootsStatus, blockCount: number}>}
81+
*/
82+
async function importCar (ipfs, inStream, timeout) {
83+
const reader = await CarBlockIterator.fromIterable(inStream)
84+
// keep track of whether the root(s) exist within the CAR or not for later reporting & pinning
85+
/** @type {RootsStatus} */
86+
const rootStatus = (await reader.getRoots()).map((/** @type {CID} */ root) => [root, false])
87+
let blockCount = 0
88+
for await (const { cid, bytes } of reader) {
89+
rootStatus.forEach((rootStatus) => {
90+
if (!rootStatus[1] && cid.equals(rootStatus[0])) {
91+
rootStatus[1] = true // the root points to a CID in the CAR
92+
}
93+
})
94+
const block = new Block(bytes, new LegacyCID(cid.bytes))
95+
await ipfs.block.put(block, { timeout })
96+
blockCount++
97+
}
98+
return { rootStatus, blockCount }
99+
}

packages/ipfs-cli/test/commands.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
const { expect } = require('aegir/utils/chai')
55
const cli = require('./utils/cli')
66

7-
const commandCount = 111
7+
const commandCount = 112
88

99
describe('commands', () => {
1010
it('list the commands', async () => {

0 commit comments

Comments
 (0)