From 4626e275941f2c03b831eadfabcb0ccdb5a0ed42 Mon Sep 17 00:00:00 2001 From: Mark Schroering Date: Thu, 30 May 2019 08:47:04 -0400 Subject: [PATCH 1/3] Added 'files mv' command to rename files --- lib/cmds/files_cmds/mv.js | 58 +++++++++++++++++++++++++++++++++ test/unit/commands/file.test.js | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 lib/cmds/files_cmds/mv.js diff --git a/lib/cmds/files_cmds/mv.js b/lib/cmds/files_cmds/mv.js new file mode 100644 index 0000000..e2d685f --- /dev/null +++ b/lib/cmds/files_cmds/mv.js @@ -0,0 +1,58 @@ +'use strict'; + +const chalk = require('chalk'); +const querystring = require('querystring'); +const path = require('path'); +const { patch, list } = require('../../api'); + +exports.command = 'mv '; +exports.desc = 'Move/rename files from to within a project'; +exports.builder = yargs => { + yargs.positional('source', { + describe: 'Either the ID of the file to move or a project ID and prefix of the form / to move a set of files in a project.', + type: 'string' + }).positional('dest', { + describe: 'The destination directory for the download file. Defaults to current working directory', + type: 'string' + }).option('recursive', { + describe: + 'If is a project Id with an optional prefix, move all files under that prefix to the destination.', + alias: 'r', + type: 'boolean', + default: false + }).option('limit', { + describe: 'The maximum number of files to move when used with the --recursive option', + alias: 'l', + type: 'number', + default: 1000 + }); +}; + +exports.handler = async argv => { + if (argv.recursive) { + const [datasetId, prefix] = argv.source.split('/'); + + const opts = { + datasetId, + pageSize: argv.limit + }; + + if (prefix) { + opts.name = prefix; + } + + const response = await list(argv, `/v1/files?${querystring.stringify(opts)}`); + for (const item of response.data.items) { + const newName = path.join(argv.dest, item.name.replace(prefix, '')); + await patch(argv, `/v1/files/${item.id}`, { + name: newName + }); + console.log(chalk.green(`Moved: `) + item.name + chalk.green(` to `) + newName); + } + } else { + await patch(argv, `/v1/files/${argv.source}`, { + name: argv.dest + }); + console.log(chalk.green(`Moved: `) + argv.source + chalk.green(` to `) + argv.dest); + } +}; diff --git a/test/unit/commands/file.test.js b/test/unit/commands/file.test.js index 66093f7..1394126 100644 --- a/test/unit/commands/file.test.js +++ b/test/unit/commands/file.test.js @@ -10,6 +10,7 @@ const querystring = require('querystring'); const getStub = sinon.stub(); const postStub = sinon.stub(); +const patchStub = sinon.stub(); const delStub = sinon.stub(); const printSpy = sinon.spy(); const downloadSpy = sinon.spy(); @@ -37,6 +38,10 @@ const mocks = { list: function (options, url) { return listStub(options, url); }, + patch: (options, url, opts) => { + patchStub(options, url, opts); + callback(); + }, post: postStub, del: delStub, download: function (options, url, file, name) { @@ -69,6 +74,7 @@ const list = proxyquire('../../../lib/cmds/files_cmds/list', mocks); const download = proxyquire('../../../lib/cmds/files_cmds/download', mocks); const upload = proxyquire('../../../lib/cmds/files_cmds/upload', mocks); const ls = proxyquire('../../../lib/cmds/files_cmds/ls', mocks); +const mv = proxyquire('../../../lib/cmds/files_cmds/mv', mocks); test.beforeEach(t => { t.context.sandbox = sinon.createSandbox(); @@ -78,6 +84,7 @@ test.afterEach.always(t => { listStub.reset(); getStub.reset(); postStub.reset(); + patchStub.reset(); delStub.reset(); printSpy.resetHistory(); uploadSpy.resetHistory(); @@ -236,6 +243,57 @@ test.serial.cb('The "files-download" command should download a set of files from .parse('download projectId/prefix /dir -r'); }); +test.serial.cb('The "files-mv" command should move a set of files from a project', t => { + patchStub.returns({}); + listStub.onFirstCall().returns({ + data: { + items: [ + { + id: '1', + name: 'foo.txt' + } + ] + } + }); + + callback = () => { + t.is(listStub.callCount, 1); + t.is(listStub.getCall(0).args[1], '/v1/files?datasetId=projectId&pageSize=1000&name=prefix'); + + t.is(patchStub.callCount, 1); + t.is(patchStub.getCall(0).args[1], '/v1/files/1'); + t.deepEqual(patchStub.getCall(0).args[2], { + name: '/dir/foo.txt' + }); + t.end(); + }; + + t.context.deleteFileStub = t.context.sandbox.stub(fs, 'unlinkSync').callsFake(callback); + t.context.copyFileStub = t.context.sandbox.stub(fs, 'copyFileSync').callsFake(callback); + + yargs.command(mv) + .parse('mv projectId/prefix /dir -r'); +}); + +test.serial.cb('The "files-mv" command should move a file in a project', t => { + patchStub.returns({}); + + callback = () => { + t.is(patchStub.callCount, 1); + t.is(patchStub.getCall(0).args[1], '/v1/files/1234'); + t.deepEqual(patchStub.getCall(0).args[2], { + name: '/dir/bar.txt' + }); + t.end(); + }; + + t.context.deleteFileStub = t.context.sandbox.stub(fs, 'unlinkSync').callsFake(callback); + t.context.copyFileStub = t.context.sandbox.stub(fs, 'copyFileSync').callsFake(callback); + + yargs.command(mv) + .parse('mv 1234 /dir/bar.txt'); +}); + test.serial.cb('The "files-upload" command should upload a file', t => { const res = { data: { uploadUrl: 'https://host/upload' } }; postStub.onFirstCall().returns(res); From 27b7f8041ca0de7c2f9584655fe5b9d8d3875208 Mon Sep 17 00:00:00 2001 From: Mark Schroering Date: Thu, 30 May 2019 13:01:23 -0400 Subject: [PATCH 2/3] Stop using path.join --- lib/cmds/files_cmds/mv.js | 3 +-- test/unit/commands/file.test.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/cmds/files_cmds/mv.js b/lib/cmds/files_cmds/mv.js index e2d685f..c22e908 100644 --- a/lib/cmds/files_cmds/mv.js +++ b/lib/cmds/files_cmds/mv.js @@ -2,7 +2,6 @@ const chalk = require('chalk'); const querystring = require('querystring'); -const path = require('path'); const { patch, list } = require('../../api'); exports.command = 'mv '; @@ -43,7 +42,7 @@ exports.handler = async argv => { const response = await list(argv, `/v1/files?${querystring.stringify(opts)}`); for (const item of response.data.items) { - const newName = path.join(argv.dest, item.name.replace(prefix, '')); + const newName = [argv.dest, item.name.replace(prefix, '')].join('/').replace(/\/{1,}/g, '/'); await patch(argv, `/v1/files/${item.id}`, { name: newName }); diff --git a/test/unit/commands/file.test.js b/test/unit/commands/file.test.js index 1394126..fc28ede 100644 --- a/test/unit/commands/file.test.js +++ b/test/unit/commands/file.test.js @@ -272,7 +272,7 @@ test.serial.cb('The "files-mv" command should move a set of files from a project t.context.copyFileStub = t.context.sandbox.stub(fs, 'copyFileSync').callsFake(callback); yargs.command(mv) - .parse('mv projectId/prefix /dir -r'); + .parse('mv projectId/prefix /dir/ -r'); }); test.serial.cb('The "files-mv" command should move a file in a project', t => { From f3aa5b1923b29e4c314929dd428146fc29150237 Mon Sep 17 00:00:00 2001 From: Mark Schroering Date: Thu, 30 May 2019 13:03:29 -0400 Subject: [PATCH 3/3] Fixed arg description --- lib/cmds/files_cmds/mv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cmds/files_cmds/mv.js b/lib/cmds/files_cmds/mv.js index c22e908..c231204 100644 --- a/lib/cmds/files_cmds/mv.js +++ b/lib/cmds/files_cmds/mv.js @@ -11,7 +11,7 @@ exports.builder = yargs => { describe: 'Either the ID of the file to move or a project ID and prefix of the form / to move a set of files in a project.', type: 'string' }).positional('dest', { - describe: 'The destination directory for the download file. Defaults to current working directory', + describe: 'The new destination for the file within the project.', type: 'string' }).option('recursive', { describe: