From 108285e43fa1ebfa83713204d4cb84e334b1a79a Mon Sep 17 00:00:00 2001 From: jkrone Date: Tue, 27 Feb 2018 11:46:21 -0500 Subject: [PATCH 01/10] initial sweep through to understand how pin works. Did make some changes but mostly minor. --- src/cli/commands/pin/add.js | 4 +- src/cli/commands/pin/ls.js | 17 +++-- src/cli/commands/pin/rm.js | 4 +- src/core/components/pin-set.js | 38 +++------- src/core/components/pin.js | 35 +++++----- src/core/components/pin.proto.js | 17 +++++ src/core/utils.js | 105 ++++++++++++++++------------ src/http/api/resources/pin.js | 115 ++++++++++++++++++------------- src/http/api/routes/pin.js | 11 ++- test/cli/pin.js | 14 ++-- 10 files changed, 205 insertions(+), 155 deletions(-) create mode 100644 src/core/components/pin.proto.js diff --git a/src/cli/commands/pin/add.js b/src/cli/commands/pin/add.js index 1c6ab15bcc..7b1a220aa5 100644 --- a/src/cli/commands/pin/add.js +++ b/src/cli/commands/pin/add.js @@ -1,5 +1,7 @@ 'use strict' +const print = require('../../utils').print + module.exports = { command: 'add ', @@ -21,7 +23,7 @@ module.exports = { argv.ipfs.pin.add(paths[0], { recursive: recursive }, (err, results) => { if (err) { throw err } results.forEach((res) => { - console.log(`pinned ${res.hash} ${type}ly`) + print(`pinned ${res.hash} ${type}ly`) }) }) } diff --git a/src/cli/commands/pin/ls.js b/src/cli/commands/pin/ls.js index 0702eba1b5..30e88e04bd 100644 --- a/src/cli/commands/pin/ls.js +++ b/src/cli/commands/pin/ls.js @@ -1,21 +1,20 @@ 'use strict' +const print = require('../../utils').print + module.exports = { - command: 'ls', + // bracket syntax with '...' tells yargs to accept a list and that it is optional + command: 'ls [ipfs-path...]', describe: 'List objects pinned to local storage.', builder: { - path: { - type: 'string', - describe: 'List pinned state of specific .' - }, type: { type: 'string', alias: 't', default: 'all', - describe: ('The type of pinned keys to list. ' + - 'Can be "direct", "indirect", "recursive", or "all".') + choices: ['direct', 'indirect', 'recursive', 'all'], + describe: 'The type of pinned keys to list.' }, quiet: { type: 'boolean', @@ -26,7 +25,7 @@ module.exports = { }, handler: (argv) => { - const paths = argv.path && argv.path.split(' ') + const paths = argv.ipfsPath || [] const type = argv.type const quiet = argv.quiet argv.ipfs.pin.ls(paths, { type: type }, (err, results) => { @@ -36,7 +35,7 @@ module.exports = { if (!quiet) { line += ` ${res.type}` } - console.log(line) + print(line) }) }) } diff --git a/src/cli/commands/pin/rm.js b/src/cli/commands/pin/rm.js index bef493aae3..f3d1e7cf7f 100644 --- a/src/cli/commands/pin/rm.js +++ b/src/cli/commands/pin/rm.js @@ -1,5 +1,7 @@ 'use strict' +const print = require('../../utils').print + module.exports = { command: 'rm ', @@ -20,7 +22,7 @@ module.exports = { argv.ipfs.pin.rm(paths, { recursive: recursive }, (err, results) => { if (err) { throw err } results.forEach((res) => { - console.log(`unpinned ${res.hash}`) + print(`unpinned ${res.hash}`) }) }) } diff --git a/src/core/components/pin-set.js b/src/core/components/pin-set.js index 4a3319dc94..a0ea499051 100644 --- a/src/core/components/pin-set.js +++ b/src/core/components/pin-set.js @@ -11,28 +11,14 @@ const DAGLink = dagPB.DAGLink const varint = require('varint') const once = require('once') +const pbSchema = require('./pin.proto') + const emptyKeyHash = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n' const emptyKey = multihashes.fromB58String(emptyKeyHash) const defaultFanout = 256 const maxItems = 8192 - -// Protobuf interface -// from go-ipfs/pin/internal/pb/header.proto -const pbSchema = ` - syntax = "proto2"; - - package ipfs.pin; - - option go_package = "pb"; - - message Set { - optional uint32 version = 1; - optional uint32 fanout = 2; - optional fixed32 seed = 3; - } -` - const pb = protobuf(pbSchema) + function readHeader (rootNode) { // rootNode.data should be a buffer of the format: // < varint(headerLength) | header | itemData... > @@ -63,7 +49,6 @@ exports = module.exports = function (dag) { const pinSet = { // should this be part of `object` API? hasChild: (root, childhash, callback, _links, _checked, _seen) => { - // callback (err, has) callback = once(callback) if (typeof childhash === 'object') { childhash = toB58String(childhash) @@ -81,11 +66,13 @@ exports = module.exports = function (dag) { if (bs58link === childhash) { return callback(null, true) } + + // don't check the same links twice + if (bs58link in _seen) { return } + _seen[bs58link] = true + dag.get(new CID(link.multihash), (err, res) => { if (err) { return callback(err) } - // don't check the same links twice - if (bs58link in _seen) { return } - _seen[bs58link] = true _checked++ _links += res.value.links.length @@ -95,7 +82,6 @@ exports = module.exports = function (dag) { }, storeSet: (keys, logInternalKey, callback) => { - // callback (err, rootNode) callback = once(callback) const items = keys.map((key) => { return { @@ -115,10 +101,8 @@ exports = module.exports = function (dag) { }, storeItems: (items, logInternalKey, callback, _depth, _subcalls, _done) => { - // callback (err, rootNode) callback = once(callback) - // const seed = crypto.randomBytes(4).readUInt32LE(0, true) // old nondeterministic behavior - const seed = _depth // new deterministic behavior + const seed = _depth const pbHeader = pb.Set.encode({ version: 1, fanout: defaultFanout, @@ -210,9 +194,8 @@ exports = module.exports = function (dag) { }, loadSet: (rootNode, name, logInternalKey, callback) => { - // callback (err, keys) callback = once(callback) - const link = rootNode.links.filter(l => l.name === name).pop() + const link = rootNode.links.find(l => l.name === name) if (!link) { return callback(new Error('No link found with name ' + name)) } logInternalKey(link.multihash) dag.get(new CID(link.multihash), (err, res) => { @@ -229,7 +212,6 @@ exports = module.exports = function (dag) { }, walkItems: (node, walkerFn, logInternalKey, callback) => { - // callback (err) callback = once(callback) const h = readHeader(node) if (h.err) { return callback(h.err) } diff --git a/src/core/components/pin.js b/src/core/components/pin.js index 7bb8b2173f..517cee45f5 100644 --- a/src/core/components/pin.js +++ b/src/core/components/pin.js @@ -8,7 +8,6 @@ const pinSet = require('./pin-set') const normalizeHashes = require('../utils').normalizeHashes const promisify = require('promisify-es6') const multihashes = require('multihashes') -const Key = require('interface-datastore').Key const each = require('async/each') const series = require('async/series') const waterfall = require('async/waterfall') @@ -22,7 +21,7 @@ module.exports = function pin (self) { let recursivePins = new Set() let internalPins = new Set() - const pinDataStoreKey = new Key('/local/pins') + const pinDataStoreKey = '/local/pins' const repo = self._repo const dag = self.dag @@ -50,7 +49,7 @@ module.exports = function pin (self) { options = null } callback = once(callback) - const recursive = !options || options.recursive !== false + const recursive = options ? options.recursive : true normalizeHashes(self, hashes, (err, mhs) => { if (err) { return callback(err) } // verify that each hash can be pinned @@ -63,6 +62,9 @@ module.exports = function pin (self) { } // entire graph of nested links should be pinned, // so make sure we have all the objects + // dag.tree(key, { recursive: true }, (err, res) => { + // console.log('dag.tree err,res:', err, res) + // }) dag._getRecursive(multihash, (err) => { if (err) { return cb(err) } // found all objects, we can add the pin @@ -122,16 +124,16 @@ module.exports = function pin (self) { pin.isPinnedWithType(multihash, pin.types.all, (err, res) => { if (err) { return cb(err) } const { pinned, reason } = res - if (!pinned) { return cb(new Error('not pinned')) } const key = toB58String(multihash) + if (!pinned) { + return cb(new Error(`${key} is not pinned`)) + } switch (reason) { case (pin.types.recursive): if (recursive) { return cb(null, key) } else { - return cb(new Error( - `${key} is pinned recursively` - )) + return cb(new Error(`${key} is pinned recursively`)) } case (pin.types.direct): return cb(null, key) @@ -173,12 +175,12 @@ module.exports = function pin (self) { type = options.type.toLowerCase() } callback = once(callback) - if (Object.keys(pin.types).indexOf(type) < 0) { + if (!pin.types[type]) { return callback(new Error( `Invalid type '${type}', must be one of {direct, indirect, recursive, all}` )) } - if (hashes) { + if (hashes && hashes.length) { // check the pinned state of specific hashes normalizeHashes(self, hashes, (err, mhs) => { if (err) { return callback(err) } @@ -206,10 +208,7 @@ module.exports = function pin (self) { }) } }) - }), (err, results) => { - if (err) { return callback(err) } - return callback(null, results) - }) + }), callback) }) } else { // show all pinned items of type @@ -269,6 +268,7 @@ module.exports = function pin (self) { if ((pinType === pin.types.direct)) { return callback(null, {pinned: false}) } + // internal if ((pinType === pin.types.internal || pinType === pin.types.all) && internalPins.has(key)) { return callback(null, {pinned: true, reason: pin.types.internal}) @@ -318,9 +318,6 @@ module.exports = function pin (self) { getIndirectKeys: (callback) => { const indirectKeys = new Set() const rKeys = pin.recursiveKeys() - if (!rKeys.length) { - return callback(null, []) - } each(rKeys, (multihash, cb) => { dag._getRecursive(multihash, (err, nodes) => { if (err) { return cb(err) } @@ -391,7 +388,7 @@ module.exports = function pin (self) { waterfall([ (cb) => repo.closed ? repo.datastore.open(cb) : cb(null, null), // hack for CLI tests (_, cb) => repo.datastore.has(pinDataStoreKey, cb), - (has, cb) => has ? cb() : callback(), + (has, cb) => has ? cb() : cb('No pins to load'), (cb) => repo.datastore.get(pinDataStoreKey, cb), (mh, cb) => dag.get(new CID(mh), cb), (root, cb) => handle.put('root', root.value, cb), @@ -399,7 +396,9 @@ module.exports = function pin (self) { (rKeys, cb) => handle.put('rKeys', rKeys, cb), (cb) => pin.set.loadSet(handle.root, pin.types.direct, logInternalKey, cb) ], (err, dKeys) => { - if (err && err !== 'break') { return callback(err) } + if (err && err !== 'break' && err !== 'No pins to load') { + return callback(err) + } if (dKeys) { directPins = new Set(dKeys.map(mh => toB58String(mh))) recursivePins = new Set(handle.rKeys.map(mh => toB58String(mh))) diff --git a/src/core/components/pin.proto.js b/src/core/components/pin.proto.js new file mode 100644 index 0000000000..abda1dcb4b --- /dev/null +++ b/src/core/components/pin.proto.js @@ -0,0 +1,17 @@ +/** + * Protobuf interface + * from go-ipfs/pin/internal/pb/header.proto + */ +module.exports.pbSchema = ` + syntax = "proto2"; + + package ipfs.pin; + + option go_package = "pb"; + + message Set { + optional uint32 version = 1; + optional uint32 fanout = 2; + optional fixed32 seed = 3; + } +` diff --git a/src/core/utils.js b/src/core/utils.js index e8a1f9371b..5011f59fec 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -6,15 +6,22 @@ const CID = require('cids') exports.OFFLINE_ERROR = 'This command must be run in online mode. Try running \'ipfs daemon\' first.' -const parseIpfsPath = exports.parseIpfsPath = function (pathString) { - // example: '/ipfs/b58Hash/links/by/name' - // -> { root: 'b58Hash', links: ['links', 'by', 'name'] } +/** + * Break an ipfs-path down into it's root hash and an array of links. + * + * example: + * /ipfs/b58Hash/links/by/name -> { root: 'b58Hash', links: ['links', 'by', 'name'] } + * + * @param {String} pathString An ipfs-path + * @return {Object} { root: base58 string, links: [string], ?err: Error } + */ +const parseIpfsPath = exports.parseIpfsPath = function parseIpfsPath (pathString) { const matched = pathString.match(/^(?:\/ipfs\/)?([^/]+(?:\/[^/]+)*)\/?$/) - const errorResult = () => ({ + const errorResult = { error: new Error('invalid ipfs ref path') - }) + } if (!matched) { - return errorResult() + return errorResult } const split = matched[1].split('/') const root = split[0] @@ -25,58 +32,66 @@ const parseIpfsPath = exports.parseIpfsPath = function (pathString) { links: split.slice(1, split.length) } } else { - return errorResult() + return errorResult } } catch (err) { - return errorResult() + return errorResult } } -exports.normalizeHashes = function (ipfs, hashes, callback) { - // try to accept a variety of hash options including - // multihash Buffers, base58 strings, and ipfs path - // strings, either individually or as an array - if (!Array.isArray(hashes)) { - hashes = [hashes] +/** + * Resolve various styles of an ipfs-path to the hash of the destination node after + * following any links described in the path. + * + * Handles formats: + * - + * - /link/to/another/planet + * - /ipfs/ + * - multihash Buffer + * - Buffers of any of the above + * + * @param {IPFS} ipfs the IPFS node + * @param {Described above} ipfsPaths A single or collection of ipfs-paths + * @param {Function} callback Node-style callback. res is Array + * @return {void} + */ +exports.normalizeHashes = function normalizeHashes (ipfs, ipfsPaths, callback) { + if (!Array.isArray(ipfsPaths)) { + ipfsPaths = [ipfsPaths] } - mapSeries(hashes, (hash, cb) => { + mapSeries(ipfsPaths, (path, cb) => { const validate = (mh) => { try { multihashes.validate(mh) cb(null, mh) } catch (err) { cb(err) } } - if (typeof hash === 'string') { - const {error, root, links} = parseIpfsPath(hash) - const rootHash = multihashes.fromB58String(root) - if (error) return cb(error) + if (typeof path !== 'string') { + return validate(path) + } + const {error, root, links} = parseIpfsPath(path) + const rootHash = multihashes.fromB58String(root) + if (error) return cb(error) + if (!links.length) { + return validate(rootHash) + } + // recursively follow named links to the target node + const pathFn = (err, obj) => { + if (err) { return cb(err) } if (!links.length) { - return validate(rootHash) - } else { - // recursively follow named links to the target - const pathFn = (err, obj) => { - if (err) { return cb(err) } - if (links.length) { - const linkName = links.shift() - const nextLink = obj.links.filter(link => link.name === linkName) - if (!nextLink.length) { - return cb(new Error( - `no link named ${linkName} under ${obj.toJSON().Hash}` - )) - } - const nextHash = nextLink[0].multihash - ipfs.object.get(nextHash, pathFn) - } else { - validate(obj.multihash) - } - } - ipfs.object.get(rootHash, pathFn) + // done tracing, we have the target node + return validate(obj.multihash) } - } else { - validate(hash) + const linkName = links.shift() + const nextLink = obj.links.find(link => link.name === linkName) + if (!nextLink) { + return cb(new Error( + `no link named ${linkName} under ${obj.toJSON().Hash}` + )) + } + const nextHash = nextLink.multihash + ipfs.object.get(nextHash, pathFn) } - }, (err, results) => { - if (err) { return callback(err) } - return callback(null, results) - }) + ipfs.object.get(rootHash, pathFn) + }, callback) } diff --git a/src/http/api/resources/pin.js b/src/http/api/resources/pin.js index c942cc1e11..2d56676755 100644 --- a/src/http/api/resources/pin.js +++ b/src/http/api/resources/pin.js @@ -7,48 +7,71 @@ log.error = debug('jsipfs:http-api:pin:error') exports = module.exports -exports.ls = (request, reply) => { - const ipfs = request.server.app.ipfs - const types = ipfs.pin.types - const path = request.query.arg - const type = request.query.type || types.all - ipfs.pin.ls(path, { type }, (err, result) => { - if (err) { - log.error(err) - return reply({ - Message: `Failed to list pins: ${err.message}`, - Code: 0 - }).code(500) - } +function parseArgs (request, reply) { + const query = request.query + if (!query.arg) { + return reply({ + Message: "Argument 'arg' is required", + Code: 0 + }).code(400).takeover() + } + + const recursive = query.recursive !== 'false' + + return reply({ + path: query.arg, + recursive: recursive, + }) +} + +exports.ls = { + parseArgs: (request, reply) => { + const ipfs = request.server.app.ipfs + const type = query.type || ipfs.pin.types.all return reply({ - Keys: _.mapValues( - _.keyBy(result, obj => obj.hash), - obj => ({Type: obj.type}) - ) + path: query.arg, + type: type }) - }) + }, + + handler: (request, reply) => { + const { path, type } = request.args + const ipfs = request.server.app.ipfs + ipfs.pin.ls([path], { type }, (err, result) => { + if (err) { + log.error(err) + return reply({ + Message: `Failed to list pins: ${err.message}`, + Code: 0 + }).code(500) + } + + return reply({ + Keys: _.mapValues( + _.keyBy(result, obj => obj.hash), + obj => ({Type: obj.type}) + ) + }) + }) + } } exports.add = { - // main route handler which is called after `parseArgs`, - // but only if the args were valid + parseArgs: parseArgs, + handler: (request, reply) => { const ipfs = request.server.app.ipfs - const path = request.query.arg - const recursive = request.query.recursive !== 'false' - const onError = (err, code) => { - log.error(err) - return reply({ - Message: `Failed to add pin: ${err.message}`, - Code: 0 - }).code(code) - } - if (!path) { - return onError(new Error("Argument 'ipfs-path' is required"), 400) - } + const { path, recursive } = request.args ipfs.pin.add(path, { recursive }, (err, result) => { - if (err) { return onError(err, 500) } + if (err) { + log.error(err) + return reply({ + Message: `Failed to add pin: ${err.message}`, + Code: 0 + }).code(500) + } + return reply({ Pins: result.map(obj => obj.hash) }) @@ -57,24 +80,20 @@ exports.add = { } exports.rm = { - // main route handler which is called after `parseArgs`, - // but only if the args were valid + parseArgs: parseArgs, + handler: (request, reply) => { const ipfs = request.server.app.ipfs - const path = request.query.arg - const recursive = request.query.recursive !== 'false' - const onError = (err, code) => { - log.error(err) - return reply({ - Message: `Failed to remove pin: ${err.message}`, - Code: 0 - }).code(code) - } - if (!path) { - return onError(new Error("Argument 'ipfs-path' is required"), 400) - } + const { path, recursive } = request.args ipfs.pin.rm(path, { recursive }, (err, result) => { - if (err) { return onError(err, 500) } + if (err) { + log.error(err) + return reply({ + Message: `Failed to remove pin: ${err.message}`, + Code: 0 + }).code(500) + } + return reply({ Pins: result.map(obj => obj.hash) }) diff --git a/src/http/api/routes/pin.js b/src/http/api/routes/pin.js index 2e3cf185b6..657bb375ac 100644 --- a/src/http/api/routes/pin.js +++ b/src/http/api/routes/pin.js @@ -9,6 +9,9 @@ module.exports = (server) => { method: '*', path: '/api/v0/pin/add', config: { + pre: [ + { method: resources.pin.add.parseArgs, assign: 'args' } + ], handler: resources.pin.add.handler } }) @@ -17,6 +20,9 @@ module.exports = (server) => { method: '*', path: '/api/v0/pin/rm', config: { + pre: [ + { method: resources.pin.rm.parseArgs, assign: 'args' } + ], handler: resources.pin.rm.handler } }) @@ -25,7 +31,10 @@ module.exports = (server) => { method: '*', path: '/api/v0/pin/ls', config: { - handler: resources.pin.ls + pre: [ + { method: resources.pin.ls.parseArgs, assign: 'args' } + ], + handler: resources.pin.ls.handler } }) } diff --git a/test/cli/pin.js b/test/cli/pin.js index 5506ea58b5..9d98dbc85a 100644 --- a/test/cli/pin.js +++ b/test/cli/pin.js @@ -17,7 +17,7 @@ const keys = { index: 'QmQN88TEidd3RY2u3dpib49fERTDfKtDpvxnvczATNsfKT' } -describe('pin', () => runOnAndOff((thing) => { +describe('pin', () => runOnAndOff.off((thing) => { const filesDir = 'test/fixtures/test-data/recursive-get-dir/init-docs' let ipfs @@ -48,23 +48,29 @@ describe('pin', () => runOnAndOff((thing) => { }) it('ls (recursive)', () => { - return ipfs(`pin ls --path ${keys.root}`).then((out) => { + return ipfs(`pin ls ${keys.root}`).then((out) => { expect(out).to.eql(`${keys.root} recursive\n`) }) }) it('ls (direct)', () => { - return ipfs(`pin ls --path ${keys.readme}`).then((out) => { + return ipfs(`pin ls ${keys.readme}`).then((out) => { expect(out).to.eql(`${keys.readme} direct\n`) }) }) it('ls (indirect)', () => { - return ipfs(`pin ls --path ${keys.index}`).then((out) => { + return ipfs(`pin ls ${keys.index}`).then((out) => { expect(out).to.eql(`${keys.index} indirect through ${keys.root}\n`) }) }) + it('ls with multiple keys', () => { + return ipfs(`pin ls ${keys.root} ${keys.readme}`).then((out) => { + expect(out).to.eql(`${keys.root} recursive\n${keys.readme} direct\n`) + }) + }) + it('ls (all)', () => { return ipfs('pin ls').then((out) => { expect(out.split('\n').length).to.eql(12) From 2061bacef5a3e7c72edec79a6ef7c1033f9aedcd Mon Sep 17 00:00:00 2001 From: jkrone Date: Thu, 1 Mar 2018 17:40:12 -0500 Subject: [PATCH 02/10] refactor pb schema to it's own file --- src/core/components/pin.proto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/components/pin.proto.js b/src/core/components/pin.proto.js index abda1dcb4b..db2391c91f 100644 --- a/src/core/components/pin.proto.js +++ b/src/core/components/pin.proto.js @@ -2,7 +2,7 @@ * Protobuf interface * from go-ipfs/pin/internal/pb/header.proto */ -module.exports.pbSchema = ` +module.exports = ` syntax = "proto2"; package ipfs.pin; From 9abb097dff6ecfdf62c0e224c60000cc690f9bb2 Mon Sep 17 00:00:00 2001 From: jonkrone Date: Fri, 2 Mar 2018 17:45:30 -0500 Subject: [PATCH 03/10] fix: don't pin files during files.add if opts.pin === false --- src/core/components/files.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/components/files.js b/src/core/components/files.js index 674f3c5335..d269e77bd8 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -18,6 +18,7 @@ const CID = require('cids') const toB58String = require('multihashes').toB58String function noop () {} +function identity (x) { return x } function prepareFile (self, opts, file, callback) { opts = opts || {} @@ -72,6 +73,7 @@ function normalizeContent (content) { }) } +// TODO: if !opts.recursive, be sure to only pin that one function pinFile (self, file, cb) { // since adding paths like `directory/filename` automatically // adds the directory as well as the file, we can just recursively @@ -125,7 +127,7 @@ module.exports = function files (self) { }, options) let total = 0 - let prog = opts.progress || (() => {}) + const prog = opts.progress || noop const progress = (bytes) => { total += bytes prog(total) @@ -137,7 +139,7 @@ module.exports = function files (self) { pull.flatten(), importer(self._ipld, opts), pull.asyncMap(prepareFile.bind(null, self, opts)), - pull.asyncMap(pinFile.bind(null, self)) + opts.pin ? pull.asyncMap(pinFile.bind(null, self)) : identity ) } From c8135b0b676449d6f20632de850b1108d05a50ea Mon Sep 17 00:00:00 2001 From: jonkrone Date: Fri, 2 Mar 2018 17:48:20 -0500 Subject: [PATCH 04/10] feat: add some http qs parsing, http route/resources cleanup, cleanup core/utils.parseIpfsPath --- src/core/utils.js | 22 ++++++++++------------ src/http/api/resources/files.js | 6 ++++-- src/http/api/resources/pin.js | 19 +++++++++---------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index 5011f59fec..6cfebc1153 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -15,7 +15,7 @@ exports.OFFLINE_ERROR = 'This command must be run in online mode. Try running \' * @param {String} pathString An ipfs-path * @return {Object} { root: base58 string, links: [string], ?err: Error } */ -const parseIpfsPath = exports.parseIpfsPath = function parseIpfsPath (pathString) { +exports.parseIpfsPath = function parseIpfsPath (pathString) { const matched = pathString.match(/^(?:\/ipfs\/)?([^/]+(?:\/[^/]+)*)\/?$/) const errorResult = { error: new Error('invalid ipfs ref path') @@ -23,16 +23,14 @@ const parseIpfsPath = exports.parseIpfsPath = function parseIpfsPath (pathString if (!matched) { return errorResult } - const split = matched[1].split('/') - const root = split[0] + + const [root, ...links] = matched[1].split('/') + try { - if (CID.isCID(new CID(root))) { - return { - root: root, - links: split.slice(1, split.length) - } - } else { - return errorResult + multihashes.fromB58String(root) + return { + root: root, + links: links } } catch (err) { return errorResult @@ -47,8 +45,8 @@ const parseIpfsPath = exports.parseIpfsPath = function parseIpfsPath (pathString * - * - /link/to/another/planet * - /ipfs/ - * - multihash Buffer * - Buffers of any of the above + * - multihash Buffer * * @param {IPFS} ipfs the IPFS node * @param {Described above} ipfsPaths A single or collection of ipfs-paths @@ -69,7 +67,7 @@ exports.normalizeHashes = function normalizeHashes (ipfs, ipfsPaths, callback) { if (typeof path !== 'string') { return validate(path) } - const {error, root, links} = parseIpfsPath(path) + const {error, root, links} = exports.parseIpfsPath(path) const rootHash = multihashes.fromB58String(root) if (error) return cb(error) if (!links.length) { diff --git a/src/http/api/resources/files.js b/src/http/api/resources/files.js index 55c406d86c..142bdbbee7 100644 --- a/src/http/api/resources/files.js +++ b/src/http/api/resources/files.js @@ -147,7 +147,8 @@ exports.add = { is: 1, then: Joi.boolean().valid(false).required(), otherwise: Joi.boolean().valid(false) - }) + }), + pin: Joi.boolean().default(true) }) // TODO: Necessary until validate "recursive", "stream-channels" etc. .options({ allowUnknown: true }) @@ -205,7 +206,8 @@ exports.add = { const options = { 'cid-version': request.query['cid-version'], 'raw-leaves': request.query['raw-leaves'], - progress: request.query['progress'] ? progressHandler : null + progress: request.query['progress'] ? progressHandler : null, + pin: request.query.pin } const aborter = abortable() diff --git a/src/http/api/resources/pin.js b/src/http/api/resources/pin.js index 2d56676755..a2c89ad29b 100644 --- a/src/http/api/resources/pin.js +++ b/src/http/api/resources/pin.js @@ -8,18 +8,17 @@ log.error = debug('jsipfs:http-api:pin:error') exports = module.exports function parseArgs (request, reply) { - const query = request.query - if (!query.arg) { + if (!request.query.arg) { return reply({ Message: "Argument 'arg' is required", Code: 0 }).code(400).takeover() } - const recursive = query.recursive !== 'false' + const recursive = request.query.recursive !== 'false' return reply({ - path: query.arg, + path: request.query.arg, recursive: recursive, }) } @@ -27,18 +26,18 @@ function parseArgs (request, reply) { exports.ls = { parseArgs: (request, reply) => { const ipfs = request.server.app.ipfs - const type = query.type || ipfs.pin.types.all + const type = request.query.type || ipfs.pin.types.all return reply({ - path: query.arg, + path: request.query.arg, type: type }) }, handler: (request, reply) => { - const { path, type } = request.args + const { path, type } = request.pre.args const ipfs = request.server.app.ipfs - ipfs.pin.ls([path], { type }, (err, result) => { + ipfs.pin.ls(path, { type }, (err, result) => { if (err) { log.error(err) return reply({ @@ -62,7 +61,7 @@ exports.add = { handler: (request, reply) => { const ipfs = request.server.app.ipfs - const { path, recursive } = request.args + const { path, recursive } = request.pre.args ipfs.pin.add(path, { recursive }, (err, result) => { if (err) { log.error(err) @@ -84,7 +83,7 @@ exports.rm = { handler: (request, reply) => { const ipfs = request.server.app.ipfs - const { path, recursive } = request.args + const { path, recursive } = request.pre.args ipfs.pin.rm(path, { recursive }, (err, result) => { if (err) { log.error(err) From efc2b401ebba91f5f7a654d305dad63a4240ec2a Mon Sep 17 00:00:00 2001 From: jonkrone Date: Fri, 2 Mar 2018 17:49:09 -0500 Subject: [PATCH 05/10] feat: expand pin tests. \nFirst draft. still needs some further work. --- src/core/components/pin.js | 2 +- test/core/utils.spec.js | 23 ++++++++++++++--------- test/http-api/inject/files.js | 14 +++++++++++++- test/http-api/spec/pin.js | 4 ++-- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/core/components/pin.js b/src/core/components/pin.js index 517cee45f5..33d6b9c384 100644 --- a/src/core/components/pin.js +++ b/src/core/components/pin.js @@ -180,7 +180,7 @@ module.exports = function pin (self) { `Invalid type '${type}', must be one of {direct, indirect, recursive, all}` )) } - if (hashes && hashes.length) { + if (hashes) { // check the pinned state of specific hashes normalizeHashes(self, hashes, (err, mhs) => { if (err) { return callback(err) } diff --git a/test/core/utils.spec.js b/test/core/utils.spec.js index df2141adfe..d58edcf3c7 100644 --- a/test/core/utils.spec.js +++ b/test/core/utils.spec.js @@ -7,21 +7,20 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) const multihashes = require('multihashes') -const utils = require('../../src/core/utils') // This gets replaced by `create-repo-browser.js` in the browser const createTempRepo = require('../utils/create-repo-nodejs.js') - const IPFS = require('../../src/core') +const utils = require('../../src/core/utils') -describe('utils', () => { - const rootHashString = 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU' - const rootHash = multihashes.fromB58String(rootHashString) - const rootPathString = `/ipfs/${rootHashString}/` - const aboutHashString = 'QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V' - const aboutHash = multihashes.fromB58String(aboutHashString) - const aboutPathString = `/ipfs/${rootHashString}/about` +const rootHashString = 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU' +const rootHash = multihashes.fromB58String(rootHashString) +const rootPathString = `/ipfs/${rootHashString}/` +const aboutHashString = 'QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V' +const aboutHash = multihashes.fromB58String(aboutHashString) +const aboutPathString = `/ipfs/${rootHashString}/about` +describe('utils', () => { describe('parseIpfsPath', () => { it('parses path with no links', function () { expect(utils.parseIpfsPath(rootHashString)) @@ -30,6 +29,7 @@ describe('utils', () => { links: [] }) }) + it('parses path with links', function () { expect(utils.parseIpfsPath(`${rootHashString}/docs/index`)) .to.deep.equal({ @@ -37,6 +37,7 @@ describe('utils', () => { links: ['docs', 'index'] }) }) + it('parses path with /ipfs/ prefix', function () { expect(utils.parseIpfsPath(`/ipfs/${rootHashString}/about`)) .to.deep.equal({ @@ -44,11 +45,13 @@ describe('utils', () => { links: ['about'] }) }) + it('returns error for malformed path', function () { const result = utils.parseIpfsPath(`${rootHashString}//about`) expect(result.error).to.be.instanceof(Error) .and.have.property('message', 'invalid ipfs ref path') }) + it('returns error if root is not a valid CID', function () { const result = utils.parseIpfsPath('invalid/ipfs/path') expect(result.error).to.be.instanceof(Error) @@ -129,5 +132,7 @@ describe('utils', () => { done() }) }) + + it('') }) }) diff --git a/test/http-api/inject/files.js b/test/http-api/inject/files.js index 9a22f6370c..e06a9f8d78 100644 --- a/test/http-api/inject/files.js +++ b/test/http-api/inject/files.js @@ -11,7 +11,19 @@ module.exports = (http) => { api = http.api.server.select('API') }) - describe('/add', () => {}) // TODO + describe('/add', () => { + + // it('files are pinned by default', function (done) { + // api.inject({ + // method: 'POST', + // url: '/api/v0/add?' + // }, (res) => { + // expect(res.statusCode).to.equal(400) + // expect(res.result.Message).to.be.a('string') + // done() + // }) + // }) + }) // TODO describe('/cat', () => { it('returns 400 for request without argument', (done) => { diff --git a/test/http-api/spec/pin.js b/test/http-api/spec/pin.js index 7016ce1110..1460f0f202 100644 --- a/test/http-api/spec/pin.js +++ b/test/http-api/spec/pin.js @@ -21,7 +21,7 @@ const keys = { } module.exports = (http) => { - describe('pin', () => { + describe.only('pin', () => { let api before((done) => { @@ -94,7 +94,7 @@ module.exports = (http) => { it('finds all pinned objects', (done) => { api.inject({ method: 'GET', - url: ('/api/v0/pin/ls') + url: '/api/v0/pin/ls' }, (res) => { expect(res.statusCode).to.equal(200) expect(res.result.Keys[keys.root].Type).to.equal('recursive') From 504f9fdf73d7ecbc0cdf9b6109bf81437b2c36d2 Mon Sep 17 00:00:00 2001 From: jonkrone Date: Sat, 3 Mar 2018 18:46:07 -0500 Subject: [PATCH 06/10] feat: Add logging for entry/exit of pins: add/rm/flush/load. Clean some documentation. --- src/cli/commands/pin/ls.js | 2 +- src/core/components/pin.js | 16 +++++++++------- src/core/utils.js | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/cli/commands/pin/ls.js b/src/cli/commands/pin/ls.js index 30e88e04bd..4f5cbdde09 100644 --- a/src/cli/commands/pin/ls.js +++ b/src/cli/commands/pin/ls.js @@ -3,7 +3,7 @@ const print = require('../../utils').print module.exports = { - // bracket syntax with '...' tells yargs to accept a list and that it is optional + // bracket syntax with '...' tells yargs to optionally accept a list command: 'ls [ipfs-path...]', describe: 'List objects pinned to local storage.', diff --git a/src/core/components/pin.js b/src/core/components/pin.js index 33d6b9c384..8d03da006e 100644 --- a/src/core/components/pin.js +++ b/src/core/components/pin.js @@ -14,7 +14,9 @@ const waterfall = require('async/waterfall') const until = require('async/until') const once = require('once') -const toB58String = multihashes.toB58String +function toB58String (hash) { + return new CID(hash).toBaseEncodedString() +} module.exports = function pin (self) { let directPins = new Set() @@ -62,9 +64,6 @@ module.exports = function pin (self) { } // entire graph of nested links should be pinned, // so make sure we have all the objects - // dag.tree(key, { recursive: true }, (err, res) => { - // console.log('dag.tree err,res:', err, res) - // }) dag._getRecursive(multihash, (err) => { if (err) { return cb(err) } // found all objects, we can add the pin @@ -73,9 +72,7 @@ module.exports = function pin (self) { } else { if (recursivePins.has(key)) { // recursive supersedes direct, can't have both - return cb( - new Error(`${key} already pinned recursively`) - ) + return cb(new Error(`${key} already pinned recursively`)) } if (directPins.has(key)) { // already directly pinned @@ -103,6 +100,7 @@ module.exports = function pin (self) { // persist updated pin sets to datastore pin.flush((err, root) => { if (err) { return callback(err) } + self.log(`Added pins: ${results}`) return callback(null, results.map(key => ({hash: key}))) }) }) @@ -151,6 +149,7 @@ module.exports = function pin (self) { // persist updated pin sets to datastore pin.flush((err, root) => { if (err) { return callback(err) } + self.log(`Removed pins: ${results}`) return callback(null, results.map(key => ({hash: key}))) }) }) @@ -371,6 +370,7 @@ module.exports = function pin (self) { (_, cb) => repo.datastore.put(pinDataStoreKey, handle.root.multihash, cb) ], (err, result) => { if (err) { return callback(err) } + self.log(`Flushed ${handle.root} to the datastore.`) internalPins = newInternalPins return callback(null, handle.root) }) @@ -397,6 +397,7 @@ module.exports = function pin (self) { (cb) => pin.set.loadSet(handle.root, pin.types.direct, logInternalKey, cb) ], (err, dKeys) => { if (err && err !== 'break' && err !== 'No pins to load') { + console.log('*!*!*!*!*!*! Error loading:\n', err) return callback(err) } if (dKeys) { @@ -405,6 +406,7 @@ module.exports = function pin (self) { logInternalKey(handle.root.multihash) internalPins = newInternalPins } + self.log('Loaded pins from the datastore') return callback() }) }) diff --git a/src/core/utils.js b/src/core/utils.js index 6cfebc1153..68299841cd 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -38,8 +38,8 @@ exports.parseIpfsPath = function parseIpfsPath (pathString) { } /** - * Resolve various styles of an ipfs-path to the hash of the destination node after - * following any links described in the path. + * Resolve various styles of an ipfs-path to the hash of the target node. + * Follows links in the path. * * Handles formats: * - From 46f6af58f47f3be719fb5b58d1ef0062baa7a321 Mon Sep 17 00:00:00 2001 From: jonkrone Date: Mon, 5 Mar 2018 21:29:43 -0500 Subject: [PATCH 07/10] feat: add --pin to files.add, fix: improper pin option parsing in core. --- src/cli/commands/files/add.js | 8 +++++++- src/cli/commands/pin/ls.js | 2 +- src/core/components/files.js | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/cli/commands/files/add.js b/src/cli/commands/files/add.js index 5886ac2365..a5c62087ae 100644 --- a/src/cli/commands/files/add.js +++ b/src/cli/commands/files/add.js @@ -172,6 +172,11 @@ module.exports = { type: 'boolean', default: false, describe: 'Write no output' + }, + pin: { + type: 'boolean', + default: true, + describe: 'Pin this object when adding' } }, @@ -182,7 +187,8 @@ module.exports = { strategy: argv.trickle ? 'trickle' : 'balanced', shardSplitThreshold: argv.enableShardingExperiment ? argv.shardSplitThreshold : Infinity, 'cid-version': argv['cid-version'], - 'raw-leaves': argv['raw-leaves'] + 'raw-leaves': argv['raw-leaves'], + pin: argv.pin } // Temporary restriction on raw-leaves: diff --git a/src/cli/commands/pin/ls.js b/src/cli/commands/pin/ls.js index 4f5cbdde09..869d684f73 100644 --- a/src/cli/commands/pin/ls.js +++ b/src/cli/commands/pin/ls.js @@ -25,7 +25,7 @@ module.exports = { }, handler: (argv) => { - const paths = argv.ipfsPath || [] + const paths = argv.ipfsPath || '' const type = argv.type const quiet = argv.quiet argv.ipfs.pin.ls(paths, { type: type }, (err, results) => { diff --git a/src/core/components/files.js b/src/core/components/files.js index d269e77bd8..26d89aed7c 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -127,6 +127,7 @@ module.exports = function files (self) { }, options) let total = 0 + const shouldPin = 'pin' in opts ? opts.pin : true const prog = opts.progress || noop const progress = (bytes) => { total += bytes @@ -139,7 +140,7 @@ module.exports = function files (self) { pull.flatten(), importer(self._ipld, opts), pull.asyncMap(prepareFile.bind(null, self, opts)), - opts.pin ? pull.asyncMap(pinFile.bind(null, self)) : identity + shouldPin ? pull.asyncMap(pinFile.bind(null, self)) : identity ) } From d32434ae259d0b88809dd943d6d67055c7d0736b Mon Sep 17 00:00:00 2001 From: jonkrone Date: Mon, 5 Mar 2018 21:32:52 -0500 Subject: [PATCH 08/10] feat: Use ipfs.files.add to add init-docs instead of directly using the unix-fs importer. --- src/core/components/init-assets.js | 21 +++++++++------------ src/core/components/init.js | 7 ++----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/core/components/init-assets.js b/src/core/components/init-assets.js index 00c37120a8..79e96a9fdf 100644 --- a/src/core/components/init-assets.js +++ b/src/core/components/init-assets.js @@ -15,23 +15,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) + ), 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) => { diff --git a/src/core/components/init.js b/src/core/components/init.js index 21504c9128..bdc9258087 100644 --- a/src/core/components/init.js +++ b/src/core/components/init.js @@ -87,13 +87,10 @@ module.exports = function init (self) { self.log('adding assets') const tasks = [ // add empty unixfs dir object (go-ipfs assumes this exists) - (cb) => self.object.new('unixfs-dir', cb) + (cb) => self.object.new('unixfs-dir', cb), + (cb) => addDefaultAssets(self, opts.log, cb) ] - if (typeof addDefaultAssets === 'function') { - tasks.push((cb) => addDefaultAssets(self, opts.log, cb)) - } - parallel(tasks, (err) => { if (err) { cb(err) From 943489b1d639a1d5e25cb28df24403fcfbd39e46 Mon Sep 17 00:00:00 2001 From: jonkrone Date: Tue, 6 Mar 2018 12:18:49 -0500 Subject: [PATCH 09/10] feat(tests): Add tests for cli --pin option. I know this should be more of an integration test. Should be written in /core. Maybe talk with Victor about testing different layers --- src/core/components/files.js | 1 - src/core/components/init.js | 6 ++---- src/core/components/pin.js | 3 +-- src/core/utils.js | 3 +-- test/cli/files.js | 25 +++++++++++++++++++++++++ test/cli/pin.js | 2 +- test/core/utils.spec.js | 16 +++++++--------- test/http-api/inject/files.js | 14 +------------- test/http-api/spec/pin.js | 2 +- 9 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/core/components/files.js b/src/core/components/files.js index 26d89aed7c..7b9a9b4ebc 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -73,7 +73,6 @@ function normalizeContent (content) { }) } -// TODO: if !opts.recursive, be sure to only pin that one function pinFile (self, file, cb) { // since adding paths like `directory/filename` automatically // adds the directory as well as the file, we can just recursively diff --git a/src/core/components/init.js b/src/core/components/init.js index bdc9258087..3ef13ff2b7 100644 --- a/src/core/components/init.js +++ b/src/core/components/init.js @@ -85,13 +85,11 @@ module.exports = function init (self) { } self.log('adding assets') - const tasks = [ + parallel([ // add empty unixfs dir object (go-ipfs assumes this exists) (cb) => self.object.new('unixfs-dir', cb), (cb) => addDefaultAssets(self, opts.log, cb) - ] - - parallel(tasks, (err) => { + ], (err) => { if (err) { cb(err) } else { diff --git a/src/core/components/pin.js b/src/core/components/pin.js index 8d03da006e..00959d24f0 100644 --- a/src/core/components/pin.js +++ b/src/core/components/pin.js @@ -396,8 +396,7 @@ module.exports = function pin (self) { (rKeys, cb) => handle.put('rKeys', rKeys, cb), (cb) => pin.set.loadSet(handle.root, pin.types.direct, logInternalKey, cb) ], (err, dKeys) => { - if (err && err !== 'break' && err !== 'No pins to load') { - console.log('*!*!*!*!*!*! Error loading:\n', err) + if (err && err !== 'No pins to load') { return callback(err) } if (dKeys) { diff --git a/src/core/utils.js b/src/core/utils.js index 68299841cd..9440df5a95 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -87,8 +87,7 @@ exports.normalizeHashes = function normalizeHashes (ipfs, ipfsPaths, callback) { `no link named ${linkName} under ${obj.toJSON().Hash}` )) } - const nextHash = nextLink.multihash - ipfs.object.get(nextHash, pathFn) + ipfs.object.get(nextLink.multihash, pathFn) } ipfs.object.get(rootHash, pathFn) }, callback) diff --git a/test/cli/files.js b/test/cli/files.js index b2d317abd3..95d2b5b31e 100644 --- a/test/cli/files.js +++ b/test/cli/files.js @@ -270,6 +270,31 @@ describe('files', () => runOnAndOff((thing) => { }) }) + it('add pins by default', function () { + const filePath = path.join(os.tmpdir(), hat()) + const content = String(Math.random()) + const file = fs.writeFileSync(filePath, content) + + return ipfs(`files add -Q ${filePath}`) + .then(out => { + const hash = out.trim() + return ipfs(`pin ls ${hash}`) + .then(ls => expect(ls).to.include(hash)) + }) + .then(() => fs.unlinkSync(filePath)) + }) + + it('add does not pin with --pin=false', function () { + const filePath = path.join(os.tmpdir(), hat()) + const content = String(Math.random()) + const file = fs.writeFileSync(filePath, content) + + return ipfs(`files add -Q --pin=false ${filePath}`) + .then(out => ipfs(`pin ls ${out.trim()}`)) + .then(ls => expect(ls.trim()).to.eql('')) + .then(() => fs.unlinkSync(filePath)) + }) + it('cat', function () { this.timeout(30 * 1000) diff --git a/test/cli/pin.js b/test/cli/pin.js index 9d98dbc85a..9271ffe410 100644 --- a/test/cli/pin.js +++ b/test/cli/pin.js @@ -17,7 +17,7 @@ const keys = { index: 'QmQN88TEidd3RY2u3dpib49fERTDfKtDpvxnvczATNsfKT' } -describe('pin', () => runOnAndOff.off((thing) => { +describe('pin', () => runOnAndOff((thing) => { const filesDir = 'test/fixtures/test-data/recursive-get-dir/init-docs' let ipfs diff --git a/test/core/utils.spec.js b/test/core/utils.spec.js index d58edcf3c7..638aebf837 100644 --- a/test/core/utils.spec.js +++ b/test/core/utils.spec.js @@ -13,14 +13,14 @@ const createTempRepo = require('../utils/create-repo-nodejs.js') const IPFS = require('../../src/core') const utils = require('../../src/core/utils') -const rootHashString = 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU' -const rootHash = multihashes.fromB58String(rootHashString) -const rootPathString = `/ipfs/${rootHashString}/` -const aboutHashString = 'QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V' -const aboutHash = multihashes.fromB58String(aboutHashString) -const aboutPathString = `/ipfs/${rootHashString}/about` - describe('utils', () => { + const rootHashString = 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU' + const rootHash = multihashes.fromB58String(rootHashString) + const rootPathString = `/ipfs/${rootHashString}/` + const aboutHashString = 'QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V' + const aboutHash = multihashes.fromB58String(aboutHashString) + const aboutPathString = `/ipfs/${rootHashString}/about` + describe('parseIpfsPath', () => { it('parses path with no links', function () { expect(utils.parseIpfsPath(rootHashString)) @@ -132,7 +132,5 @@ describe('utils', () => { done() }) }) - - it('') }) }) diff --git a/test/http-api/inject/files.js b/test/http-api/inject/files.js index e06a9f8d78..9a22f6370c 100644 --- a/test/http-api/inject/files.js +++ b/test/http-api/inject/files.js @@ -11,19 +11,7 @@ module.exports = (http) => { api = http.api.server.select('API') }) - describe('/add', () => { - - // it('files are pinned by default', function (done) { - // api.inject({ - // method: 'POST', - // url: '/api/v0/add?' - // }, (res) => { - // expect(res.statusCode).to.equal(400) - // expect(res.result.Message).to.be.a('string') - // done() - // }) - // }) - }) // TODO + describe('/add', () => {}) // TODO describe('/cat', () => { it('returns 400 for request without argument', (done) => { diff --git a/test/http-api/spec/pin.js b/test/http-api/spec/pin.js index 1460f0f202..f58ff3a10f 100644 --- a/test/http-api/spec/pin.js +++ b/test/http-api/spec/pin.js @@ -21,7 +21,7 @@ const keys = { } module.exports = (http) => { - describe.only('pin', () => { + describe('pin', () => { let api before((done) => { From 765e0365b5efc350db7e80636f271bb41f0cf74a Mon Sep 17 00:00:00 2001 From: jonkrone Date: Fri, 16 Mar 2018 12:56:11 -0400 Subject: [PATCH 10/10] feat: use isIPFS to valiate a multihash. --- package.json | 4 +--- src/core/utils.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index ba5bc0eb67..4e9ebfaeac 100644 --- a/package.json +++ b/package.json @@ -107,10 +107,8 @@ "hapi": "^16.6.2", "hapi-set-header": "^1.0.2", "hoek": "^5.0.3", - "interface-datastore": "^0.4.1", - "ipfs-api": "^18.0.0", - "ipfs-bitswap": "~0.19.0", "human-to-milliseconds": "^1.0.0", + "interface-datastore": "^0.4.1", "ipfs-api": "^18.1.1", "ipfs-bitswap": "~0.19.0", "ipfs-block": "~0.6.1", diff --git a/src/core/utils.js b/src/core/utils.js index 9440df5a95..be101273bb 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -3,20 +3,23 @@ const multihashes = require('multihashes') const mapSeries = require('async/mapSeries') const CID = require('cids') +const isIPFS = require('is-ipfs') exports.OFFLINE_ERROR = 'This command must be run in online mode. Try running \'ipfs daemon\' first.' /** * Break an ipfs-path down into it's root hash and an array of links. * - * example: + * examples: + * b58Hash -> { root: 'b58Hash', links: [] } + * b58Hash/mercury/venus -> { root: 'b58Hash', links: ['mercury', 'venus']} * /ipfs/b58Hash/links/by/name -> { root: 'b58Hash', links: ['links', 'by', 'name'] } * - * @param {String} pathString An ipfs-path + * @param {String} ipfsPath An ipfs-path * @return {Object} { root: base58 string, links: [string], ?err: Error } */ -exports.parseIpfsPath = function parseIpfsPath (pathString) { - const matched = pathString.match(/^(?:\/ipfs\/)?([^/]+(?:\/[^/]+)*)\/?$/) +exports.parseIpfsPath = function parseIpfsPath (ipfsPath) { + const matched = ipfsPath.match(/^(?:\/ipfs\/)?([^/]+(?:\/[^/]+)*)\/?$/) const errorResult = { error: new Error('invalid ipfs ref path') } @@ -26,13 +29,12 @@ exports.parseIpfsPath = function parseIpfsPath (pathString) { const [root, ...links] = matched[1].split('/') - try { - multihashes.fromB58String(root) + if (isIPFS.multihash(root)) { return { root: root, links: links } - } catch (err) { + } else { return errorResult } }