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

Commit 598c1fc

Browse files
committed
Add /api/v0/config endpoint
1 parent dab845f commit 598c1fc

File tree

5 files changed

+329
-16
lines changed

5 files changed

+329
-16
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"scripts": {
1010
"lint": "standard",
1111
"coverage": "istanbul cover --print both -- _mocha tests/test-core/index.js",
12+
"coverage:http-api": "istanbul cover --print both -- _mocha tests/test-http-api/index.js",
1213
"test": "npm run test:node && npm run test:browser",
1314
"test:node": "npm run test:node:core && npm run test:node:http-api && npm run test:node:cli",
1415
"test:node:cli": "mocha tests/test-cli/index.js",

src/http-api/resources/config.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
const IPFS = require('../../ipfs-core')
2+
const debug = require('debug')
3+
const get = require('lodash.get')
4+
const set = require('lodash.set')
5+
const log = debug('http-api:config')
6+
log.error = debug('http-api:config:error')
7+
8+
exports = module.exports
9+
10+
exports.getOrSet = {
11+
parseArgs: (request, reply) => {
12+
const parseValue = (args) => {
13+
if (request.query.bool !== undefined) {
14+
args.value = args.value === 'true'
15+
} else if (request.query.json !== undefined) {
16+
try {
17+
args.value = JSON.parse(args.value)
18+
} catch (err) {
19+
log.error(err)
20+
return reply({
21+
Message: 'failed to unmarshal json. ' + err,
22+
Code: 0
23+
}).code(500).takeover()
24+
}
25+
}
26+
27+
return reply(args)
28+
}
29+
30+
if (request.query.arg instanceof Array) {
31+
return parseValue({
32+
key: request.query.arg[0],
33+
value: request.query.arg[1]
34+
})
35+
}
36+
37+
if (request.params.key) {
38+
return parseValue({
39+
key: request.params.key,
40+
value: request.query.arg
41+
})
42+
}
43+
44+
if (!request.query.arg) {
45+
return reply("Argument 'key' is required").code(400).takeover()
46+
}
47+
48+
return reply({
49+
key: request.query.arg
50+
})
51+
},
52+
53+
handler: (request, reply) => {
54+
const node = new IPFS()
55+
const key = request.pre.args.key
56+
const value = request.pre.args.value
57+
58+
if (value === undefined) {
59+
// Get the value of a given key
60+
return node.config.show((err, config) => {
61+
if (err) {
62+
log.error(err)
63+
return reply({
64+
Message: 'Failed to get config value: ' + err,
65+
Code: 0
66+
}).code(500)
67+
}
68+
69+
const value = get(config, key)
70+
if (value === undefined) {
71+
return reply({
72+
Message: 'Failed to get config value: key has no attributes',
73+
Code: 0
74+
}).code(500)
75+
}
76+
77+
return reply({
78+
Key: key,
79+
Value: value
80+
})
81+
})
82+
} else {
83+
// Set the new value of a given key
84+
node.config.show((err, originalConfig) => {
85+
if (err) {
86+
log.error(err)
87+
return reply({
88+
Message: 'Failed to get config value: ' + err,
89+
Code: 0
90+
}).code(500)
91+
}
92+
93+
const updatedConfig = set(originalConfig, key, value)
94+
node.config.replace(updatedConfig, (err) => {
95+
if (err) {
96+
log.error(err)
97+
return reply({
98+
Message: 'Failed to get config value: ' + err,
99+
Code: 0
100+
}).code(500)
101+
}
102+
103+
return reply({
104+
Key: key,
105+
Value: value
106+
})
107+
})
108+
})
109+
}
110+
}
111+
}

src/http-api/routes/config.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
const api = require('./../index.js').server.select('API')
22
const resources = require('./../resources')
33

4-
// TODO
5-
64
api.route({
7-
method: 'GET',
8-
path: '/api/v0/config',
9-
handler: resources.config
5+
method: ['GET', 'POST'],
6+
path: '/api/v0/config/{key?}',
7+
config: {
8+
pre: [
9+
{ method: resources.config.getOrSet.parseArgs, assign: 'args' }
10+
],
11+
handler: resources.config.getOrSet.handler
12+
}
1013
})

src/http-api/routes/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ require('./bootstrap')
44
// require('./block')
55
// require('./object')
66
// require('./repo')
7-
// require('./config')
7+
require('./config')

tests/test-http-api/test-config.js

Lines changed: 208 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,221 @@
11
/* eslint-env mocha */
22

3-
// const expect = require('chai').expect
4-
// const APIctl = require('ipfs-api')
3+
const expect = require('chai').expect
4+
const fs = require('fs')
5+
const APIctl = require('ipfs-api')
6+
7+
describe('config', () => {
8+
const configPath = process.cwd() + '/tests/repo-tests-run/config'
9+
const updatedConfig = () => JSON.parse(fs.readFileSync(configPath, 'utf8'))
510

6-
describe('version', () => {
711
describe('api', () => {
8-
// TODO
9-
})
12+
var api
13+
14+
before('api', (done) => {
15+
api = require('../../src/http-api').server.select('API')
16+
done()
17+
})
18+
19+
it('returns 400 for request without arguments', (done) => {
20+
api.inject({
21+
method: 'POST',
22+
url: '/api/v0/config'
23+
}, res => {
24+
expect(res.statusCode).to.equal(400)
25+
done()
26+
})
27+
})
28+
29+
it('returns 500 for request with invalid argument', (done) => {
30+
api.inject({
31+
method: 'POST',
32+
url: '/api/v0/config?arg=kitten'
33+
}, res => {
34+
expect(res.statusCode).to.equal(500)
35+
expect(res.result.Code).to.equal(0)
36+
expect(res.result.Message).to.be.a('string')
37+
done()
38+
})
39+
})
40+
41+
it('returns value for request with argument', (done) => {
42+
api.inject({
43+
method: 'POST',
44+
url: '/api/v0/config?arg=API.HTTPHeaders'
45+
}, res => {
46+
expect(res.statusCode).to.equal(200)
47+
expect(res.result.Key).to.equal('API.HTTPHeaders')
48+
expect(res.result.Value).to.equal(null)
49+
done()
50+
})
51+
})
52+
53+
it('returns value for request as subcommand', (done) => {
54+
api.inject({
55+
method: 'POST',
56+
url: '/api/v0/config/API.HTTPHeaders'
57+
}, res => {
58+
expect(res.statusCode).to.equal(200)
59+
expect(res.result.Key).to.equal('API.HTTPHeaders')
60+
expect(res.result.Value).to.equal(null)
61+
done()
62+
})
63+
})
64+
65+
it('updates value for request with both args', (done) => {
66+
api.inject({
67+
method: 'POST',
68+
url: '/api/v0/config?arg=Datastore.Path&arg=kitten'
69+
}, res => {
70+
expect(res.statusCode).to.equal(200)
71+
expect(res.result.Key).to.equal('Datastore.Path')
72+
expect(res.result.Value).to.equal('kitten')
73+
expect(updatedConfig().Datastore.Path).to.equal('kitten')
74+
75+
done()
76+
})
77+
})
78+
79+
it('returns 500 value for request with both args and JSON flag with invalid JSON argument', (done) => {
80+
api.inject({
81+
method: 'POST',
82+
url: '/api/v0/config?arg=Datastore.Path&arg=kitten&json'
83+
}, res => {
84+
expect(res.statusCode).to.equal(500)
85+
expect(res.result.Code).to.equal(0)
86+
expect(res.result.Message).to.be.a('string')
87+
88+
done()
89+
})
90+
})
91+
92+
it('updates value for request with both args and JSON flag with valid JSON argument', (done) => {
93+
api.inject({
94+
method: 'POST',
95+
url: '/api/v0/config?arg=Datastore.Path&arg={\"kitten\": true}&json'
96+
}, res => {
97+
expect(res.statusCode).to.equal(200)
98+
expect(res.result.Key).to.equal('Datastore.Path')
99+
expect(res.result.Value).to.deep.equal({ kitten: true })
100+
expect(updatedConfig().Datastore.Path).to.deep.equal({ kitten: true })
101+
102+
done()
103+
})
104+
})
105+
106+
it('updates value for request with both args and bool flag and true argument', (done) => {
107+
api.inject({
108+
method: 'POST',
109+
url: '/api/v0/config?arg=Datastore.Path&arg=true&bool'
110+
}, res => {
111+
expect(res.statusCode).to.equal(200)
112+
expect(res.result.Key).to.equal('Datastore.Path')
113+
expect(res.result.Value).to.deep.equal(true)
114+
expect(updatedConfig().Datastore.Path).to.deep.equal(true)
115+
116+
done()
117+
})
118+
})
119+
120+
it('updates value for request with both args and bool flag and false argument', (done) => {
121+
api.inject({
122+
method: 'POST',
123+
url: '/api/v0/config?arg=Datastore.Path&arg=false&bool'
124+
}, res => {
125+
expect(res.statusCode).to.equal(200)
126+
expect(res.result.Key).to.equal('Datastore.Path')
127+
expect(res.result.Value).to.deep.equal(false)
128+
expect(updatedConfig().Datastore.Path).to.deep.equal(false)
10129

11-
describe('gateway', () => {
12-
// TODO
130+
done()
131+
})
132+
})
13133
})
14134

15135
describe('using js-ipfs-api', () => {
16-
// var ctl
136+
var ctl
17137

18-
it('start IPFS API ctl', (done) => {
19-
// ctl = APIctl('/ip4/127.0.0.1/tcp/6001')
138+
before('start IPFS API ctl', (done) => {
139+
ctl = APIctl('/ip4/127.0.0.1/tcp/6001')
20140
done()
21141
})
142+
143+
it('returns error for request without arguments', (done) => {
144+
ctl.config.get(null, (err, res) => {
145+
expect(err).to.exist
146+
147+
done()
148+
})
149+
})
150+
151+
it('returns error for request with invalid argument', (done) => {
152+
ctl.config.get('kittens', (err, res) => {
153+
expect(err).to.exist
154+
155+
done()
156+
})
157+
})
158+
159+
it('returns value for request with argument', (done) => {
160+
ctl.config.get('API.HTTPHeaders', (err, res) => {
161+
expect(err).not.to.exist
162+
expect(res.Key).to.equal('API.HTTPHeaders')
163+
expect(res.Value).to.equal(null)
164+
165+
done()
166+
})
167+
})
168+
169+
it('updates value for request with both args', (done) => {
170+
ctl.config.set('Datastore.Path', 'kitten', (err, res) => {
171+
expect(err).not.to.exist
172+
expect(res.Key).to.equal('Datastore.Path')
173+
expect(res.Value).to.equal('kitten')
174+
expect(updatedConfig().Datastore.Path).to.equal('kitten')
175+
176+
done()
177+
})
178+
})
179+
180+
it('returns error for request with both args and JSON flag with invalid JSON argument', (done) => {
181+
ctl.config.set('Datastore.Path', 'kitten', { json: true }, (err, res) => {
182+
expect(err).to.exist
183+
184+
done()
185+
})
186+
})
187+
188+
it('updates value for request with both args and JSON flag with valid JSON argument', (done) => {
189+
ctl.config.set('Datastore.Path', JSON.stringify({ kitten: true }), { json: true }, (err, res) => {
190+
expect(err).not.to.exist
191+
expect(res.Key).to.equal('Datastore.Path')
192+
expect(res.Value).to.deep.equal({ kitten: true })
193+
expect(updatedConfig().Datastore.Path).to.deep.equal({ kitten: true })
194+
195+
done()
196+
})
197+
})
198+
199+
it('updates value for request with both args and bool flag and true argument', (done) => {
200+
ctl.config.set('Datastore.Path', true, { bool: true }, (err, res) => {
201+
expect(err).not.to.exist
202+
expect(res.Key).to.equal('Datastore.Path')
203+
expect(res.Value).to.deep.equal(true)
204+
expect(updatedConfig().Datastore.Path).to.deep.equal(true)
205+
206+
done()
207+
})
208+
})
209+
210+
it('updates value for request with both args and bool flag and false argument', (done) => {
211+
ctl.config.set('Datastore.Path', false, { bool: true }, (err, res) => {
212+
expect(err).not.to.exist
213+
expect(res.Key).to.equal('Datastore.Path')
214+
expect(res.Value).to.deep.equal(false)
215+
expect(updatedConfig().Datastore.Path).to.deep.equal(false)
216+
217+
done()
218+
})
219+
})
22220
})
23221
})

0 commit comments

Comments
 (0)