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

Commit

Permalink
Merge pull request #75 from ipfs/feature/http-api-config-replace
Browse files Browse the repository at this point in the history
Add /api/v0/config/replace endpoint
  • Loading branch information
daviddias committed Mar 4, 2016
2 parents 05f2549 + 6b0838b commit 015ab10
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 0 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"buffer-loader": "0.0.1",
"chai": "^3.4.1",
"expose-loader": "^0.7.1",
"form-data": "^1.0.0-rc3",
"fs-blob-store": "^5.2.1",
"idb-plus-blob-store": "^1.0.0",
"istanbul": "^0.4.1",
Expand All @@ -61,6 +62,7 @@
"pre-commit": "^1.1.2",
"rimraf": "^2.4.4",
"standard": "^5.4.1",
"stream-to-promise": "^1.1.0",
"transform-loader": "^0.2.3",
"webpack": "^2.0.7-beta"
},
Expand All @@ -73,6 +75,7 @@
"ipfs-api": "^2.13.1",
"ipfs-blocks": "^0.1.0",
"ipfs-merkle-dag": "^0.2.1",
"ipfs-multipart": "0.0.1",
"ipfs-repo": "^0.5.0",
"joi": "^8.0.2",
"lodash.get": "^4.0.0",
Expand Down
61 changes: 61 additions & 0 deletions src/http-api/resources/config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
'use strict'

const ipfs = require('./../index.js').ipfs
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')
const multipart = require('ipfs-multipart')

exports = module.exports

Expand Down Expand Up @@ -124,3 +127,61 @@ exports.show = (request, reply) => {
return reply(config)
})
}

exports.replace = {
// pre request handler that parses the args and returns `config` which is assigned to `request.pre.args`
parseArgs: (request, reply) => {
if (!request.payload) {
return reply({
Message: "Argument 'file' is required",
Code: 1123

}).code(400).takeover()
}

const parser = multipart.reqParser(request.payload)
let file

parser.on('file', (fileName, fileStream) => {
fileStream.on('data', (data) => {
file = data
})
})

parser.on('end', () => {
if (!file) {
return reply({
Message: "Argument 'file' is required",
Code: 1123

}).code(400).takeover()
}

try {
return reply({
config: JSON.parse(file.toString())
})
} catch (err) {
return reply({
Message: 'Failed to decode file as config: ' + err,
Code: 0
}).code(500).takeover()
}
})
},

// main route handler which is called after the above `parseArgs`, but only if the args were valid
handler: (request, reply) => {
return ipfs.config.replace(request.pre.args.config, (err) => {
if (err) {
log.error(err)
return reply({
Message: 'Failed to save config: ' + err,
Code: 0
}).code(500)
}

return reply()
})
}
}
15 changes: 15 additions & 0 deletions src/http-api/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,18 @@ api.route({
path: '/api/v0/config/show',
handler: resources.config.show
})

