diff --git a/package.json b/package.json index 276e4723e7..2af6d922b9 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "lint": "standard", "coverage": "istanbul cover --print both -- _mocha tests/test-core/index.js", + "coverage:http-api": "istanbul cover --print both -- _mocha tests/test-http-api/index.js", "test": "npm run test:node && npm run test:browser", "test:node": "npm run test:node:core && npm run test:node:http-api && npm run test:node:cli", "test:node:cli": "mocha tests/test-cli/index.js", diff --git a/src/http-api/resources/config.js b/src/http-api/resources/config.js index e69de29bb2..e451ce38ff 100644 --- a/src/http-api/resources/config.js +++ b/src/http-api/resources/config.js @@ -0,0 +1,111 @@ +const IPFS = require('../../ipfs-core') +const debug = require('debug') +const get = require('lodash.get') +const set = require('lodash.set') +const log = debug('http-api:config') +log.error = debug('http-api:config:error') + +exports = module.exports + +exports.getOrSet = { + parseArgs: (request, reply) => { + const parseValue = (args) => { + if (request.query.bool !== undefined) { + args.value = args.value === 'true' + } else if (request.query.json !== undefined) { + try { + args.value = JSON.parse(args.value) + } catch (err) { + log.error(err) + return reply({ + Message: 'failed to unmarshal json. ' + err, + Code: 0 + }).code(500).takeover() + } + } + + return reply(args) + } + + if (request.query.arg instanceof Array) { + return parseValue({ + key: request.query.arg[0], + value: request.query.arg[1] + }) + } + + if (request.params.key) { + return parseValue({ + key: request.params.key, + value: request.query.arg + }) + } + + if (!request.query.arg) { + return reply("Argument 'key' is required").code(400).takeover() + } + + return reply({ + key: request.query.arg + }) + }, + + handler: (request, reply) => { + const node = new IPFS() + const key = request.pre.args.key + const value = request.pre.args.value + + if (value === undefined) { + // Get the value of a given key + return node.config.show((err, config) => { + if (err) { + log.error(err) + return reply({ + Message: 'Failed to get config value: ' + err, + Code: 0 + }).code(500) + } + + const value = get(config, key) + if (value === undefined) { + return reply({ + Message: 'Failed to get config value: key has no attributes', + Code: 0 + }).code(500) + } + + return reply({ + Key: key, + Value: value + }) + }) + } else { + // Set the new value of a given key + node.config.show((err, originalConfig) => { + if (err) { + log.error(err) + return reply({ + Message: 'Failed to get config value: ' + err, + Code: 0 + }).code(500) + } + + const updatedConfig = set(originalConfig, key, value) + node.config.replace(updatedConfig, (err) => { + if (err) { + log.error(err) + return reply({ + Message: 'Failed to get config value: ' + err, + Code: 0 + }).code(500) + } + + return reply({ + Key: key, + Value: value + }) + }) + }) + } + } +} diff --git a/src/http-api/routes/config.js b/src/http-api/routes/config.js index 62be916cc4..10e0bfd70e 100644 --- a/src/http-api/routes/config.js +++ b/src/http-api/routes/config.js @@ -1,10 +1,13 @@ const api = require('./../index.js').server.select('API') const resources = require('./../resources') -// TODO - api.route({ - method: 'GET', - path: '/api/v0/config', - handler: resources.config + method: '*', + path: '/api/v0/config/{key?}', + config: { + pre: [ + { method: resources.config.getOrSet.parseArgs, assign: 'args' } + ], + handler: resources.config.getOrSet.handler + } }) diff --git a/src/http-api/routes/index.js b/src/http-api/routes/index.js index bde22e774b..c715b087c5 100644 --- a/src/http-api/routes/index.js +++ b/src/http-api/routes/index.js @@ -4,4 +4,4 @@ require('./bootstrap') // require('./block') // require('./object') // require('./repo') -// require('./config') +require('./config') diff --git a/tests/test-http-api/test-config.js b/tests/test-http-api/test-config.js index ebcfa6ed31..3d6a1b1a63 100644 --- a/tests/test-http-api/test-config.js +++ b/tests/test-http-api/test-config.js @@ -1,23 +1,225 @@ /* eslint-env mocha */ -// const expect = require('chai').expect -// const APIctl = require('ipfs-api') +const expect = require('chai').expect +const fs = require('fs') +const APIctl = require('ipfs-api') + +describe('config', () => { + const configPath = process.cwd() + '/tests/repo-tests-run/config' + const updatedConfig = () => JSON.parse(fs.readFileSync(configPath, 'utf8')) -describe('version', () => { describe('api', () => { - // TODO - }) + var api + + before('api', (done) => { + api = require('../../src/http-api').server.select('API') + done() + }) + + describe('/config', () => { + it('returns 400 for request without arguments', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config' + }, res => { + expect(res.statusCode).to.equal(400) + done() + }) + }) + + it('returns 500 for request with invalid argument', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=kitten' + }, res => { + expect(res.statusCode).to.equal(500) + expect(res.result.Code).to.equal(0) + expect(res.result.Message).to.be.a('string') + done() + }) + }) + + it('returns value for request with argument', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=API.HTTPHeaders' + }, res => { + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('API.HTTPHeaders') + expect(res.result.Value).to.equal(null) + done() + }) + }) + + it('returns value for request as subcommand', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config/API.HTTPHeaders' + }, res => { + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('API.HTTPHeaders') + expect(res.result.Value).to.equal(null) + done() + }) + }) + + it('updates value for request with both args', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=kitten' + }, res => { + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.equal('kitten') + expect(updatedConfig().Datastore.Path).to.equal('kitten') + + done() + }) + }) + + it('returns 500 value for request with both args and JSON flag with invalid JSON argument', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=kitten&json' + }, res => { + expect(res.statusCode).to.equal(500) + expect(res.result.Code).to.equal(0) + expect(res.result.Message).to.be.a('string') + + done() + }) + }) + + it('updates value for request with both args and JSON flag with valid JSON argument', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg={\"kitten\": true}&json' + }, res => { + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.deep.equal({ kitten: true }) + expect(updatedConfig().Datastore.Path).to.deep.equal({ kitten: true }) + + done() + }) + }) + + it('updates value for request with both args and bool flag and true argument', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=true&bool' + }, res => { + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.deep.equal(true) + expect(updatedConfig().Datastore.Path).to.deep.equal(true) + + done() + }) + }) + + it('updates value for request with both args and bool flag and false argument', (done) => { + api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=false&bool' + }, res => { + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.deep.equal(false) + expect(updatedConfig().Datastore.Path).to.deep.equal(false) - describe('gateway', () => { - // TODO + done() + }) + }) + }) }) describe('using js-ipfs-api', () => { -// var ctl + var ctl - it('start IPFS API ctl', (done) => { -// ctl = APIctl('/ip4/127.0.0.1/tcp/6001') + before('start IPFS API ctl', (done) => { + ctl = APIctl('/ip4/127.0.0.1/tcp/6001') done() }) + + describe('ipfs.config', () => { + it('returns error for request without arguments', (done) => { + ctl.config.get(null, (err, res) => { + expect(err).to.exist + + done() + }) + }) + + it('returns error for request with invalid argument', (done) => { + ctl.config.get('kittens', (err, res) => { + expect(err).to.exist + + done() + }) + }) + + it('returns value for request with argument', (done) => { + ctl.config.get('API.HTTPHeaders', (err, res) => { + expect(err).not.to.exist + expect(res.Key).to.equal('API.HTTPHeaders') + expect(res.Value).to.equal(null) + + done() + }) + }) + + it('updates value for request with both args', (done) => { + ctl.config.set('Datastore.Path', 'kitten', (err, res) => { + expect(err).not.to.exist + expect(res.Key).to.equal('Datastore.Path') + expect(res.Value).to.equal('kitten') + expect(updatedConfig().Datastore.Path).to.equal('kitten') + + done() + }) + }) + + it('returns error for request with both args and JSON flag with invalid JSON argument', (done) => { + ctl.config.set('Datastore.Path', 'kitten', { json: true }, (err, res) => { + expect(err).to.exist + + done() + }) + }) + + it('updates value for request with both args and JSON flag with valid JSON argument', (done) => { + ctl.config.set('Datastore.Path', JSON.stringify({ kitten: true }), { json: true }, (err, res) => { + expect(err).not.to.exist + expect(res.Key).to.equal('Datastore.Path') + expect(res.Value).to.deep.equal({ kitten: true }) + expect(updatedConfig().Datastore.Path).to.deep.equal({ kitten: true }) + + done() + }) + }) + + it('updates value for request with both args and bool flag and true argument', (done) => { + ctl.config.set('Datastore.Path', true, { bool: true }, (err, res) => { + expect(err).not.to.exist + expect(res.Key).to.equal('Datastore.Path') + expect(res.Value).to.deep.equal(true) + expect(updatedConfig().Datastore.Path).to.deep.equal(true) + + done() + }) + }) + + it('updates value for request with both args and bool flag and false argument', (done) => { + ctl.config.set('Datastore.Path', false, { bool: true }, (err, res) => { + expect(err).not.to.exist + expect(res.Key).to.equal('Datastore.Path') + expect(res.Value).to.deep.equal(false) + expect(updatedConfig().Datastore.Path).to.deep.equal(false) + + done() + }) + }) + }) }) })