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

Commit

Permalink
feat: implement "ipfs file ls" (#1078)
Browse files Browse the repository at this point in the history
* feat: cli for "file ls"

* fix: typo

* chore: remove crlfs

* chore: linting

* chore: linting

* feat: http-api for 'file ls'

* fix: lint errors

* fix: file type codes

* refactor: talk directly to ipfs-unixfs-engine

ipfs.ls does not provide enough information

* fix: lint errors
  • Loading branch information
richardschneider authored and daviddias committed Nov 17, 2017
1 parent 9de6f4c commit 6db3fb8
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/cli/commands/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict'

module.exports = {
command: 'file',

description: 'Interact with IPFS objects representing Unix filesystems.',

builder (yargs) {
return yargs
.commandDir('file')
},

handler (argv) {
}
}
27 changes: 27 additions & 0 deletions src/cli/commands/file/ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict'

const print = require('../../utils').print

module.exports = {
command: 'ls <key>',

describe: 'List directory contents for Unix filesystem objects.',

builder: {},

handler (argv) {
let path = argv.key
argv.ipfs.ls(path, (err, links) => {
if (err) {
throw err
}

// Single file? Then print its hash
if (links.length === 0) {
links = [{hash: path}]
}

links.forEach((file) => print(file.hash))
})
}
}
111 changes: 111 additions & 0 deletions src/http/api/resources/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use strict'

const mh = require('multihashes')
const debug = require('debug')
const log = debug('jsipfs:http-api:file')
log.error = debug('jsipfs:http-api:file:error')
const unixfsEngine = require('ipfs-unixfs-engine')
const exporter = unixfsEngine.exporter
const pull = require('pull-stream')
const toB58String = require('multihashes').toB58String

exports = module.exports

const fileTypeMap = {
file: 'File',
dir: 'Directory'
}

function toFileObject (file) {
const fo = {
Hash: toB58String(file.hash),
Size: file.size,
Type: fileTypeMap[file.type] || file.type
}
if (fo.Hash !== file.name) {
fo.Name = file.name
}
return fo
}

// common pre request handler that parses the args and returns `key` which is assigned to `request.pre.args`
exports.parseKey = (request, reply) => {
if (!request.query.arg) {
return reply({
Message: "Argument 'key' is required",
Code: 0
}).code(400).takeover()
}

let key = request.query.arg
if (key.indexOf('/ipfs/') === 0) {
key = key.substring(6)
}

let hash = key
const slashIndex = hash.indexOf('/')
if (slashIndex > 0) {
hash = hash.substring(0, slashIndex)
}

try {
mh.fromB58String(hash)
} catch (err) {
log.error(err)
return reply({
Message: 'invalid ipfs ref path',
Code: 0
}).code(500).takeover()
}

const subpaths = key.split('/')
subpaths.shift()
reply({
path: request.query.arg,
subpaths: subpaths,
key: key,
hash: hash
})
}

exports.ls = {
// uses common parseKey method that returns a `key`
parseArgs: exports.parseKey,

// main route handler which is called after the above `parseArgs`, but only if the args were valid
handler: (request, reply) => {
const path = request.pre.args.path
const ipfs = request.server.app.ipfs
const subpaths = request.pre.args.subpaths
const rootDepth = subpaths.length

pull(
exporter(path, ipfs._ipldResolver, { maxDepth: rootDepth + 1 }),
pull.collect((err, files) => {
if (err) {
return reply({
Message: 'Failed to list dir: ' + err.message,
Code: 0
}).code(500)
}

let res = {
Arguments: {},
Objects: {}
}
const links = []
files.forEach((file) => {
if (file.depth === rootDepth) {
let id = toB58String(file.hash)
res.Arguments[path] = id
res.Objects[id] = toFileObject(file)
res.Objects[id].Links = file.type === 'file' ? null : links
} else {
links.push(toFileObject(file))
}
})
return reply(res)
})
)
}
}
1 change: 1 addition & 0 deletions src/http/api/resources/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ exports.config = require('./config')
exports.block = require('./block')
exports.swarm = require('./swarm')
exports.bitswap = require('./bitswap')
exports.file = require('./file')
exports.files = require('./files')
exports.pubsub = require('./pubsub')
19 changes: 19 additions & 0 deletions src/http/api/routes/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict'

const resources = require('./../resources')

module.exports = (server) => {
const api = server.select('API')

api.route({
// TODO fix method
method: '*',
path: '/api/v0/file/ls',
config: {
pre: [
{ method: resources.file.ls.parseArgs, assign: 'args' }
],
handler: resources.file.ls.handler
}
})
}
1 change: 1 addition & 0 deletions src/http/api/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = (server) => {
require('./config')(server)
require('./swarm')(server)
require('./bitswap')(server)
require('./file')(server)
require('./files')(server)
require('./pubsub')(server)
require('./debug')(server)
Expand Down
2 changes: 1 addition & 1 deletion test/cli/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
const expect = require('chai').expect
const runOnAndOff = require('../utils/on-and-off')

const commandCount = 57
const commandCount = 58

describe('commands', () => runOnAndOff((thing) => {
let ipfs
Expand Down
33 changes: 33 additions & 0 deletions test/cli/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const runOnAndOff = require('../utils/on-and-off')
const file = 'QmR56UJmAaZLXLdTT1ALrE9vVqV8soUEekm9BMd4FnuYqV'
const dir = 'QmYmW4HiZhotsoSqnv2o1oUusvkRM8b9RweBoH7ao5nki2'

describe('file ls', () => runOnAndOff((thing) => {
let ipfs

before(function () {
this.timeout(50 * 1000)
ipfs = thing.ipfs
return ipfs('files add -r test/fixtures/test-data/recursive-get-dir')
})

it('prints a filename', () => {
return ipfs(`file ls ${file}`)
.then((out) => expect(out).to.eql(`${file}\n`))
})

it('prints the filenames in a directory', () => {
return ipfs(`file ls ${dir}`)
.then((out) => expect(out).to.eql(
'QmQQHYDwAQms78fPcvx1uFFsfho23YJNoewfLbi9AtdyJ9\n' +
'QmPkWYfSLCEBLZu7BZt4kigGDMe3cpogMbeVf97gN2xJDN\n' +
'Qma13ZrhKG52MWnwtZ6fMD8jGj8d4Q9sJgn5xtKgeZw5uz\n' +
'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU\n' +
'QmR56UJmAaZLXLdTT1ALrE9vVqV8soUEekm9BMd4FnuYqV\n'
))
})
}))

0 comments on commit 6db3fb8

Please sign in to comment.