-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: pin API #1045
Changes from all commits
0662cd8
ee813eb
f847f98
be8747e
beead9a
868570a
066c113
90dad57
17b81ea
753f618
92b3d2f
a5c556c
8f7de05
408d2bc
f77a379
e3013ee
6c29a3d
a1b9b93
10f40ca
a7a55b9
d0b4a0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use strict' | ||
|
||
module.exports = { | ||
command: 'pin', | ||
|
||
description: 'Pin and unpin objects to local storage.', | ||
|
||
builder (yargs) { | ||
return yargs | ||
.commandDir('pin') | ||
}, | ||
|
||
handler (argv) { | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
'use strict' | ||
|
||
const print = require('../../utils').print | ||
|
||
module.exports = { | ||
command: 'add <ipfsPath...>', | ||
|
||
describe: 'Pins object to local storage.', | ||
|
||
builder: { | ||
recursive: { | ||
type: 'boolean', | ||
alias: 'r', | ||
default: true, | ||
describe: 'Recursively pin the object linked to by the specified object(s).' | ||
} | ||
}, | ||
|
||
handler (argv) { | ||
const recursive = argv.recursive | ||
const type = recursive ? 'recursive' : 'direct' | ||
argv.ipfs.pin.add(argv.ipfsPath, { recursive: recursive }, (err, results) => { | ||
if (err) { throw err } | ||
results.forEach((res) => { | ||
print(`pinned ${res.hash} ${type}ly`) | ||
}) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
'use strict' | ||
|
||
const print = require('../../utils').print | ||
|
||
module.exports = { | ||
// bracket syntax with '...' tells yargs to optionally accept a list | ||
command: 'ls [ipfsPath...]', | ||
|
||
describe: 'List objects pinned to local storage.', | ||
|
||
builder: { | ||
type: { | ||
type: 'string', | ||
alias: 't', | ||
default: 'all', | ||
choices: ['direct', 'indirect', 'recursive', 'all'], | ||
describe: 'The type of pinned keys to list.' | ||
}, | ||
quiet: { | ||
type: 'boolean', | ||
alias: 'q', | ||
default: false, | ||
describe: 'Write just hashes of objects.' | ||
} | ||
}, | ||
|
||
handler: (argv) => { | ||
const paths = argv.ipfsPath | ||
const type = argv.type | ||
const quiet = argv.quiet | ||
|
||
argv.ipfs.pin.ls(paths, { type }, (err, results) => { | ||
if (err) { throw err } | ||
results.forEach((res) => { | ||
let line = res.hash | ||
if (!quiet) { | ||
line += ` ${res.type}` | ||
} | ||
print(line) | ||
}) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
'use strict' | ||
|
||
const print = require('../../utils').print | ||
|
||
module.exports = { | ||
command: 'rm <ipfsPath...>', | ||
|
||
describe: 'Removes the pinned object from local storage.', | ||
|
||
builder: { | ||
recursive: { | ||
type: 'boolean', | ||
alias: 'r', | ||
default: true, | ||
describe: 'Recursively unpin the objects linked to by the specified object(s).' | ||
} | ||
}, | ||
|
||
handler: (argv) => { | ||
const recursive = argv.recursive | ||
argv.ipfs.pin.rm(argv.ipfsPath, { recursive: recursive }, (err, results) => { | ||
if (err) { throw err } | ||
results.forEach((res) => { | ||
print(`unpinned ${res.hash}`) | ||
}) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ | |
const promisify = require('promisify-es6') | ||
const CID = require('cids') | ||
const pull = require('pull-stream') | ||
const mapAsync = require('async/map') | ||
const flattenDeep = require('lodash.flattendeep') | ||
|
||
module.exports = function dag (self) { | ||
return { | ||
|
@@ -33,6 +35,12 @@ module.exports = function dag (self) { | |
} else { | ||
path = '/' | ||
} | ||
} else if (Buffer.isBuffer(cid)) { | ||
try { | ||
cid = new CID(cid) | ||
} catch (err) { | ||
return callback(err) | ||
} | ||
} | ||
|
||
self._ipld.get(cid, path, options, callback) | ||
|
@@ -73,6 +81,23 @@ module.exports = function dag (self) { | |
self._ipld.treeStream(cid, path, options), | ||
pull.collect(callback) | ||
) | ||
}), | ||
|
||
// TODO - use IPLD selectors once they are implemented | ||
_getRecursive: promisify((multihash, callback) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fine as a first pass, and it'll be great to replace with IPLD selectors when they come along, I'm just wondering, there's no concept of "seen" nodes here, meaning what we could get duplicates in the return value...would that cause issues, do we need to dedupe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. Good question, I'm not sure. My first thought is that yes we could hit a loop but when I look at I think the answer here also effects the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No because DAG (acyclic). |
||
// gets flat array of all DAGNodes in tree given by multihash | ||
|
||
self.dag.get(new CID(multihash), (err, res) => { | ||
if (err) { return callback(err) } | ||
|
||
mapAsync(res.value.links, (link, cb) => { | ||
self.dag._getRecursive(link.multihash, cb) | ||
}, (err, nodes) => { | ||
// console.log('nodes:', nodes) | ||
if (err) return callback(err) | ||
callback(null, flattenDeep([res.value, nodes])) | ||
}) | ||
}) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,9 @@ function prepareFile (self, opts, file, callback) { | |
} | ||
|
||
waterfall([ | ||
(cb) => opts.onlyHash ? cb(null, file) : self.object.get(file.multihash, opts, cb), | ||
(cb) => opts.onlyHash | ||
? cb(null, file) | ||
: self.object.get(file.multihash, opts, cb), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a note for the future, please avoid formatting changes for code you're not altering - it makes it harder and more time consuming for me to review as my attention is being drawn away from actual code changes/additions/removals. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, this was a rebase conflict that I resolved to the previous formatting. |
||
(node, cb) => { | ||
const b58Hash = cid.toBaseEncodedString() | ||
|
||
|
@@ -87,6 +89,19 @@ function normalizeContent (opts, content) { | |
}) | ||
} | ||
|
||
function pinFile (self, opts, file, cb) { | ||
// Pin a file if it is the root dir of a recursive add or the single file | ||
// of a direct add. | ||
const pin = 'pin' in opts ? opts.pin : true | ||
const isRootDir = !file.path.includes('/') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know the code to well. Is the path already normalized here? Does it work on Windows? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe so, this path is from |
||
const shouldPin = pin && isRootDir && !opts.onlyHash && !opts.hashAlg | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't pin if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this is related to #1373 (comment). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I don't think it'd work. IIRC, it's more like 'IPLD can't handle CIDv1' and anytime Example: A node is created and fetched with a I am happy to cb(err) but should we just revert the functionality? |
||
if (shouldPin) { | ||
return self.pin.add(file.hash, err => cb(err, file)) | ||
} else { | ||
cb(null, file) | ||
} | ||
} | ||
|
||
class AddHelper extends Duplex { | ||
constructor (pullStream, push, options) { | ||
super(Object.assign({ objectMode: true }, options)) | ||
|
@@ -130,7 +145,8 @@ module.exports = function files (self) { | |
} | ||
|
||
let total = 0 | ||
let prog = opts.progress || (() => {}) | ||
|
||
const prog = opts.progress || noop | ||
const progress = (bytes) => { | ||
total += bytes | ||
prog(total) | ||
|
@@ -141,7 +157,8 @@ module.exports = function files (self) { | |
pull.map(normalizeContent.bind(null, opts)), | ||
pull.flatten(), | ||
importer(self._ipld, opts), | ||
pull.asyncMap(prepareFile.bind(null, self, opts)) | ||
pull.asyncMap(prepareFile.bind(null, self, opts)), | ||
pull.asyncMap(pinFile.bind(null, self, opts)) | ||
) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,7 @@ | ||
'use strict' | ||
|
||
const path = require('path') | ||
const fs = require('fs') | ||
const glob = require('glob') | ||
const importer = require('ipfs-unixfs-engine').importer | ||
const pull = require('pull-stream') | ||
const file = require('pull-file') | ||
const CID = require('cids') | ||
|
@@ -15,23 +13,20 @@ module.exports = function addDefaultAssets (self, log, callback) { | |
|
||
pull( | ||
pull.values([initDocsPath]), | ||
pull.asyncMap((val, cb) => glob(path.join(val, '/**/*'), cb)), | ||
pull.asyncMap((val, cb) => | ||
glob(path.join(val, '/**/*'), { nodir: true }, cb) | ||
), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
pull.flatten(), | ||
pull.map((element) => { | ||
pull.map(element => { | ||
const addPath = element.substring(index + 1) | ||
|
||
if (fs.statSync(element).isDirectory()) { return } | ||
|
||
return { path: addPath, content: file(element) } | ||
}), | ||
// Filter out directories, which are undefined from above | ||
pull.filter(Boolean), | ||
importer(self._ipld), | ||
pull.through((el) => { | ||
if (el.path === 'init-docs') { | ||
const cid = new CID(el.multihash) | ||
self.files.addPullStream(), | ||
pull.through(file => { | ||
if (file.path === 'init-docs') { | ||
const cid = new CID(file.hash) | ||
log('to get started, enter:\n') | ||
log(`\t jsipfs files cat /ipfs/${cid.toBaseEncodedString()}/readme\n`) | ||
log(`\tjsipfs files cat /ipfs/${cid.toBaseEncodedString()}/readme\n`) | ||
} | ||
}), | ||
pull.collect((err) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,16 +85,18 @@ module.exports = function init (self) { | |
return cb(null, true) | ||
} | ||
|
||
self.log('adding assets') | ||
const tasks = [ | ||
// add empty unixfs dir object (go-ipfs assumes this exists) | ||
(cb) => self.object.new('unixfs-dir', cb) | ||
] | ||
|
||
if (typeof addDefaultAssets === 'function') { | ||
// addDefaultAssets is undefined on browsers. | ||
// See package.json browser config | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we don't add the default assets (initial docs) in the browser to reduce the js payload. |
||
tasks.push((cb) => addDefaultAssets(self, opts.log, cb)) | ||
} | ||
|
||
self.log('adding assets') | ||
parallel(tasks, (err) => { | ||
if (err) { | ||
cb(err) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JonKrone Is this dependency necessary? If yes, for what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used to generate a
Key
where we put pin records in the datastore: https://github.com/ipfs/js-ipfs/pull/1045/files#diff-d96275251fef125ec785643a9facfab4R27.You had previously brought this up in this PR or #107, though I can't seem to find it now. You had suggested that the datastore
put
,get
,..
operations cast strings toKeys
automatically. @dignifiedquire What do you think, is that workable?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be. Wanna go ahead and make that happen?