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

Commit bb795ee

Browse files
committed
Adds "files add" http route.
1 parent 217574f commit bb795ee

File tree

8 files changed

+259
-2
lines changed

8 files changed

+259
-2
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"lodash.get": "^4.3.0",
7979
"lodash.set": "^4.2.0",
8080
"multiaddr": "^2.0.0",
81+
"multihashes": "^0.2.2",
8182
"path-exists": "^3.0.0",
8283
"peer-book": "^0.1.1",
8384
"peer-id": "^0.6.6",
@@ -122,4 +123,4 @@
122123
"kumavis <[email protected]>",
123124
"nginnever <[email protected]>"
124125
]
125-
}
126+
}

src/http-api/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ exports = module.exports = function HttpApi (repo) {
1919

2020
this.start = (callback) => {
2121
if (typeof repo === 'string') {
22-
console.log('here')
2322
repo = new IPFSRepo(repo, {stores: fsbs})
2423
}
2524

src/http-api/resources/files.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict'
2+
3+
const multihash = require('multihashes')
4+
const ndjson = require('ndjson')
5+
const multipart = require('ipfs-multipart')
6+
const debug = require('debug')
7+
const log = debug('http-api:files')
8+
log.error = debug('http-api:files:error')
9+
10+
exports = module.exports
11+
12+
exports.add = {
13+
handler: (request, reply) => {
14+
if (!request.payload) {
15+
return reply('Array, Buffer, or String is required.').code(400).takeover()
16+
}
17+
18+
const parser = multipart.reqParser(request.payload)
19+
20+
var filesParsed = false
21+
var filesAdded = 0
22+
23+
var serialize = ndjson.serialize()
24+
// hapi doesn't permit object streams: http://hapijs.com/api#replyerr-result
25+
serialize._readableState.objectMode = false
26+
27+
var fileAdder = request.server.app.ipfs.files.add()
28+
29+
fileAdder.on('data', (file) => {
30+
serialize.write({
31+
Name: file.path,
32+
Hash: multihash.toB58String(file.multihash)
33+
})
34+
filesAdded++
35+
})
36+
37+
fileAdder.on('end', () => {
38+
if (filesAdded === 0 && filesParsed) {
39+
return reply({
40+
Message: 'Failed to add files.',
41+
Code: 0
42+
}).code(500)
43+
} else {
44+
serialize.end()
45+
return reply(serialize)
46+
.header('x-chunked-output', '1')
47+
.header('content-type', 'application/json')
48+
}
49+
})
50+
51+
parser.on('file', (fileName, fileStream) => {
52+
var filePair = {
53+
path: fileName,
54+
stream: fileStream
55+
}
56+
filesParsed = true
57+
fileAdder.write(filePair)
58+
})
59+
60+
parser.on('end', () => {
61+
if (!filesParsed) {
62+
return reply("File argument 'data' is required.").code(400).takeover()
63+
}
64+
fileAdder.end()
65+
})
66+
}
67+
}

src/http-api/resources/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ exports.config = require('./config')
99
exports.block = require('./block')
1010
exports.swarm = require('./swarm')
1111
exports.bitswap = require('./bitswap')
12+
exports.files = require('./files')

src/http-api/routes/files.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict'
2+
3+
const resources = require('./../resources')
4+
5+
module.exports = (server) => {
6+
const api = server.select('API')
7+
8+
api.route({
9+
method: '*',
10+
path: '/api/v0/add',
11+
config: {
12+
payload: {
13+
parse: false,
14+
output: 'stream'
15+
},
16+
handler: resources.files.add.handler
17+
}
18+
})
19+
}

src/http-api/routes/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = (server) => {
88
require('./object')(server)
99
// require('./repo')(server)
1010
require('./config')(server)
11+
require('./files')(server)
1112
require('./swarm')(server)
1213
require('./bitswap')(server)
1314
}

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

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const expect = require('chai').expect
5+
const APIctl = require('ipfs-api')
6+
const fs = require('fs')
7+
const FormData = require('form-data')
8+
const streamToPromise = require('stream-to-promise')
9+
const Readable = require('stream').Readable
10+
const http = require('http')
11+
12+
function singleFileServer (filename) {
13+
return http.createServer(function (req, res) {
14+
fs.createReadStream(filename).pipe(res)
15+
})
16+
}
17+
18+
module.exports = (httpAPI) => {
19+
describe('files', () => {
20+
describe('api', () => {
21+
let api
22+
23+
it('api', () => {
24+
api = httpAPI.server.select('API')
25+
})
26+
27+
describe('/files/add', () => {
28+
it('returns 400 if no tuple is provided', (done) => {
29+
const form = new FormData()
30+
const headers = form.getHeaders()
31+
32+
streamToPromise(form).then((payload) => {
33+
api.inject({
34+
method: 'POST',
35+
url: '/api/v0/add',
36+
headers: headers,
37+
payload: payload
38+
}, (res) => {
39+
expect(res.statusCode).to.equal(400)
40+
done()
41+
})
42+
})
43+
})
44+
45+
it('adds a file', (done) => {
46+
const form = new FormData()
47+
const filePath = 'test/test-data/node.json'
48+
form.append('file', fs.createReadStream(filePath))
49+
const headers = form.getHeaders()
50+
51+
streamToPromise(form).then((payload) => {
52+
api.inject({
53+
method: 'POST',
54+
url: '/api/v0/add',
55+
headers: headers,
56+
payload: payload
57+
}, (res) => {
58+
expect(res.statusCode).to.equal(200)
59+
var result = JSON.parse(res.result)
60+
expect(result.Name).to.equal('node.json')
61+
expect(result.Hash).to.equal('QmRRdjTN2PjyEPrW73GBxJNAZrstH5tCZzwHYFJpSTKkhe')
62+
done()
63+
})
64+
})
65+
})
66+
67+
it('adds multiple files', (done) => {
68+
const form = new FormData()
69+
const filePath = 'test/test-data/hello'
70+
const filePath2 = 'test/test-data/otherconfig'
71+
form.append('file', fs.createReadStream(filePath))
72+
form.append('file', fs.createReadStream(filePath2))
73+
const headers = form.getHeaders()
74+
75+
streamToPromise(form).then((payload) => {
76+
api.inject({
77+
method: 'POST',
78+
url: '/api/v0/add',
79+
headers: headers,
80+
payload: payload
81+
}, (res) => {
82+
expect(res.statusCode).to.equal(200)
83+
var results = res.result.split('\n').slice(0, -1).map(JSON.parse)
84+
expect(results[0].Name).to.equal('hello')
85+
expect(results[0].Hash).to.equal('QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o')
86+
expect(results[1].Name).to.equal('otherconfig')
87+
expect(results[1].Hash).to.equal('QmayedZNznnEbHtyfjeQvvt29opSLjYjLtLqwfwSWq28ds')
88+
done()
89+
})
90+
})
91+
})
92+
})
93+
})
94+
95+
describe('using js-ipfs-api', () => {
96+
var ctl
97+
98+
it('start IPFS API ctl', (done) => {
99+
ctl = APIctl('/ip4/127.0.0.1/tcp/6001')
100+
done()
101+
})
102+
103+
describe('ipfs.add', () => {
104+
it('adds two files under a chunk Size', (done) => {
105+
const rs = new Readable()
106+
const rs2 = new Readable()
107+
var files = []
108+
const buffered = fs.readFileSync('test/test-data/hello')
109+
const buffered2 = fs.readFileSync('test/test-data/otherconfig')
110+
rs.push(buffered)
111+
rs.push(null)
112+
rs2.push(buffered2)
113+
rs2.push(null)
114+
const filePair = {path: 'hello', content: rs}
115+
const filePair2 = {path: 'otherconfig', content: rs2}
116+
files.push(filePair)
117+
files.push(filePair2)
118+
119+
ctl.add(files, (err, res) => {
120+
expect(err).to.not.exist
121+
expect(res[0].Name).to.equal('hello')
122+
expect(res[0].Hash).to.equal('QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o')
123+
expect(res[1].Name).to.equal('otherconfig')
124+
expect(res[1].Hash).to.equal('QmayedZNznnEbHtyfjeQvvt29opSLjYjLtLqwfwSWq28ds')
125+
done()
126+
})
127+
})
128+
129+
it('adds a large file > a chunk', (done) => {
130+
const rs = new Readable()
131+
var files = []
132+
const buffered = fs.readFileSync('test/test-data/1.2MiB.txt')
133+
rs.push(buffered)
134+
rs.push(null)
135+
const filePair = {path: '1.2MiB.txt', content: rs}
136+
files.push(filePair)
137+
138+
ctl.add(filePair, (err, res) => {
139+
expect(err).to.not.exist
140+
expect(res[0].Name).to.equal('1.2MiB.txt')
141+
expect(res[0].Hash).to.equal('QmW7BDxEbGqxxSYVtn3peNPQgdDXbWkoQ6J1EFYAEuQV3Q')
142+
done()
143+
})
144+
})
145+
146+
it('adds a buffer', (done) => {
147+
const buffer = new Buffer('hello world')
148+
ctl.add(buffer, (err, res) => {
149+
expect(err).to.not.exist
150+
expect(res[0].Hash).to.equal('Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD')
151+
done()
152+
})
153+
})
154+
155+
it('adds a url', (done) => {
156+
var server = singleFileServer('test/test-data/1.2MiB.txt')
157+
server.listen(2913, function () {
158+
ctl.add('http://localhost:2913/', (err, res) => {
159+
expect(err).to.not.exist
160+
const added = res[0] != null ? res[0] : res
161+
expect(added).to.have.a.property('Hash', 'QmW7BDxEbGqxxSYVtn3peNPQgdDXbWkoQ6J1EFYAEuQV3Q')
162+
done()
163+
})
164+
})
165+
})
166+
})
167+
})
168+
})
169+
}

test/test-data/1.2MiB.txt

1.2 MB
Binary file not shown.

0 commit comments

Comments
 (0)