From a741e100d170dcc5f26a8c3ccbc5ad0d4c572ff0 Mon Sep 17 00:00:00 2001 From: dirkmc Date: Sat, 11 May 2019 07:49:30 -0400 Subject: [PATCH] feat: refs endpoint (#978) BREAKING CHANGE: ipfs.refs now returns objects with camelCase properties not PascalCase properties. i.e. `{ ref, err }` not `{ Ref, Err }` --- README.md | 8 ++ src/files-regular/index.js | 5 +- src/files-regular/refs-local-pull-stream.js | 27 +++++ .../refs-local-readable-stream.js | 23 ++++ src/files-regular/refs-local.js | 32 ++++++ src/files-regular/refs-pull-stream.js | 34 ++++++ src/files-regular/refs-readable-stream.js | 30 ++++++ src/files-regular/refs.js | 75 +++++++++++++ src/refs.js | 40 ------- src/utils/load-commands.js | 4 +- test/refs.spec.js | 100 ------------------ test/sub-modules.spec.js | 13 ++- 12 files changed, 242 insertions(+), 149 deletions(-) create mode 100644 src/files-regular/refs-local-pull-stream.js create mode 100644 src/files-regular/refs-local-readable-stream.js create mode 100644 src/files-regular/refs-local.js create mode 100644 src/files-regular/refs-pull-stream.js create mode 100644 src/files-regular/refs-readable-stream.js create mode 100644 src/files-regular/refs.js delete mode 100644 src/refs.js delete mode 100644 test/refs.spec.js diff --git a/README.md b/README.md index a1ef73530..c4544e791 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,14 @@ const ipfs = ipfsClient({ - [`ipfs.block.put(block, [options], [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BLOCK.md#blockput) - [`ipfs.block.stat(cid, [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BLOCK.md#blockstat) +- [refs](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md) + - [`ipfs.refs(ipfsPath, [options], [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refs) + - [`ipfs.refsReadableStream(ipfsPath, [options], [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refsreadablestream) + - [`ipfs.refsPullStream(ipfsPath, [options], [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refspullstream) + - [`ipfs.refs.local([callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refslocal) + - [`ipfs.refs.localReadableStream([callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refslocalreadablestream) + - [`ipfs.refs.localPullStream([callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refslocalpullstream) + #### Graph - [dag](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md) diff --git a/src/files-regular/index.js b/src/files-regular/index.js index a52e7505a..059d7ea1c 100644 --- a/src/files-regular/index.js +++ b/src/files-regular/index.js @@ -20,6 +20,9 @@ module.exports = (arg) => { getPullStream: require('../files-regular/get-pull-stream')(send), ls: require('../files-regular/ls')(send), lsReadableStream: require('../files-regular/ls-readable-stream')(send), - lsPullStream: require('../files-regular/ls-pull-stream')(send) + lsPullStream: require('../files-regular/ls-pull-stream')(send), + refs: require('../files-regular/refs')(send), + refsReadableStream: require('../files-regular/refs-readable-stream')(send), + refsPullStream: require('../files-regular/refs-pull-stream')(send) } } diff --git a/src/files-regular/refs-local-pull-stream.js b/src/files-regular/refs-local-pull-stream.js new file mode 100644 index 000000000..c4452b116 --- /dev/null +++ b/src/files-regular/refs-local-pull-stream.js @@ -0,0 +1,27 @@ +'use strict' + +const pull = require('pull-stream') +const toPull = require('stream-to-pull-stream') +const deferred = require('pull-defer') +const moduleConfig = require('../utils/module-config') + +module.exports = (send) => { + send = moduleConfig(send) + + return (opts) => { + opts = opts || {} + + const p = deferred.source() + + send({ path: 'refs/local', qs: opts }, (err, stream) => { + if (err) { return p.resolve(pull.error(err)) } + + p.resolve(pull( + toPull.source(stream), + pull.map(r => ({ ref: r.Ref, err: r.Err })) + )) + }) + + return p + } +} diff --git a/src/files-regular/refs-local-readable-stream.js b/src/files-regular/refs-local-readable-stream.js new file mode 100644 index 000000000..0d8bc15bf --- /dev/null +++ b/src/files-regular/refs-local-readable-stream.js @@ -0,0 +1,23 @@ +'use strict' + +const Stream = require('readable-stream') +const pump = require('pump') +const through = require('through2') + +module.exports = (send) => { + return (opts) => { + opts = opts || {} + + const pt = new Stream.PassThrough({ objectMode: true }) + + send({ path: 'refs/local', qs: opts }, (err, stream) => { + if (err) { return pt.destroy(err) } + + pump(stream, through.obj(function (r, enc, cb) { + cb(null, { ref: r.Ref, err: r.Err }) + }), pt) + }) + + return pt + } +} diff --git a/src/files-regular/refs-local.js b/src/files-regular/refs-local.js new file mode 100644 index 000000000..680e51000 --- /dev/null +++ b/src/files-regular/refs-local.js @@ -0,0 +1,32 @@ +'use strict' + +const promisify = require('promisify-es6') +const streamToValueWithTransformer = require('../utils/stream-to-value-with-transformer') +const moduleConfig = require('../utils/module-config') + +module.exports = (arg) => { + const send = moduleConfig(arg) + + return promisify((opts, callback) => { + if (typeof (opts) === 'function') { + callback = opts + opts = {} + } + + const transform = (res, cb) => { + cb(null, res.map(r => ({ ref: r.Ref, err: r.Err }))) + } + + const request = { + path: 'refs/local', + qs: opts + } + send(request, (err, result) => { + if (err) { + return callback(err) + } + + streamToValueWithTransformer(result, transform, callback) + }) + }) +} diff --git a/src/files-regular/refs-pull-stream.js b/src/files-regular/refs-pull-stream.js new file mode 100644 index 000000000..e3c1b113a --- /dev/null +++ b/src/files-regular/refs-pull-stream.js @@ -0,0 +1,34 @@ +'use strict' + +const pull = require('pull-stream') +const toPull = require('stream-to-pull-stream') +const deferred = require('pull-defer') +const moduleConfig = require('../utils/module-config') +const { checkArgs, normalizeOpts } = require('./refs') + +module.exports = (send) => { + send = moduleConfig(send) + + return (args, opts) => { + opts = normalizeOpts(opts) + + const p = deferred.source() + + try { + args = checkArgs(args) + } catch (err) { + return p.end(err) + } + + send({ path: 'refs', args, qs: opts }, (err, stream) => { + if (err) { return p.resolve(pull.error(err)) } + + p.resolve(pull( + toPull.source(stream), + pull.map(r => ({ ref: r.Ref, err: r.Err })) + )) + }) + + return p + } +} diff --git a/src/files-regular/refs-readable-stream.js b/src/files-regular/refs-readable-stream.js new file mode 100644 index 000000000..4c9ae2d1f --- /dev/null +++ b/src/files-regular/refs-readable-stream.js @@ -0,0 +1,30 @@ +'use strict' + +const Stream = require('readable-stream') +const pump = require('pump') +const through = require('through2') +const { checkArgs, normalizeOpts } = require('./refs') + +module.exports = (send) => { + return (args, opts) => { + opts = normalizeOpts(opts) + + const pt = new Stream.PassThrough({ objectMode: true }) + + try { + args = checkArgs(args) + } catch (err) { + return pt.destroy(err) + } + + send({ path: 'refs', args, qs: opts }, (err, stream) => { + if (err) { return pt.destroy(err) } + + pump(stream, through.obj(function (r, enc, cb) { + cb(null, { ref: r.Ref, err: r.Err }) + }), pt) + }) + + return pt + } +} diff --git a/src/files-regular/refs.js b/src/files-regular/refs.js new file mode 100644 index 000000000..986a6f6cc --- /dev/null +++ b/src/files-regular/refs.js @@ -0,0 +1,75 @@ +'use strict' + +const IsIpfs = require('is-ipfs') +const promisify = require('promisify-es6') +const streamToValueWithTransformer = require('../utils/stream-to-value-with-transformer') +const moduleConfig = require('../utils/module-config') +const cleanCID = require('../utils/clean-cid') + +module.exports = (arg) => { + const send = moduleConfig(arg) + + const refs = promisify((args, opts, callback) => { + if (typeof (opts) === 'function') { + callback = opts + opts = {} + } + opts = module.exports.normalizeOpts(opts) + + try { + args = module.exports.checkArgs(args) + } catch (err) { + return callback(err) + } + + const transform = (res, cb) => { + cb(null, res.map(r => ({ ref: r.Ref, err: r.Err }))) + } + + const request = { + args, + path: 'refs', + qs: opts + } + send(request, (err, result) => { + if (err) { + return callback(err) + } + + streamToValueWithTransformer(result, transform, callback) + }) + }) + + refs.local = require('./refs-local')(arg) + refs.localReadableStream = require('./refs-local-readable-stream')(arg) + refs.localPullStream = require('./refs-local-pull-stream')(arg) + + return refs +} + +module.exports.checkArgs = (args) => { + const isArray = Array.isArray(args) + args = isArray ? args : [args] + + const res = [] + for (let arg of args) { + try { + arg = cleanCID(arg) + } catch (err) { + if (!IsIpfs.ipfsPath(arg)) { + throw err + } + } + res.push(arg) + } + + return isArray ? res : res[0] +} + +module.exports.normalizeOpts = (opts) => { + opts = opts || {} + if (typeof opts.maxDepth === 'number') { + opts['max-depth'] = opts.maxDepth + } + return opts +} diff --git a/src/refs.js b/src/refs.js deleted file mode 100644 index 97d204124..000000000 --- a/src/refs.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict' - -const promisify = require('promisify-es6') -const streamToValue = require('./utils/stream-to-value') -const moduleConfig = require('./utils/module-config') - -module.exports = (arg) => { - const send = moduleConfig(arg) - - const refs = promisify((args, opts, callback) => { - if (typeof (opts) === 'function') { - callback = opts - opts = {} - } - - const request = { - path: 'refs', - args: args, - qs: opts - } - - send.andTransform(request, streamToValue, callback) - }) - - refs.local = promisify((opts, callback) => { - if (typeof (opts) === 'function') { - callback = opts - opts = {} - } - - const request = { - path: 'refs/local', - qs: opts - } - - send.andTransform(request, streamToValue, callback) - }) - - return refs -} diff --git a/src/utils/load-commands.js b/src/utils/load-commands.js index 030aef5ab..e4a914dd0 100644 --- a/src/utils/load-commands.js +++ b/src/utils/load-commands.js @@ -18,6 +18,9 @@ function requireCommands () { ls: require('../files-regular/ls'), lsReadableStream: require('../files-regular/ls-readable-stream'), lsPullStream: require('../files-regular/ls-pull-stream'), + refs: require('../files-regular/refs'), + refsReadableStream: require('../files-regular/refs-readable-stream'), + refsPullStream: require('../files-regular/refs-pull-stream'), // Files MFS (Mutable Filesystem) files: require('../files-mfs'), @@ -50,7 +53,6 @@ function requireCommands () { key: require('../key'), log: require('../log'), mount: require('../mount'), - refs: require('../refs'), repo: require('../repo'), stop: require('../stop'), shutdown: require('../stop'), diff --git a/test/refs.spec.js b/test/refs.spec.js deleted file mode 100644 index 9ffcf4ebe..000000000 --- a/test/refs.spec.js +++ /dev/null @@ -1,100 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const isNode = require('detect-node') -const waterfall = require('async/waterfall') -const path = require('path') -const fs = require('fs') - -const ipfsClient = require('../src') -const f = require('./utils/factory') - -describe('.refs', function () { - this.timeout(80 * 1000) - - if (!isNode) { return } - - let ipfs - let ipfsd - let folder - - before((done) => { - const filesPath = path.join(__dirname, '/fixtures/test-folder') - - // Symlinks in a repo don't always clone well, especially on Windows. - // So if the 'hello-link' is not a symlink, then make it one. - const symlinkPath = filesPath + '/hello-link' - const symlinkTarget = 'files/hello.txt' - if (!fs.lstatSync(symlinkPath).isSymbolicLink()) { - fs.unlinkSync(symlinkPath) - fs.symlinkSync(symlinkTarget, symlinkPath) - } - - waterfall([ - (cb) => f.spawn({ initOptions: { bits: 1024, profile: 'test' } }, cb), - (_ipfsd, cb) => { - ipfsd = _ipfsd - ipfs = ipfsClient(_ipfsd.apiAddr) - ipfs.addFromFs(filesPath, { recursive: true }, cb) - }, - (hashes, cb) => { - folder = hashes[hashes.length - 1].hash - expect(folder).to.be.eql('QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh') - cb() - } - ], done) - }) - - after((done) => { - if (!ipfsd) return done() - ipfsd.stop(done) - }) - - const result = [ - { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmcUYKmQxmTcFom4R4UZP7FWeQzgJkwcFn51XrvsMy7PE9 add', - Err: '' - }, { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmNeHxDfQfjVFyYj2iruvysLH9zpp78v3cu1s3BZq1j5hY cat', - Err: '' - }, { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmTYFLz5vsdMpq4XXw1a1pSxujJc9Z5V3Aw1Qg64d849Zy files', - Err: '' - }, { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu hello-link', - Err: '' - }, { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmU7wetVaAqc3Meurif9hcYBHGvQmL5QdpPJYBoZizyTNL ipfs-add', - Err: '' - }, { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmctZfSuegbi2TMFY2y3VQjxsH5JbRBu7XmiLfHNvshhio ls', - Err: '' - }, { - Ref: 'QmQao3KNcpCsdXaLGpjieFGMfXzsSXgsf6Rnc5dJJA3QMh QmbkMNB6rwfYAxRvnG9CWJ6cKKHEdq2ZKTozyF5FQ7H8Rs version', - Err: '' - } - ] - - describe('Callback API', () => { - it('refs', (done) => { - ipfs.refs(folder, { format: ' ' }, (err, objs) => { - expect(err).to.not.exist() - expect(objs).to.eql(result) - done() - }) - }) - }) - - describe('Promise API', () => { - it('refs', () => { - return ipfs.refs(folder, { format: ' ' }) - .then((objs) => { - expect(objs).to.eql(result) - }) - }) - }) -}) diff --git a/test/sub-modules.spec.js b/test/sub-modules.spec.js index 333407ea4..cabd09b55 100644 --- a/test/sub-modules.spec.js +++ b/test/sub-modules.spec.js @@ -183,6 +183,12 @@ describe('submodules', () => { expect(filesRegular.ls).to.be.a('function') expect(filesRegular.lsReadableStream).to.be.a('function') expect(filesRegular.lsPullStream).to.be.a('function') + expect(filesRegular.refs).to.be.a('function') + expect(filesRegular.refsReadableStream).to.be.a('function') + expect(filesRegular.refsPullStream).to.be.a('function') + expect(filesRegular.refs.local).to.be.a('function') + expect(filesRegular.refs.localReadableStream).to.be.a('function') + expect(filesRegular.refs.localPullStream).to.be.a('function') }) it('files MFS API', () => { @@ -209,11 +215,4 @@ describe('submodules', () => { expect(mount).to.be.a('function') }) - - it('refs', () => { - const refs = require('../src/refs')(config) - - expect(refs).to.be.a('function') - expect(refs.local).to.be.a('function') - }) })