Skip to content
This repository has been archived by the owner on Mar 10, 2020. It is now read-only.

Commit

Permalink
Merge pull request #180 from ipfs/files-api-flat
Browse files Browse the repository at this point in the history
Working and passing
  • Loading branch information
daviddias committed Dec 28, 2015
2 parents 266ae6b + 0af2ccd commit dc87c99
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 98 deletions.
14 changes: 5 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
"description": "A client library for the IPFS API",
"main": "src/index.js",
"dependencies": {
"merge-stream": "^1.0.0",
"detect-node": "^2.0.3",
"flatmap": "0.0.3",
"glob": "^6.0.2",
"multiaddr": "^1.0.0",
"multipart-stream": "^2.0.0",
"multipart-stream": "github:dignifiedquire/multipart-stream#a1a4e9c4d41d1f78dcf32279affbbb0bfd930c28",
"ndjson": "^1.4.3",
"qs": "^6.0.0",
"require-dir": "^0.3.0",
"vinyl": "^1.1.0",
"vinyl-fs-browser": "^2.1.1-1",
"vinyl-multipart-stream": "^1.2.6",
"wreck": "^7.0.0"
},
"engines": {
Expand Down Expand Up @@ -58,13 +56,11 @@
"pre-commit": "^1.0.6",
"raw-loader": "^0.5.1",
"rimraf": "^2.4.3",
"run-sequence": "^1.1.4",
"run-sequence": "^1.1.5",
"semver": "^5.1.0",
"stream-equal": "^0.1.7",
"stream-http": "^2.0.2",
"uglify-js": "^2.4.24",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"webpack-stream": "^3.1.0"
},
"scripts": {
Expand Down
152 changes: 84 additions & 68 deletions src/get-files-stream.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,104 @@
'use strict'

const File = require('vinyl')
const vinylfs = require('vinyl-fs-browser')
const vmps = require('vinyl-multipart-stream')
const stream = require('stream')
const Merge = require('merge-stream')
const isNode = require('detect-node')
const Multipart = require('multipart-stream')
const flatmap = require('flatmap')

exports = module.exports = getFilesStream
function headers (file) {
const name = file.path || ''
const header = {
'Content-Disposition': `file; filename="${name}"`
}

function getFilesStream (files, opts) {
if (!files) return null
if (file.dir) {
header['Content-Type'] = 'application/x-directory'
} else {
header['Content-Type'] = 'application/octet-stream'
}

// merge all inputs into one stream
const adder = new Merge()
return header
}

// single stream for pushing directly
const single = new stream.PassThrough({objectMode: true})
adder.add(single)
function strip (name, base) {
const smallBase = base
.split('/')
.slice(0, -1)
.join('/') + '/'
return name.replace(smallBase, '')
}

for (let i = 0; i < files.length; i++) {
const file = files[i]
function loadPaths (opts, file) {
const path = require('path')
const fs = require('fs')
const glob = require('glob')

if (typeof (file) === 'string') {
const srcOpts = {
buffer: false,
stripBOM: false,
followSymlinks: opts.followSymlinks != null ? opts.followSymlinks : true
}
const followSymlinks = opts.followSymlinks != null ? opts.followSymlinks : true

// add the file or dir itself
adder.add(vinylfs.src(file, srcOpts))
file = path.resolve(file)
const stats = fs.statSync(file)

// if recursive, glob the contents
if (opts.recursive) {
adder.add(vinylfs.src(file + '/**/*', srcOpts))
}
} else {
// try to create a single vinyl file, and push it.
// throws if cannot use the file.
single.push(vinylFile(file))
}
if (stats.isDirectory() && !opts.recursive) {
throw new Error('Can only add directories using --recursive')
}

single.end()
return adder.pipe(vmps.flat())
}
if (stats.isDirectory() && opts.recursive) {
const mg = new glob.sync.GlobSync(`${file}/**/*`, {
follow: followSymlinks
})

// vinylFile tries to cast a file object to a vinyl file.
// it's agressive. If it _cannot_ be converted to a file,
// it returns null.
function vinylFile (file) {
if (file instanceof File) {
return file // it's a vinyl file.
return mg.found.map(name => {
if (mg.cache[name] === 'FILE') {
return {
path: strip(name, file),
dir: false,
content: fs.createReadStream(name)
}
} else {
return {
path: strip(name, file),
dir: true
}
}
})
}

// let's try to make a vinyl file?
const f = {cwd: '/', base: '/', path: ''}
if (file.contents && file.path) {
// set the cwd + base, if there.
f.path = file.path
f.cwd = file.cwd || f.cwd
f.base = file.base || f.base
f.contents = file.contents
} else {
// ok maybe we just have contents?
f.contents = file
return {
path: file,
content: fs.createReadStream(file)
}

// ensure the contents are safe to pass.
// throws if vinyl cannot use the contents
f.contents = vinylContentsSafe(f.contents)
return new File(f)
}

function vinylContentsSafe (c) {
if (Buffer.isBuffer(c)) return c
if (typeof (c) === 'string') return c
if (c instanceof stream.Stream) return c
if (typeof (c.pipe) === 'function') {
// hey, looks like a stream. but vinyl won't detect it.
// pipe it to a PassThrough, and use that
const s = new stream.PassThrough()
return c.pipe(s)
}
function getFilesStream (files, opts) {
if (!files) return null

const mp = new Multipart()

flatmap(files, file => {
if (typeof file === 'string') {
if (!isNode) {
throw new Error('Can not add paths in node')
}

return loadPaths(opts, file)
}

if (file.path && (file.content || file.dir)) {
return file
}

throw new Error('vinyl will not accept: ' + c)
return {
path: '',
dir: false,
content: file
}
}).forEach(file => {
mp.addPart({
headers: headers(file),
body: file.content
})
})

return mp
}

exports = module.exports = getFilesStream
5 changes: 1 addition & 4 deletions src/load-commands.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
'use strict'

const isNode = !global.window

function requireCommands () {
if (isNode) return require('require-dir')('./api')

return {
add: require('./api/add'),
block: require('./api/block'),
Expand All @@ -14,6 +10,7 @@ function requireCommands () {
dht: require('./api/dht'),
diag: require('./api/diag'),
id: require('./api/id'),
files: require('./api/files'),
log: require('./api/log'),
ls: require('./api/ls'),
mount: require('./api/mount'),
Expand Down
2 changes: 1 addition & 1 deletion src/request-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const Qs = require('qs')
const ndjson = require('ndjson')
const getFilesStream = require('./get-files-stream')

const isNode = !global.window
const isNode = require('detect-node')

// -- Internal

Expand Down
24 changes: 11 additions & 13 deletions test/api/add.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use strict'

const path = require('path')
const File = require('vinyl')
const Readable = require('stream').Readable

const isNode = !global.window
const isNode = require('detect-node')

const testfilePath = __dirname + '/../testfile.txt'
let testfile
Expand All @@ -26,19 +24,17 @@ describe('.add', () => {
return done()
}

const file = new File({
cwd: path.dirname(testfilePath),
base: path.dirname(testfilePath),
path: testfilePath,
contents: new Buffer(testfile)
})
const file = {
path: 'testfile.txt',
content: new Buffer(testfile)
}

apiClients['a'].add(file, (err, res) => {
apiClients['a'].add([file], (err, res) => {
expect(err).to.not.exist

const added = res[0] != null ? res[0] : res
expect(added).to.have.property('Hash', 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
expect(added).to.have.property('Name', path.basename(testfilePath))
expect(added).to.have.property('Name', 'testfile.txt')
done()
})
})
Expand All @@ -47,6 +43,7 @@ describe('.add', () => {
let buf = new Buffer(testfile)
apiClients['a'].add(buf, (err, res) => {
expect(err).to.not.exist

expect(res).to.have.length(1)
expect(res[0]).to.have.property('Hash', 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
done()
Expand All @@ -60,6 +57,7 @@ describe('.add', () => {

apiClients['a'].add(testfileBig, (err, res) => {
expect(err).to.not.exist

expect(res).to.have.length(1)
expect(res[0]).to.have.a.property('Hash', 'Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq')
done()
Expand All @@ -84,10 +82,9 @@ describe('.add', () => {
apiClients['a'].add(__dirname + '/../test-folder', { recursive: true }, (err, res) => {
if (isNode) {
expect(err).to.not.exist
console.log('Jeromy ->', res)

const added = res[res.length - 1]
expect(added).to.have.property('Hash', 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg')
expect(added).to.have.property('Hash', 'QmTDH2RXGn8XyDAo9YyfbZAUXwL1FCr44YJCN9HBZmL9Gj')
done()
} else {
expect(err.message).to.be.equal('Recursive uploads are not supported in the browser')
Expand All @@ -100,6 +97,7 @@ describe('.add', () => {
const stream = new Readable()
stream.push('Hello world')
stream.push(null)

apiClients['a'].add(stream, (err, res) => {
expect(err).to.not.exist

Expand Down
8 changes: 6 additions & 2 deletions test/api/files.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,18 @@ describe('.files', function () {
it('files.read', function (done) {
this.timeout(20000)

if (!isNode) {
return done()
}

apiClients['a'].files.read('/test-folder/test-file', function (err, stream) {
expect(err).to.not.exist
let buf = ''
stream
.on('error', function (err) {
.on('error', function (err) {
expect(err).to.not.exist
})
.on('data', function (data) {
.on('data', function (data) {
buf += data
})
.on('end', function () {
Expand Down
2 changes: 1 addition & 1 deletion test/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const apiAddrs = require('./tmp-disposable-nodes-addrs.json')

global.expect = require('chai').expect
global.apiClients = {} // a, b, c
global.isNode = !global.window
global.isNode = require('detect-node')

function connectNodes (done) {
const addrs = {}
Expand Down

0 comments on commit dc87c99

Please sign in to comment.