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

feat: jsipfs add --only-hash (#1233) #1266

Merged
merged 4 commits into from
Mar 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions src/cli/commands/files/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ module.exports = {
type: 'boolean',
default: false
},
'only-hash': {
alias: 'n',
type: 'boolean',
default: false,
describe: 'Only chunk and hash, do not write'
},
'enable-sharding-experiment': {
type: 'boolean',
default: false
Expand Down Expand Up @@ -180,9 +186,12 @@ module.exports = {
const index = inPath.lastIndexOf('/') + 1
const options = {
strategy: argv.trickle ? 'trickle' : 'balanced',
shardSplitThreshold: argv.enableShardingExperiment ? argv.shardSplitThreshold : Infinity,
'cid-version': argv['cid-version'],
'raw-leaves': argv['raw-leaves']
shardSplitThreshold: argv.enableShardingExperiment
? argv.shardSplitThreshold
: Infinity,
cidVersion: argv.cidVersion,
rawLeaves: argv.rawLeaves,
onlyHash: argv.onlyHash
}

// Temporary restriction on raw-leaves:
Expand All @@ -193,15 +202,15 @@ module.exports = {
// cid-version > 0 unless explicitly set to false.
//
// This retains feature parity without having to implement raw-leaves.
if (argv['cid-version'] > 0 && argv['raw-leaves'] !== false) {
if (options.cidVersion > 0 && options.rawLeaves !== false) {
throw new Error('Implied argument raw-leaves must be passed and set to false when cid-version is > 0')
}

if (argv['raw-leaves']) {
if (options.rawLeaves) {
throw new Error('Not implemented: raw-leaves')
}

if (argv.enableShardingExperiment && utils.isDaemonOn()) {
if (options.enableShardingExperiment && utils.isDaemonOn()) {
throw new Error('Error: Enabling the sharding experiment should be done on the daemon')
}
const ipfs = argv.ipfs
Expand Down Expand Up @@ -230,8 +239,7 @@ module.exports = {
}
}

const thing = (cb) => cb(null, ipfs.files.addPullStream(options))
thing(next)
next(null, ipfs.files.addPullStream(options))
}
], (err, addStream) => {
if (err) throw err
Expand Down
2 changes: 1 addition & 1 deletion src/core/components/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function prepareFile (self, opts, file, callback) {
opts = opts || {}

waterfall([
(cb) => self.object.get(file.multihash, cb),
(cb) => opts.onlyHash ? cb(null, file) : self.object.get(file.multihash, cb),
(node, cb) => {
let cid = new CID(node.multihash)

Expand Down
12 changes: 7 additions & 5 deletions src/http/api/resources/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,12 @@ exports.add = {
// cid-version > 0 unless explicitly set to false.
//
// This retains feature parity without having to implement raw-leaves.
'raw-leaves': Joi.any().when('cid-version', {
'raw-leaves': Joi.boolean().when('cid-version', {
is: 1,
then: Joi.boolean().valid(false).required(),
otherwise: Joi.boolean().valid(false)
})
}),
'only-hash': Joi.boolean()
})
// TODO: Necessary until validate "recursive", "stream-channels" etc.
.options({ allowUnknown: true })
Expand Down Expand Up @@ -203,9 +204,10 @@ exports.add = {
}

const options = {
'cid-version': request.query['cid-version'],
'raw-leaves': request.query['raw-leaves'],
progress: request.query['progress'] ? progressHandler : null
cidVersion: request.query['cid-version'],
rawLeaves: request.query['raw-leaves'],
progress: request.query.progress ? progressHandler : null,
onlyHash: request.query['only-hash']
}

const aborter = abortable()
Expand Down
3 changes: 2 additions & 1 deletion test/cli/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ describe('config', () => runOnAndOff((thing) => {
})

it('call config with no arguments', () => {
return ipfs.fail('config')
return ipfs('config')
.then(out => expect(out).to.include('bin.js config <key> [value]'))
})
})

Expand Down
30 changes: 30 additions & 0 deletions test/cli/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use strict'

const fs = require('fs')
const os = require('os')
const expect = require('chai').expect
const path = require('path')
const compareDir = require('dir-compare').compareSync
Expand Down Expand Up @@ -270,6 +271,35 @@ describe('files', () => runOnAndOff((thing) => {
})
})

it('add --only-hash outputs correct hash', function () {
return ipfs('files add --only-hash src/init-files/init-docs/readme')
.then(out =>
expect(out)
.to.eql('added QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB readme\n')
)
})

it('add --only-hash does not add a file to the datastore', function () {
this.timeout(30 * 1000)
this.slow(10 * 1000)
const content = String(Math.random() + Date.now())
const filepath = path.join(os.tmpdir(), `${content}.txt`)
fs.writeFileSync(filepath, content)

return ipfs(`files add --only-hash ${filepath}`)
.then(out => {
const hash = out.split(' ')[1]

// 'jsipfs object get <hash>' should timeout with the daemon on
// and should fail fast with the daemon off
return Promise.race([
ipfs.fail(`object get ${hash}`),
new Promise((resolve, reject) => setTimeout(resolve, 4000))
])
.then(() => fs.unlinkSync(filepath))
})
})

it('cat', function () {
this.timeout(30 * 1000)

Expand Down
49 changes: 49 additions & 0 deletions test/http-api/files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-env mocha */
/* eslint max-nested-callbacks: ["error", 8] */
'use strict'

const chai = require('chai')
const dirtyChai = require('dirty-chai')
const DaemonFactory = require('ipfsd-ctl')
const expect = chai.expect
chai.use(dirtyChai)

const df = DaemonFactory.create({ exec: 'src/cli/bin.js' })

describe('.files', () => {
let ipfs = null
let ipfsd = null
before(function (done) {
this.timeout(20 * 1000)
df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api
done()
})
})

after(function (done) {
this.timeout(10 * 1000)
ipfsd.stop(done)
})

describe('.add', function () {
it('performs a speculative add, --only-hash', () => {
const content = String(Math.random())

return ipfs.add(Buffer.from(content), { onlyHash: true })
.then(files => {
const getAttempt = ipfs.object.get(files[0].hash)
.then(() => {
throw new Error('Should not find an object for content added with --only-hash')
})

return Promise.race([
getAttempt,
new Promise((resolve, reject) => setTimeout(resolve, 4000))
])
})
})
})
})
19 changes: 16 additions & 3 deletions test/utils/ipfs-exec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,28 @@ module.exports = (repoPath, opts) => {
return res
}

/**
* Expect the command passed as @param arguments to fail.
* @return {Promise} Resolves if the command passed as @param arguments fails,
* rejects if it was successful.
*/
ipfs.fail = function ipfsFail () {
let args = Array.from(arguments)
let caught = false
if (args.length === 1) {
args = args[0].split(' ')
}

return exec(args).catch((err) => {
expect(err).to.exist()
})
return exec(args)
.catch(err => {
caught = true
expect(err).to.exist()
})
.then(() => {
if (!caught) {
throw new Error(`jsipfs expected to fail during command: jsipfs ${args.join(' ')}`)
}
})
}

return ipfs
Expand Down