diff --git a/package.json b/package.json index 83cfca88d7..3b7d491167 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "ipfs-merkle-dag": "^0.6.0", "ipfs-multipart": "^0.1.0", "ipfs-repo": "^0.8.0", - "ipfs-unixfs-engine": "^0.6.1", + "ipfs-unixfs-engine": "^0.8.0", "joi": "^8.0.5", "libp2p-ipfs": "^0.3.8", "libp2p-swarm": "^0.12.11", @@ -122,4 +122,4 @@ "kumavis ", "nginnever " ] -} \ No newline at end of file +} diff --git a/src/cli/commands/cat.js b/src/cli/commands/cat.js deleted file mode 100644 index a18480b818..0000000000 --- a/src/cli/commands/cat.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const Command = require('ronin').Command - -module.exports = Command.extend({ - desc: '', - - run: function () {} -}) diff --git a/src/cli/commands/files/cat.js b/src/cli/commands/files/cat.js index fe61d61316..c517b8ad3e 100644 --- a/src/cli/commands/files/cat.js +++ b/src/cli/commands/files/cat.js @@ -22,15 +22,22 @@ module.exports = Command.extend({ if (err) { throw err } + if (utils.isDaemonOn()) { + ipfs.cat(path, (err, res) => { + if (err) { + throw err + } + console.log(res.toString()) + }) + return + } ipfs.files.cat(path, (err, res) => { if (err) { throw (err) } - if (res) { - res.on('file', (data) => { - data.stream.pipe(process.stdout) - }) - } + res.on('data', (data) => { + data.stream.pipe(process.stdout) + }) }) }) } diff --git a/src/cli/commands/files/get.js b/src/cli/commands/files/get.js index 31e8d17ad8..3640cf1617 100644 --- a/src/cli/commands/files/get.js +++ b/src/cli/commands/files/get.js @@ -84,7 +84,7 @@ module.exports = Command.extend({ if (err) { throw err } - result.on('file', fileHandler(result, dir)) + result.on('data', fileHandler(result, dir)) }) }) } diff --git a/src/cli/commands/get.js b/src/cli/commands/get.js deleted file mode 100644 index a46afd1faa..0000000000 --- a/src/cli/commands/get.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const Command = require('ronin').Command - -module.exports = Command.extend({ - desc: '', - - run: () => {} -}) diff --git a/src/core/ipfs/files.js b/src/core/ipfs/files.js index 07e7da4ad4..4b4dcf8c34 100644 --- a/src/core/ipfs/files.js +++ b/src/core/ipfs/files.js @@ -44,8 +44,8 @@ module.exports = function files (self) { if (data.type === 'directory') { callback('This dag node is a directory', null) } else { - const exportEvent = Exporter(hash, self._dagS) - callback(null, exportEvent) + const exportStream = Exporter(hash, self._dagS) + callback(null, exportStream) } }) }, diff --git a/src/http-api/resources/files.js b/src/http-api/resources/files.js new file mode 100644 index 0000000000..8647a8e314 --- /dev/null +++ b/src/http-api/resources/files.js @@ -0,0 +1,50 @@ +'use strict' + +const bs58 = require('bs58') +const debug = require('debug') +const log = debug('http-api:files') +log.error = debug('http-api:files:error') + +exports = module.exports + +// common pre request handler that parses the args and returns `key` which is assigned to `request.pre.args` +exports.parseKey = (request, reply) => { + if (!request.query.arg) { + return reply("Argument 'key' is required").code(400).takeover() + } + + try { + return reply({ + key: new Buffer(bs58.decode(request.query.arg)) + }) + } catch (err) { + log.error(err) + return reply({ + Message: 'invalid ipfs ref path', + Code: 0 + }).code(500).takeover() + } +} + +exports.cat = { + // uses common parseKey method that returns a `key` + parseArgs: exports.parseKey, + + // main route handler which is called after the above `parseArgs`, but only if the args were valid + handler: (request, reply) => { + const key = request.pre.args.key + + request.server.app.ipfs.files.cat(key, (err, stream) => { + if (err) { + log.error(err) + return reply({ + Message: 'Failed to cat file: ' + err, + Code: 0 + }).code(500) + } + stream.on('data', (data) => { + return reply(data.stream) + }) + }) + } +} diff --git a/src/http-api/resources/index.js b/src/http-api/resources/index.js index 920d576aa9..b90a9d912c 100644 --- a/src/http-api/resources/index.js +++ b/src/http-api/resources/index.js @@ -9,3 +9,4 @@ exports.config = require('./config') exports.block = require('./block') exports.swarm = require('./swarm') exports.bitswap = require('./bitswap') +exports.files = require('./files') diff --git a/src/http-api/routes/files.js b/src/http-api/routes/files.js new file mode 100644 index 0000000000..99c47741e3 --- /dev/null +++ b/src/http-api/routes/files.js @@ -0,0 +1,18 @@ +'use strict' + +const resources = require('./../resources') + +module.exports = (server) => { + const api = server.select('API') + + api.route({ + method: '*', + path: '/api/v0/cat', + config: { + pre: [ + { method: resources.files.cat.parseArgs, assign: 'args' } + ], + handler: resources.files.cat.handler + } + }) +} diff --git a/src/http-api/routes/index.js b/src/http-api/routes/index.js index 6b21f38389..587f25de77 100644 --- a/src/http-api/routes/index.js +++ b/src/http-api/routes/index.js @@ -10,4 +10,5 @@ module.exports = (server) => { require('./config')(server) require('./swarm')(server) require('./bitswap')(server) + require('./files')(server) } diff --git a/test/cli-tests/test-commands.js b/test/cli-tests/test-commands.js index d1a2c31ca3..7234f283a3 100644 --- a/test/cli-tests/test-commands.js +++ b/test/cli-tests/test-commands.js @@ -10,7 +10,7 @@ describe('commands', () => { .run((err, stdout, exitcode) => { expect(err).to.not.exist expect(exitcode).to.equal(0) - expect(stdout.length).to.equal(50) + expect(stdout.length).to.equal(48) done() }) }) diff --git a/test/cli-tests/test-files.js b/test/cli-tests/test-files.js new file mode 100644 index 0000000000..512b088c9e --- /dev/null +++ b/test/cli-tests/test-files.js @@ -0,0 +1,52 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect +const nexpect = require('nexpect') +const HttpAPI = require('../../src/http-api') +const repoPath = require('./index').repoPath +const _ = require('lodash') + +describe('files', () => { + const env = _.clone(process.env) + env.IPFS_PATH = repoPath + + describe('api offline', () => { + it('cat', (done) => { + nexpect.spawn('node', [process.cwd() + '/src/cli/bin.js', 'files', 'cat', 'QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o'], {env}) + .run((err, stdout, exitcode) => { + expect(err).to.not.exist + expect(exitcode).to.equal(0) + done() + }) + }) + }) + + describe('api running', () => { + let httpAPI + + before((done) => { + httpAPI = new HttpAPI(repoPath) + httpAPI.start((err) => { + expect(err).to.not.exist + done() + }) + }) + + after((done) => { + httpAPI.stop((err) => { + expect(err).to.not.exist + done() + }) + }) + + it('cat', (done) => { + nexpect.spawn('node', [process.cwd() + '/src/cli/bin.js', 'files', 'cat', 'QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o'], {env}) + .run((err, stdout, exitcode) => { + expect(err).to.not.exist + expect(exitcode).to.equal(0) + done() + }) + }) + }) +}) diff --git a/test/core-tests/test-files.js b/test/core-tests/test-files.js index 4dd22d2777..3be2be1530 100644 --- a/test/core-tests/test-files.js +++ b/test/core-tests/test-files.js @@ -37,7 +37,7 @@ describe('files', () => { const hash = 'QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o' ipfs.files.cat(hash, (err, res) => { expect(err).to.not.exist - res.on('file', (data) => { + res.on('data', (data) => { data.stream.pipe(bl((err, bldata) => { expect(err).to.not.exist expect(bldata.toString()).to.equal('hello world\n') @@ -52,7 +52,7 @@ describe('files', () => { const hash = 'QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o' ipfs.files.get(hash, (err, res) => { expect(err).to.not.exist - res.on('file', (data) => { + res.on('data', (data) => { data.stream.pipe(bl((err, bldata) => { expect(err).to.not.exist expect(bldata.toString()).to.equal('hello world\n') diff --git a/test/http-api-tests/test-files.js b/test/http-api-tests/test-files.js new file mode 100644 index 0000000000..c41dda2862 --- /dev/null +++ b/test/http-api-tests/test-files.js @@ -0,0 +1,86 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect +const APIctl = require('ipfs-api') + +module.exports = (httpAPI) => { + describe('files', () => { + describe('api', () => { + let api + + it('api', () => { + api = httpAPI.server.select('API') + }) + + describe('/files/cat', () => { + it('returns 400 for request without argument', (done) => { + api.inject({ + method: 'GET', + url: '/api/v0/cat' + }, (res) => { + expect(res.statusCode).to.equal(400) + expect(res.result).to.be.a('string') + done() + }) + }) + + it('returns 500 for request with invalid argument', (done) => { + api.inject({ + method: 'GET', + url: '/api/v0/cat?arg=invalid' + }, (res) => { + expect(res.statusCode).to.equal(500) + expect(res.result.Message).to.be.a('string') + done() + }) + }) + + it('returns a buffer', (done) => { + api.inject({ + method: 'GET', + url: '/api/v0/cat?arg=QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o' + }, (res) => { + expect(res.statusCode).to.equal(200) + expect(res.rawPayload).to.deep.equal(new Buffer('hello world' + '\n')) + expect(res.payload).to.equal('hello world' + '\n') + done() + }) + }) + }) + }) + + describe('using js-ipfs-api', () => { + var ctl + + it('start IPFS API ctl', (done) => { + ctl = APIctl('/ip4/127.0.0.1/tcp/6001') + done() + }) + + describe('ipfs.cat', () => { + it('returns error for request without argument', (done) => { + ctl.cat(null, (err, result) => { + expect(err).to.exist + done() + }) + }) + + it('returns error for request with invalid argument', (done) => { + ctl.cat('invalid', (err, result) => { + expect(err).to.exist + done() + }) + }) + + it('returns a buffer', (done) => { + ctl.cat('QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o', (err, result) => { + expect(err).to.not.exist + expect(result).to.deep.equal(new Buffer('hello world' + '\n')) + done() + }) + }) + }) + }) + }) +}