api.route({
method: '*',
path: '/api/v0/config/replace',
config: {
payload: {
parse: false,
output: 'stream'
},
pre: [
{ method: resources.config.replace.parseArgs, assign: 'args' }
],
handler: resources.config.replace.handler
}
})
3 changes: 3 additions & 0 deletions tests/badconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
bad config
}
70 changes: 70 additions & 0 deletions tests/otherconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"Identity": {
"PeerID": "QmQ2zigjQikYnyYUSXZydNXrDRhBut2mubwJBaLXobMt3A",
"PrivKey": "CAASpgkwggSiAgEAAoIBAQC2SKo/HMFZeBml1AF3XijzrxrfQXdJzjePBZAbdxqKR1Mc6juRHXij6HXYPjlAk01BhF1S3Ll4Lwi0cAHhggf457sMg55UWyeGKeUv0ucgvCpBwlR5cQ020i0MgzjPWOLWq1rtvSbNcAi2ZEVn6+Q2EcHo3wUvWRtLeKz+DZSZfw2PEDC+DGPJPl7f8g7zl56YymmmzH9liZLNrzg/qidokUv5u1pdGrcpLuPNeTODk0cqKB+OUbuKj9GShYECCEjaybJDl9276oalL9ghBtSeEv20kugatTvYy590wFlJkkvyl+nPxIH0EEYMKK9XRWlu9XYnoSfboiwcv8M3SlsjAgMBAAECggEAZtju/bcKvKFPz0mkHiaJcpycy9STKphorpCT83srBVQi59CdFU6Mj+aL/xt0kCPMVigJw8P3/YCEJ9J+rS8BsoWE+xWUEsJvtXoT7vzPHaAtM3ci1HZd302Mz1+GgS8Epdx+7F5p80XAFLDUnELzOzKftvWGZmWfSeDnslwVONkL/1VAzwKy7Ce6hk4SxRE7l2NE2OklSHOzCGU1f78ZzVYKSnS5Ag9YrGjOAmTOXDbKNKN/qIorAQ1bovzGoCwx3iGIatQKFOxyVCyO1PsJYT7JO+kZbhBWRRE+L7l+ppPER9bdLFxs1t5CrKc078h+wuUr05S1P1JjXk68pk3+kQKBgQDeK8AR11373Mzib6uzpjGzgNRMzdYNuExWjxyxAzz53NAR7zrPHvXvfIqjDScLJ4NcRO2TddhXAfZoOPVH5k4PJHKLBPKuXZpWlookCAyENY7+Pd55S8r+a+MusrMagYNljb5WbVTgN8cgdpim9lbbIFlpN6SZaVjLQL3J8TWH6wKBgQDSChzItkqWX11CNstJ9zJyUE20I7LrpyBJNgG1gtvz3ZMUQCn3PxxHtQzN9n1P0mSSYs+jBKPuoSyYLt1wwe10/lpgL4rkKWU3/m1Myt0tveJ9WcqHh6tzcAbb/fXpUFT/o4SWDimWkPkuCb+8j//2yiXk0a/T2f36zKMuZvujqQKBgC6B7BAQDG2H2B/ijofp12ejJU36nL98gAZyqOfpLJ+FeMz4TlBDQ+phIMhnHXA5UkdDapQ+zA3SrFk+6yGk9Vw4Hf46B+82SvOrSbmnMa+PYqKYIvUzR4gg34rL/7AhwnbEyD5hXq4dHwMNsIDq+l2elPjwm/U9V0gdAl2+r50HAoGALtsKqMvhv8HucAMBPrLikhXP/8um8mMKFMrzfqZ+otxfHzlhI0L08Bo3jQrb0Z7ByNY6M8epOmbCKADsbWcVre/AAY0ZkuSZK/CaOXNX/AhMKmKJh8qAOPRY02LIJRBCpfS4czEdnfUhYV/TYiFNnKRj57PPYZdTzUsxa/yVTmECgYBr7slQEjb5Onn5mZnGDh+72BxLNdgwBkhO0OCdpdISqk0F0Pxby22DFOKXZEpiyI9XYP1C8wPiJsShGm2yEwBPWXnrrZNWczaVuCbXHrZkWQogBDG3HGXNdU4MAWCyiYlyinIBpPpoAJZSzpGLmWbMWh28+RJS6AQX6KHrK1o2uw=="
},
"Datastore": {
"Type": "",
"Path": "",
"StorageMax": "",
"StorageGCWatermark": 0,
"GCPeriod": "",
"Params": null,
"NoSync": false
},
"Addresses": {
"Swarm": ["/ip4/0.0.0.0/tcp/4001", "/ip6/::/tcp/4001"],
"API": "/ip4/127.0.0.1/tcp/6001",
"Gateway": "/ip4/127.0.0.1/tcp/9090"
},
"Mounts": {
"IPFS": "/ipfs",
"IPNS": "/ipns",
"FuseAllowOther": false
},
"Version": {
"Current": "0.4.0-dev",
"Check": "error",
"CheckDate": "0001-01-01T00:00:00Z",
"CheckPeriod": "172800000000000",
"AutoUpdate": "minor"
},
"Discovery": {
"MDNS": {
"Enabled": true,
"Interval": 10
}
},
"Ipns": {
"RepublishPeriod": "",
"RecordLifetime": "",
"ResolveCacheSize": 128
},
"Bootstrap": ["/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", "/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", "/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", "/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx"],
"Tour": {
"Last": ""
},
"Gateway": {
"HTTPHeaders": null,
"RootRedirect": "",
"Writable": false
},
"SupernodeRouting": {
"Servers": ["/ip4/104.236.176.52/tcp/4002/ipfs/QmXdb7tWTxdFEQEFgWBqkuYSrZd3mXrC7HxkD4krGNYx2U", "/ip4/104.236.179.241/tcp/4002/ipfs/QmVRqViDByUxjUMoPnjurjKvZhaEMFDtK35FJXHAM4Lkj6", "/ip4/104.236.151.122/tcp/4002/ipfs/QmSZwGx8Tn8tmcM4PtDJaMeUQNRhNFdBLVGPzRiNaRJtFH", "/ip4/162.243.248.213/tcp/4002/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP", "/ip4/128.199.219.111/tcp/4002/ipfs/Qmb3brdCYmKG1ycwqCbo6LUwWxTuo3FisnJV2yir7oN92R", "/ip4/104.236.76.40/tcp/4002/ipfs/QmdRBCV8Cz2dGhoKLkD3YjPwVFECmqADQkx5ZteF2c6Fy4", "/ip4/178.62.158.247/tcp/4002/ipfs/QmUdiMPci7YoEUBkyFZAh2pAbjqcPr7LezyiPD2artLw3v", "/ip4/178.62.61.185/tcp/4002/ipfs/QmVw6fGNqBixZE4bewRLT2VXX7fAHUHs8JyidDiJ1P7RUN"]
},
"API": {
"HTTPHeaders": {
"Access-Control-Allow-Origin": [
"http://example.com"
]
}
},
"Swarm": {
"AddrFilters": null
},
"Log": {
"MaxSizeMB": 250,
"MaxBackups": 1,
"MaxAgeDays": 0
}
}
90 changes: 90 additions & 0 deletions tests/test-http-api/test-config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const fs = require('fs')
const APIctl = require('ipfs-api')
const FormData = require('form-data')
const streamToPromise = require('stream-to-promise')

