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

Commit 8759bf8

Browse files
authored
feat: add mssing dag put and dag resolve cli commands (#2521)
* feat: add mssing `dag put` and `dag resolve` cli commands Also allows passing input to `ipfs-exec` to simulate piping in cli tests. * chore: update interop dep * fix: do pinning inside core with gc lock * fix: wrap put in gc lock
1 parent 09041c3 commit 8759bf8

File tree

10 files changed

+407
-97
lines changed

10 files changed

+407
-97
lines changed

src/cli/commands/dag/put.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
'use strict'
2+
3+
const mh = require('multihashes')
4+
const multibase = require('multibase')
5+
const dagCBOR = require('ipld-dag-cbor')
6+
const dagPB = require('ipld-dag-pb')
7+
const { cidToString } = require('../../../utils/cid')
8+
9+
const inputDecoders = {
10+
json: (buf) => JSON.parse(buf.toString()),
11+
cbor: (buf) => dagCBOR.util.deserialize(buf),
12+
protobuf: (buf) => dagPB.util.deserialize(buf),
13+
raw: (buf) => buf
14+
}
15+
16+
const formats = {
17+
cbor: 'dag-cbor',
18+
raw: 'raw',
19+
protobuf: 'dag-pb',
20+
'dag-cbor': 'dag-cbor',
21+
'dag-pb': 'dag-pb'
22+
}
23+
24+
module.exports = {
25+
command: 'put [data]',
26+
27+
describe: 'accepts input from a file or stdin and parses it into an object of the specified format',
28+
29+
builder: {
30+
data: {
31+
type: 'string'
32+
},
33+
format: {
34+
type: 'string',
35+
alias: 'f',
36+
default: 'cbor',
37+
describe: 'Format that the object will be added as',
38+
choices: ['dag-cbor', 'dag-pb', 'raw', 'cbor', 'protobuf']
39+
},
40+
'input-encoding': {
41+
type: 'string',
42+
alias: 'input-enc',
43+
default: 'json',
44+
describe: 'Format that the input object will be',
45+
choices: ['json', 'cbor', 'raw', 'protobuf']
46+
},
47+
pin: {
48+
type: 'boolean',
49+
default: true,
50+
describe: 'Pin this object when adding'
51+
},
52+
'hash-alg': {
53+
type: 'string',
54+
alias: 'hash',
55+
default: 'sha2-256',
56+
describe: 'Hash function to use',
57+
choices: Object.keys(mh.names)
58+
},
59+
'cid-version': {
60+
type: 'integer',
61+
describe: 'CID version. Defaults to 0 unless an option that depends on CIDv1 is passed',
62+
default: 0
63+
},
64+
'cid-base': {
65+
describe: 'Number base to display CIDs in.',
66+
type: 'string',
67+
choices: multibase.names
68+
},
69+
preload: {
70+
type: 'boolean',
71+
default: true,
72+
describe: 'Preload this object when adding'
73+
},
74+
'only-hash': {
75+
type: 'boolean',
76+
default: false,
77+
describe: 'Only hash the content, do not write to the underlying block store'
78+
}
79+
},
80+
81+
handler ({ data, format, inputEncoding, pin, hashAlg, cidVersion, cidBase, preload, onlyHash, getIpfs, print, resolve }) {
82+
resolve((async () => {
83+
const ipfs = await getIpfs()
84+
85+
if (inputEncoding === 'cbor') {
86+
format = 'dag-cbor'
87+
} else if (inputEncoding === 'protobuf') {
88+
format = 'dag-pb'
89+
}
90+
91+
format = formats[format]
92+
93+
if (format !== 'dag-pb') {
94+
cidVersion = 1
95+
}
96+
97+
let source = data
98+
99+
if (!source) {
100+
// pipe from stdin
101+
source = Buffer.alloc(0)
102+
103+
for await (const buf of process.stdin) {
104+
source = Buffer.concat([source, buf])
105+
}
106+
} else {
107+
source = Buffer.from(source)
108+
}
109+
110+
source = inputDecoders[inputEncoding](source)
111+
112+
const cid = await ipfs.dag.put(source, {
113+
format,
114+
hashAlg,
115+
version: cidVersion,
116+
onlyHash,
117+
preload,
118+
pin
119+
})
120+
121+
print(cidToString(cid, { base: cidBase }))
122+
})())
123+
}
124+
}

src/cli/commands/dag/resolve.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict'
2+
3+
const CID = require('cids')
4+
5+
module.exports = {
6+
command: 'resolve <ref>',
7+
8+
describe: 'fetches a dag node from ipfs, prints its address and remaining path',
9+
10+
builder: {
11+
ref: {
12+
type: 'string'
13+
}
14+
},
15+
16+
handler ({ ref, getIpfs, print, resolve }) {
17+
resolve((async () => {
18+
const ipfs = await getIpfs()
19+
const options = {}
20+
21+
try {
22+
const result = await ipfs.dag.resolve(ref, options)
23+
let lastCid
24+
25+
for (const res of result) {
26+
if (CID.isCID(res.value)) {
27+
lastCid = res.value
28+
}
29+
}
30+
31+
if (!lastCid) {
32+
if (ref.startsWith('/ipfs/')) {
33+
ref = ref.substring(6)
34+
}
35+
36+
lastCid = ref.split('/').shift()
37+
}
38+
39+
print(lastCid.toString())
40+
} catch (err) {
41+
return print(`dag get resolve: ${err}`)
42+
}
43+
})())
44+
}
45+
}

src/core/components/dag.js

Lines changed: 75 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,50 @@ const all = require('async-iterator-all')
66
const errCode = require('err-code')
77
const multicodec = require('multicodec')
88

9+
function parseArgs (cid, path, options) {
10+
options = options || {}
11+
12+
// Allow options in path position
13+
if (path !== undefined && typeof path !== 'string') {
14+
options = path
15+
path = undefined
16+
}
17+
18+
if (typeof cid === 'string') {
19+
if (cid.startsWith('/ipfs/')) {
20+
cid = cid.substring(6)
21+
}
22+
23+
const split = cid.split('/')
24+
25+
try {
26+
cid = new CID(split[0])
27+
} catch (err) {
28+
throw errCode(err, 'ERR_INVALID_CID')
29+
}
30+
31+
split.shift()
32+
33+
if (split.length > 0) {
34+
path = split.join('/')
35+
} else {
36+
path = path || '/'
37+
}
38+
} else if (Buffer.isBuffer(cid)) {
39+
try {
40+
cid = new CID(cid)
41+
} catch (err) {
42+
throw errCode(err, 'ERR_INVALID_CID')
43+
}
44+
}
45+
46+
return [
47+
cid,
48+
path,
49+
options
50+
]
51+
}
52+
953
module.exports = function dag (self) {
1054
return {
1155
put: callbackify.variadic(async (dagNode, options) => {
@@ -44,50 +88,38 @@ module.exports = function dag (self) {
4488
}
4589
}
4690

47-
const cid = await self._ipld.put(dagNode, options.format, {
48-
hashAlg: options.hashAlg,
49-
cidVersion: options.version
50-
})
91+
let release
5192

52-
if (options.preload !== false) {
53-
self._preload(cid)
93+
if (options.pin) {
94+
release = await self._gcLock.readLock()
5495
}
5596

56-
return cid
57-
}),
58-
59-
get: callbackify.variadic(async (cid, path, options) => {
60-
options = options || {}
97+
try {
98+
const cid = await self._ipld.put(dagNode, options.format, {
99+
hashAlg: options.hashAlg,
100+
cidVersion: options.version
101+
})
61102

62-
// Allow options in path position
63-
if (path !== undefined && typeof path !== 'string') {
64-
options = path
65-
path = undefined
66-
}
67-
68-
if (typeof cid === 'string') {
69-
const split = cid.split('/')
70-
71-
try {
72-
cid = new CID(split[0])
73-
} catch (err) {
74-
throw errCode(err, 'ERR_INVALID_CID')
103+
if (options.pin) {
104+
await self.pin.add(cid, {
105+
lock: false
106+
})
75107
}
76108

77-
split.shift()
78-
79-
if (split.length > 0) {
80-
path = split.join('/')
81-
} else {
82-
path = path || '/'
109+
if (options.preload !== false) {
110+
self._preload(cid)
83111
}
84-
} else if (Buffer.isBuffer(cid)) {
85-
try {
86-
cid = new CID(cid)
87-
} catch (err) {
88-
throw errCode(err, 'ERR_INVALID_CID')
112+
113+
return cid
114+
} finally {
115+
if (release) {
116+
release()
89117
}
90118
}
119+
}),
120+
121+
get: callbackify.variadic(async (cid, path, options) => {
122+
[cid, path, options] = parseArgs(cid, path, options)
91123

92124
if (options.preload !== false) {
93125
self._preload(cid)
@@ -116,37 +148,23 @@ module.exports = function dag (self) {
116148
}),
117149

118150
tree: callbackify.variadic(async (cid, path, options) => { // eslint-disable-line require-await
119-
options = options || {}
151+
[cid, path, options] = parseArgs(cid, path, options)
120152

121-
// Allow options in path position
122-
if (path !== undefined && typeof path !== 'string') {
123-
options = path
124-
path = undefined
153+
if (options.preload !== false) {
154+
self._preload(cid)
125155
}
126156

127-
if (typeof cid === 'string') {
128-
const split = cid.split('/')
129-
130-
try {
131-
cid = new CID(split[0])
132-
} catch (err) {
133-
throw errCode(err, 'ERR_INVALID_CID')
134-
}
135-
136-
split.shift()
157+
return all(self._ipld.tree(cid, path, options))
158+
}),
137159

138-
if (split.length > 0) {
139-
path = split.join('/')
140-
} else {
141-
path = undefined
142-
}
143-
}
160+
resolve: callbackify.variadic(async (cid, path, options) => { // eslint-disable-line require-await
161+
[cid, path, options] = parseArgs(cid, path, options)
144162

145163
if (options.preload !== false) {
146164
self._preload(cid)
147165
}
148166

149-
return all(self._ipld.tree(cid, path, options))
167+
return all(self._ipld.resolve(cid, path))
150168
})
151169
}
152170
}

test/cli/commands.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
const { expect } = require('interface-ipfs-core/src/utils/mocha')
55
const runOnAndOff = require('../utils/on-and-off')
66

7-
const commandCount = 98
7+
const commandCount = 100
88
describe('commands', () => runOnAndOff((thing) => {
99
let ipfs
1010

test/cli/daemon.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ const daemonReady = (daemon) => {
2727
reject(new Error('Daemon didn\'t start ' + data.toString('utf8')))
2828
}
2929
})
30+
31+
daemon.catch(err => {
32+
reject(err)
33+
})
3034
})
3135
}
3236
const checkLock = (repo) => {
@@ -126,8 +130,8 @@ describe('daemon', () => {
126130
]
127131

128132
await ipfs('init')
129-
await ipfs('config', 'Addresses.API', JSON.stringify(apiAddrs), '--json')
130-
await ipfs('config', 'Addresses.Gateway', JSON.stringify(gatewayAddrs), '--json')
133+
await ipfs(`config Addresses.API ${JSON.stringify(apiAddrs)} --json`)
134+
await ipfs(`config Addresses.Gateway ${JSON.stringify(gatewayAddrs)} --json`)
131135

132136
const daemon = ipfs('daemon')
133137
let stdout = ''
@@ -154,8 +158,8 @@ describe('daemon', () => {
154158
this.timeout(100 * 1000)
155159

156160
await ipfs('init')
157-
await ipfs('config', 'Addresses.API', '[]', '--json')
158-
await ipfs('config', 'Addresses.Gateway', '[]', '--json')
161+
await ipfs('config Addresses.API [] --json')
162+
await ipfs('config Addresses.Gateway [] --json')
159163

160164
const daemon = ipfs('daemon')
161165
let stdout = ''

0 commit comments

Comments
 (0)