describe('config', () => {
const configPath = process.cwd() + '/tests/repo-tests-run/config'
const originalConfigPath = process.cwd() + '/tests/repo-example/config'
const updatedConfig = () => JSON.parse(fs.readFileSync(configPath, 'utf8'))
const restoreConfig = () => fs.writeFileSync(configPath, fs.readFileSync(originalConfigPath, 'utf8'), 'utf8')

describe('api', () => {
var api
Expand Down Expand Up @@ -143,6 +148,69 @@ describe('config', () => {
done()
})
})

describe('/config/replace', () => {
it('returns 400 if no config is provided', (done) => {
const form = new FormData()
const headers = form.getHeaders()

streamToPromise(form).then(payload => {
api.inject({
method: 'POST',
url: '/api/v0/config/replace',
headers: headers,
payload: payload
}, res => {
expect(res.statusCode).to.equal(400)
done()
})
})
})

it('returns 500 if the config is invalid', (done) => {
const form = new FormData()
const filePath = 'tests/badconfig'
form.append('file', fs.createReadStream(filePath))
const headers = form.getHeaders()

streamToPromise(form).then(payload => {
api.inject({
method: 'POST',
url: '/api/v0/config/replace',
headers: headers,
payload: payload
}, res => {
expect(res.statusCode).to.equal(500)
done()
})
})
})

it('updates value', (done) => {
const form = new FormData()
const filePath = 'tests/otherconfig'
form.append('file', fs.createReadStream(filePath))
const headers = form.getHeaders()
const expectedConfig = JSON.parse(fs.readFileSync(filePath, 'utf8'))

streamToPromise(form).then(payload => {
api.inject({
method: 'POST',
url: '/api/v0/config/replace',
headers: headers,
payload: payload
}, res => {
expect(res.statusCode).to.equal(200)
expect(updatedConfig()).to.deep.equal(expectedConfig)
done()
})
})
})

after(() => {
restoreConfig()
})
})
})

describe('using js-ipfs-api', () => {
Expand Down Expand Up @@ -240,5 +308,27 @@ describe('config', () => {
done()
})
})

describe('ipfs.config.replace', () => {
it('returns error if the config is invalid', (done) => {
const filePath = 'tests/badconfig'

ctl.config.replace(filePath, (err) => {
expect(err).to.exist
done()
})
})

it('updates value', (done) => {
const filePath = 'tests/otherconfig'
const expectedConfig = JSON.parse(fs.readFileSync(filePath, 'utf8'))

ctl.config.replace(filePath, (err) => {
expect(err).not.to.exist
expect(expectedConfig).to.deep.equal(updatedConfig())
done()
})
})
})
})
})

0 comments on commit 015ab10

Please sign in to comment.