diff --git a/.gitignore b/.gitignore index f55067b..a76b468 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,6 @@ typings/ .serverless -.idea \ No newline at end of file +.idea +bin +dist \ No newline at end of file diff --git a/README.adoc b/README.adoc index 4430bcf..ad08870 100644 --- a/README.adoc +++ b/README.adoc @@ -15,9 +15,18 @@ Interact with the Alfresco repository with an interactive command line. - Clone the repository - Install using `npm install -g && npm link` +== Features +- Performs Alfresco operations from the terminal. +- Autocompletes node names for valid operations. +- Performs glob pattern matching for filename related operations. +- Maintains a session with a valid token, automatically persists within terminal sessions. +- Maintains a local command history + + == Usage - Call `alfresco-cli` from your terminal. + === Logging in Type `login --help` to read the help on logging in to the system and fetch an authentication token for the CLI. @@ -74,14 +83,29 @@ None == Version History +* 1.3 - 20181028 +- *Major:* Improved `login` command. +- Added auto completion for site names when using `change site` command. + +* 1.2.3 - 20181026 +- Added search results auto completion in the results history. +- Added `alias set|clear` commands for adding aliases for search queries. +- Added glob pattern matching for `list` and `delete` commands. + +* 1.2.2 - 20181026 +- Added better `delete` function with confirmation prompt. +- Added `undo delete` to restore last deleted node. + +* 1.2.1 - 20181025 +- Added `list versions` command. * 1.2 - 20181025 -- [MAJOR] Adding folder name auto completion +- *Major:* Adding folder name auto completion - Added `delete` command with support for deleting child nodes. * 1.1 - 20181024 - Converted the code to Typescript - Added support for node name as an alias for nodeId when referred from a valid context. -- Added `create user`, `create site`, `cd-site` commands. +- Added `create user`, `create site`, `cd-site`, `search` commands. - Added support for `.` and `..` aliases. * 1.0 - 20181023 diff --git a/README.md b/README.md index 17376f4..1230a2d 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,18 @@ - Install using `npm install -g && npm link` +## Features + +- Performs Alfresco operations from the terminal. + +- Autocompletes node names for valid operations. + +- Performs glob pattern matching for filename related operations. + +- Maintains a session with a valid token, automatically persists within terminal sessions. + +- Maintains a local command history + ## Usage - Call `alfresco-cli` from your terminal. @@ -88,9 +100,33 @@ None ## Version History +- 1.3 - 20181028 + + - **Major:** Improved `login` command. + + - Added auto completion for site names when using `change site` command. + +- 1.2.3 - 20181026 + + - Added search results auto completion in the results history. + + - Added `alias set|clear` commands for adding aliases for search queries. + + - Added glob pattern matching for `list` and `delete` commands. + +- 1.2.2 - 20181026 + + - Added better `delete` function with confirmation prompt. + + - Added `undo delete` to restore last deleted node. + +- 1.2.1 - 20181025 + + - Added `list versions` command. + - 1.2 - 20181025 - - \[MAJOR\] Adding folder name auto completion + - **Major:** Adding folder name auto completion - Added `delete` command with support for deleting child nodes. @@ -100,7 +136,7 @@ None - Added support for node name as an alias for nodeId when referred from a valid context. - - Added `create user`, `create site`, `cd-site` commands. + - Added `create user`, `create site`, `cd-site`, `search` commands. - Added support for `.` and `..` aliases. diff --git a/package.json b/package.json index a8d1bc7..78338fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alfresco-cli", - "version": "1.2.0", + "version": "1.3.1", "description": "Alfresco CLI - Command line interface for Alfresco", "main": "src/index.js", "bin": "dist/bundle.js", @@ -8,7 +8,11 @@ "build": "webpack", "start": "npm run build && node dist/bundle.js", "test": "echo \"Error: no test specified\" && exit 1", - "prepublishOnly": "npm run build && sh md-gen.sh" + "prepublishOnly": "npm run build && sh md-gen.sh", + "version": "npm run build && git add -A", + "postversion": "git push && git push --tags", + "commit": "git add -A && git commit -m 'post-publish-automatic' && npm run postversion", + "postpublish": "npm run commit" }, "author": "Bhagya Nirmaan Silva", "license": "MIT", @@ -23,6 +27,7 @@ "flat": "^4.1.0", "fs": "0.0.1-security", "inquirer": "^6.2.0", + "minimatch": "^3.0.4", "prettyjson": "^1.2.1", "shelljs": "^0.8.2", "source-map-loader": "^0.2.4", diff --git a/src/index.ts b/src/index.ts index c185d1e..1ebbc94 100755 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,8 @@ import chalk = require('chalk'); import AsciiTable = require("ascii-table"); import prettyjson = require('prettyjson'); +let minimatch = require("minimatch") + let parseNodeRef = (nodeRef?: string) => { return nodeRef; }; @@ -20,15 +22,33 @@ let ticket = ''; const vorpal = new Vorpal(); +let siteNameAutoCompletion = async (input, callback) => { + let sites = await alfrescoJsApi.core.sitesApi.getSites().catch( + vorpal.log('Unable to find sites for listing.') + ); + let results = []; + sites.list.entries.map(entry => { + results.push(entry.entry.id); + }); + return results; +}; let nodeNameAutoCompletion = async (input, callback) => { try { let currentNodeRef = getCurrentNodeRef(); return await alfrescoJsApi.nodes.getNodeChildren(currentNodeRef).then( data => { - return data.list.entries.map(entry => { + let list = data.list.entries.map(entry => { return entry.entry.name }); + list.push('.', '..'); + + let resultsHistory = vorpal.localStorage.getItem('resultsHistory') ? JSON.parse(vorpal.localStorage.getItem('resultsHistory')) : []; + if (resultsHistory) { + list.push(resultsHistory); + } + + return list; } ) } catch (e) { @@ -38,40 +58,87 @@ let nodeNameAutoCompletion = async (input, callback) => { vorpal - .command('login [password] [host]', 'Login to an Alfresco instance.') - .option('-p', '--password', 'Password') - .option('-h', '--host', "Host") + .command('login ', 'Login to an Alfresco instance.') + .option('-p, --password ', 'Password') + .option('-h, --host ', "Host") + .option('-s, --save', "Save host") .action(function (args, callback) { let self = this; self.log(chalk.default.green('logging in..')); - let password; + let loginToAlfresco = (username, password) => { + return new Promise((resolve, reject) => { + return alfrescoJsApi.login(username, password) + .then(function (data) { + self.log('API authentication performed successfully. Login ticket:' + data); + vorpal.localStorage.setItem('ticket', data); + resolve(); + }, function (data) { + let error = JSON.parse(data.response.text).error.briefSummary; + reject(error); + }).catch(error => reject(error)); + }) + }; - self.log(JSON.stringify(args)); - if (args.host) { - host = args.host; - this.log("Updating host: " + args.host); - vorpal.localStorage.setItem('host', args.host); + let getParameter = (parameter, type = 'input') => { + return new Promise((resolve, reject) => { + let storedValue = vorpal.localStorage.getItem(parameter); + if (storedValue) { + return resolve(storedValue); + } + + if (args[parameter]) { + return resolve(args[parameter]); + } + if (args.options[parameter]) { + return resolve(args.options[parameter]); + } else { + return self.prompt({ + type: type, + name: parameter, + default: null, + message: `Please enter the ${parameter}: `, + }, results => { + let result = results[parameter]; + if (result) { + resolve(result); + } else { + reject('Please try again.') + } + }, error => { + reject(error); + }); + } + }); + }; + + let updateHost = async (host) => { + this.log(`Using host: ${host}. Change host by passing host as an argument.`); alfrescoJsApi.changeEcmHost(host); - } - if (args.password) { - password = args.password; - } else { - //prompt for password - } + if (args.options.save) { + vorpal.localStorage.setItem('host', host); + } + }; - alfrescoJsApi.login(args.username, password).then(function (data) { - self.log('API authentication performed successfully. Login ticket:' + data); - vorpal.localStorage.setItem('ticket', data); - }, function (error) { - console.error(error); - callback(); + let authenticate = new Promise((resolve, reject) => { + return getParameter('host').then((host => { + return updateHost(host).then(() => { + return getParameter('password', 'password').then(password => { + return loginToAlfresco(args.username, password).then(() => { + return resolve(); + }).catch(reject); + }).catch(reject) + }); + })); }); - callback(); + + authenticate.catch(callback).then(callback); }); vorpal.command('change site ', 'Change into a site.') + .alias('cd-site') + .autocomplete({data: siteNameAutoCompletion}) .action((args, callback) => { alfrescoJsApi.core.sitesApi.getSites().then(function (data) { vorpal.log('API called successfully. Returned data for ' + data.list.entries.length + ' sites'); @@ -83,9 +150,8 @@ vorpal.command('change site ', 'Change into a site.') } ); }, function (error) { - console.error(error); - }) - callback(); + vorpal.error(error); + }).then(callback); }); vorpal @@ -98,38 +164,11 @@ vorpal let self = this; alfrescoJsApi.core.sitesApi.getSites().then(function (data) { self.log('API called successfully. Returned data for ' + data.list.entries.length + ' sites'); - let sites = data.list.entries.map((item) => { - let i = {}; - if (args.options.info) { - i[item.entry.id] = item.entry; - } else { - i[item.entry.id] = item.entry.title + (item.entry.description ? " - " + item.entry.description : ""); - } - return i; - }) - - let rows = flatten(sites); - var table = new AsciiTable(); - if (args.info) { - table.setHeading('site-id/property', 'value', "id"); - } else { - table.setHeading('site-id', 'site-name', "id"); - } - for (var key in rows) { - if (args.property) { - if (args.property == key) { - table.addRow(key, rows[key]) - } - } else { - table.addRow(key, rows[key]); - } - } - - self.log(table.toString()); + printNodeList(data.list.entries, ['id', 'guid', 'title', 'description']); + cacheResults(data.list.entries, ['guid']); }, function (error) { - console.error(error); - }); - callback(); + vorpal.log(error); + }).then(callback); }); vorpal.command('list people', "Lists all users in system.") @@ -137,11 +176,11 @@ vorpal.command('list people', "Lists all users in system.") let self = this; alfrescoJsApi.core.peopleApi.getPersons().then(function (data) { self.log('API called successfully. Returned data for ' + data.list.entries.length + ' users.'); - + printNodeList(data.list.entries); //TODO: Add the user information table. }, function (error) { - console.error(error); - }); + vorpal.log(error); + }).then(callback); }); vorpal @@ -172,18 +211,16 @@ vorpal.command('list parents [nodeRef]', 'Lists parents for a given nodeRef.') // }) let self = this; - getNodeRef(args.nodeRef).then(nodeRef => { - alfrescoJsApi.core.childAssociationsApi.listParents(nodeRef, {}) + getNodeRefContext(args.nodeRef).then(nodeRef => { + return alfrescoJsApi.core.childAssociationsApi.listParents(nodeRef, {}) .then(function (data) { // @ts-ignore self.log('API called successfully. ' + data.list.pagination.totalItems + ' parent(s) found.'); printNodeList(data.list.entries); - cb(); }, function (error) { - console.error(error); - cb(); + vorpal.log(error); }); - }); + }).then(cb); } ); @@ -197,7 +234,7 @@ vorpal let fileToUpload = fs.createReadStream(args.filePath); const bar1 = new _cliProgress.Bar({}, _cliProgress.Presets.shades_classic); bar1.start(0, 0, null); - getNodeRef(args.destinationNodeRef).then(destinationNodeRef => { + getNodeRefContext(args.destinationNodeRef).then(destinationNodeRef => { let upload = alfrescoJsApi.upload.uploadFile(fileToUpload, args.relativePath, destinationNodeRef, null, {autoRename: args.options.autoRename}) .on('progress', (progress) => { @@ -242,13 +279,13 @@ vorpal.command('view-metadata [nodeRef] [property]', "Shows metadata for the sel .autocomplete({data: nodeNameAutoCompletion}) .action(function (args, callback) { let self = this; - getNodeRef(args.nodeRef).then(nodeRef => { + getNodeRefContext(args.nodeRef).then(nodeRef => { alfrescoJsApi.nodes.getNodeInfo(nodeRef).then(function (data) { self.log('name: ' + data.name); let rows = flatten(data); - var table = new AsciiTable(); + let table = new AsciiTable(); table.setHeading('property', 'value'); - for (var key in rows) { + for (let key in rows) { if (args.property) { if (args.property == key) { table.addRow(key, rows[key]) @@ -259,15 +296,13 @@ vorpal.command('view-metadata [nodeRef] [property]', "Shows metadata for the sel } self.log(table.toString()); - }, function (error) { self.log('This node does not exist'); }); - }); - callback(); + }).then(callback); }); -vorpal.command('move-node ', "Moves a node to a destination.") +vorpal.command('move ', "Moves a node to a destination.") .autocomplete({data: nodeNameAutoCompletion}) .action(function (args, callback) { alfrescoJsApi.nodes.moveNode(args[0]) @@ -292,19 +327,16 @@ vorpal.command('create site [title] [description]', "Creates a site (Vi alfrescoJsApi.core.sitesApi.createSite(siteBody, {skipAddToFavorites: false, skipConfiguration: false}) .then(() => { vorpal.log("Site created successfully."); - callback(); - }).catch(() => { - vorpal.log("There was an error creating the site."); - callback(); - }); + }).catch((e) => { + vorpal.log(`There was an error creating the site: ${e.message}`); + }).then(callback); }); vorpal.command("create person [email] [firstName] [lastName]", "Creates a new user.") .action((args, callback) => { - //alfresco JS API has no endpoint that can serve this request. let self = this; - var person: PersonBodyCreate = { + let person: PersonBodyCreate = { id: args.userName, password: args.password, firstName: args.firstName, @@ -320,23 +352,84 @@ vorpal.command("create person [email] [firstName] [lastNam ).catch(e => { vorpal.log("Unable to create person."); vorpal.log(e) - }); + }).then(callback); + }); + +vorpal.command('list versions ') + .action((args, callback) => { + getNodeRefContext(args.nodeRef).then(nodeId => { + alfrescoJsApi.core.versionsApi.listVersionHistory(nodeId, {}).then(function (data) { + printNodeList(data.list.entries); + }, function (error) { + vorpal.log(error); + }).then(callback) + }) + }); + +interface Alias { + alias: string; + expanded: string; +} + +vorpal.command('alias set ', 'Sets an alias for an argument/search query') + .action((args, callback) => { + let aliases = getAllAliases(); + + //if found, update + if (getAlias(args.alias)) { + //get all aliases except this one. + aliases = aliases.filter(item => { + return item.alias != args.alias; + }) + } + + let alias: Alias = {alias: args.alias, expanded: args.expanded}; + aliases.push(alias); + + vorpal.localStorage.setItem('aliases', JSON.stringify(aliases)); + //known issue, need to handle setting existing alias. callback(); }); -vorpal.command("create folder [path]", "Create folder at the destination.") - .option('-p', "--path", "Relative path from the destination nodeRef.") +vorpal.command('alias clear', 'Clears all aliases that have been set.') + .action((args, callback) => { + vorpal.localStorage.removeItem('aliases'); + callback(); + }); + +function getAllAliases(): Array { + return vorpal.localStorage.getItem('aliases') ? JSON.parse(vorpal.localStorage.getItem('aliases')) : []; +} + +function getAlias(alias): Alias { + let aliases = getAllAliases(); + let filterElement: Alias = aliases.filter(aliasItem => { + return aliasItem.alias == alias + })[0]; + return filterElement; +} + +function getAliasString(alias): string { + let foundAlias = getAlias(alias); + if (foundAlias) { + return foundAlias.expanded; + } else { + throw new Error(`Unable to find matching alias for '${alias}'.`) + } +} + +vorpal.command("create folder [destinationNodeRef] [path]", "Create folder at the destination.") + .option('-p, --path', "Relative path from the destination nodeRef.") .alias('mkdir') .action(function (args, callback) { let self = this; - getNodeRef(args.destinationNodeRef).then(destinationNodeRef => { - alfrescoJsApi.nodes.createFolder(args.folderName, args.path, destinationNodeRef).then(function (data) { + getNodeRefContext(args.destinationNodeRef).then(destinationNodeRef => { + return alfrescoJsApi.nodes.createFolder(args.folderName, args.path, destinationNodeRef).then(function (data) { self.log('The folder is created.'); }, function (error) { self.log('Error in creation of this folder or folder already exist' + error); }).catch(e => self.log(e.message)); - }); - callback(); + }).then(callback); }); let init = async () => { @@ -360,10 +453,12 @@ let init = async () => { } }; +const error = chalk.default.keyword('red'); const warning = chalk.default.keyword('orange'); -const info = chalk.default.keyword('blue'); +const info = chalk.default.keyword('gray'); -vorpal.command('search [language]', "Searches the repostitory for content.") +vorpal.command('search [language] [alias]', "Searches the repostitory for content.") + .option('-a, --alias', 'Use search query alias') .action(function (args, callback) { let self = this; @@ -371,36 +466,76 @@ vorpal.command('search [language]', "Searches the repostitory for conten self.log(info("You have not set a language, using alfresco full text search syntax (AFTS).")) } - alfrescoJsApi.search.searchApi.search({ - "query": { - "query": args.query, - "language": args.language ? args.language : "afts" - } - }).then(function (data) { - printNodeList(data.list.entries) - }, function (error) { - self.log(error); - }).catch(() => { - }); - callback(); + let query: string; + try { + query = args.options.alias ? getAliasString(args.query) : args.query; + alfrescoJsApi.search.searchApi.search({ + "query": { + "query": query, + "language": args.language ? args.language : "afts" + } + }).then(function (data) { + printNodeList(data.list.entries); + cacheResults(data.list.entries); + }, function (error) { + self.log(error); + }).catch(() => { + + }).then(callback); + } catch (e) { + vorpal.log(e.message); + callback(); + } }); -function printNodeList(entries) { - var table = new AsciiTable(); - table.setHeading('nodeId', 'name', "type"); +function printNodeList(entries, parameters=['id', 'name', 'type']) { + let table = new AsciiTable(); + table.setHeading(parameters); + //clear the results history + vorpal.localStorage.removeItem('resultsHistory'); + let found = false; entries.forEach(item => { - table.addRow(item.entry.id, item.entry.name, item.entry.nodeType); + found = true; + let row = parameters.map( + param => { + return item.entry[param]; + } + ); + table.addRow(row); }); - vorpal.log(table.toString()); + if (found) { + vorpal.log(table.toString()); + } + else { + vorpal.log(`no results`) + } +} + +function cacheResults(entries, parameters=['id']) { + vorpal.localStorage.removeItem('resultsHistory'); + try{ + entries.forEach(item => { + //add the results to the history. + let resultsHistory = vorpal.localStorage.getItem('resultsHistory'); + parameters.forEach(param => { + vorpal.localStorage.setItem('resultsHistory', + JSON.stringify(resultsHistory ? + JSON.parse(resultsHistory).push(item.entry[param]) : [item.entry[param]])) + }) + }) + }catch (e) { + vorpal.localStorage.removeItem('resultsHistory'); + } +; } async function getParent(nodeRef) { vorpal.log("getting parent for nodeRef: " + nodeRef); - let _nodeRef = await getNodeRef(nodeRef); + let _nodeRef = await getNodeRefContext(nodeRef); return await alfrescoJsApi.core.childAssociationsApi.listParents(_nodeRef) .then(function (data) { - vorpal.log('API called successfully. ' + data.list.pagination.totalItems + ' parent(s) found.'); + vorpal.log('Getting parent.. API called successfully. ' + data.list.pagination.totalItems + ' parent(s) found.'); let element = data.list.entries[0].entry; return element.id; }).catch(data => { @@ -415,18 +550,21 @@ vorpal.command("change node [nodeRef]", "Change into a nodeRef") .autocomplete({data: nodeNameAutoCompletion}) .action(function (args, callback) { let self = this; - getNodeRef(args.nodeRef).then(nodeRef => { + getNodeRefContext(args.nodeRef).then(nodeRef => { updateCurrentNodeRef(nodeRef, callback); + }).catch(e => () => { self.log(e.message); callback(); }); }); -vorpal.command('clear', "Clears the current node context.") +vorpal.command('clear', "Clears the current node context and history.") .alias('cls') .action((args, callback) => { updateCurrentNodeRef("", callback); + vorpal.localStorage.removeItem('resultsHistory'); + callback(); }); @@ -441,80 +579,147 @@ function updateCurrentNodeRef(nodeRef, after) { ; } -vorpal.command('delete [nodeRefPattern]', 'Deletes a nodeRef matching a pattern') +vorpal.command('undo delete', "Undoes the last delete.") + .action((args, callback) => { + let lastDeleted = vorpal.localStorage.getItem('lastDeleted'); + if (lastDeleted) { + vorpal.log(info(`attempting to restore last deleted node: ${lastDeleted}`)) + alfrescoJsApi.nodes.restoreNode(lastDeleted).then( + vorpal.localStorage.removeItem('lastDeleted') + ).catch().then(callback); + } else { + vorpal.log(warning('There was no last deleted nodeRef available.')); + callback(); + } + }); + +function matchesPattern(entry, pattern) { + return minimatch(entry.entry.id, pattern) || minimatch(entry.entry.name, pattern); +} + +vorpal.command('delete [nodeRefPattern] [force]', 'Deletes a nodeRef matching a pattern') .alias('rm') + .option('-f, --force', "Force deletion (no prompt)") + .option('-p, --permanent', "Delete file permanently (Skip trashing)") .autocomplete({data: nodeNameAutoCompletion}) - .action((args, callback) => { + .action(function (args, callback) { + const self = this; + let deleteNode = (nodeRef) => { - vorpal.log(`Attempting to delete node: ${nodeRef}`) - alfrescoJsApi.core.nodesApi.deleteNode(nodeRef).then( + vorpal.log(`Attempting to delete node: ${nodeRef}`); + let permanent = args.options.permanent; + let deleteOp = alfrescoJsApi.core.nodesApi.deleteNode(nodeRef); + return deleteOp.then( () => { + if (permanent) { + //purge the deleted node + vorpal.log(info('purging deleted node..')); + return alfrescoJsApi.core.nodesApi.purgeDeletedNode(nodeRef); + } vorpal.ui.redraw.clear(); - vorpal.log(`Node ${nodeRef} successfully deleted.`) + vorpal.log(info('setting last deleted value...' + nodeRef)) + vorpal.localStorage.setItem('lastDeleted', nodeRef); + vorpal.log(`Node ${nodeRef} successfully deleted.`); } - ).catch(e => { + ).then().catch(e => { vorpal.ui.redraw.clear(); vorpal.log(`There was an error deleting node : ${nodeRef}, reason: ${e.message.briefSummary}`) }) }; - let op = () => { + let op = async () => { + //get all children + let f: any; + if (args.nodeRefPattern) { - //get all children - if (args.nodeRefPattern == "*") { - getNodeRef(args.nodeRef).then(nodeRef => { - alfrescoJsApi.core.nodesApi.getNodeChildren(nodeRef).then( - value => { - value.list.entries.forEach(entry => { + vorpal.log(info(`looking for children of the specified node with pattern: ${args.nodeRefPattern}`)) + f = getNodeRefContext(args.nodeRef, true).then(nodeRef => { + return alfrescoJsApi.core.nodesApi.getNodeChildren(nodeRef).then( + value => { + value.list.entries.forEach(entry => { + if (matchesPattern(entry, args.nodeRefPattern)) { + vorpal.log(warning(`deleting node: ${entry.entry.id}:${entry.entry.name}`)) deleteNode(entry.entry.id); - }); - callback(); - } - ) - }) + } + }); + } + ).then(callback) + }); + if (args.nodeRefPattern == "*") { + + } else { + //throw error or show there are no results for pattern. } } else { - if (!args.nodeRefPattern) { - getNodeRef(args.nodeRef).then(nodeRef => { - deleteNode(nodeRef); - callback(); - }) - } + f = getNodeRefContext(args.nodeRef, true) + .then(nodeRef => { + vorpal.log(info("deleting node...")) + return deleteNode(nodeRef); + }); } + + return f.catch(e => { + vorpal.log(error(e.message)) + return e; + }).then(message => { + // vorpal.log(message); + callback(); + }) }; - op(); + if (args.options.force) { + vorpal.log(warning('You are forcing deletion. The file will be deleted without confirmation.')) + op().then(callback); + } else { + return this.prompt({ + type: 'confirm', + name: 'continue', + default: false, + message: 'Do you wish to delete the node(s) specified. Continue?', + }, function (result) { + if (!result.continue) { + self.log('Operation cancelled.'); + callback(); + } else { + self.log('Deleting node(s)..'); + op().then(callback); + } + }); + } }); -vorpal.command('list children [nodeRef]', "List all children of a given folder.") +vorpal.command('list children [nodeRef] [pattern]', "List all children of a given folder.") .alias('ls') + .option('-p, --pattern', "Pattern for filtering.") .autocomplete({data: nodeNameAutoCompletion}) .action(function (args, callback) { let self = this; - try { - let list = async (nodeRef) => { - self.log(`listing children for nodeRef : ${nodeRef}`); - nodeRef = await getNodeRef(args.nodeRef); - await alfrescoJsApi.nodes.getNodeChildren(nodeRef).then(function (data) { - let count = data.list.pagination.count; - - if (count > 0) { - printNodeList(data.list.entries); - self.log('The number of children in this folder are ' + count); + let list = async (nodeRef) => { + self.log(`listing children for nodeRef : ${nodeRef}`); + await alfrescoJsApi.nodes.getNodeChildren(nodeRef).then(function (data) { + let count = data.list.pagination.count; + + if (count > 0) { + self.log('The total number of children in this folder are ' + count); + if (args.options.pattern) { + printNodeList(data.list.entries.filter(entry => { + return matchesPattern(entry, args.pattern); + })) } else { - self.log("No children found.") + printNodeList(data.list.entries); } - }, function (error) { - self.log('This node does not exist'); - }); - }; - - getNodeRef(args.nodeRef).then(nodeRef => list(nodeRef)).catch((error) => self.log(error.message)); + } else { + self.log("No children found.") + } + }, function (error) { + self.log('This node does not exist'); + }); + }; - } catch (e) { - self.log(e.message); - } - callback(); + getNodeRefContext(args.nodeRef) + .then(nodeRef => list(nodeRef)) + .catch((error) => self.log(error.message)) + .then(callback); }); vorpal.localStorage('alfresco-cli'); @@ -530,9 +735,14 @@ function getCurrentNodeRef() { return nodeRef; } -async function getNodeRef(nodeRef: string) { +async function getNodeRefContext(nodeRef: string, explicit = false): Promise { let storedNodeRef = getCurrentNodeRef(); + //check for connectivity. + if (!alfrescoJsApi.isLoggedIn()) { + throw new Error("Unable to connect to the repository, please login again."); + } + if (nodeRef === "..") { return await getParent(storedNodeRef); } @@ -543,38 +753,38 @@ async function getNodeRef(nodeRef: string) { if (nodeRef) { //look under children first, if not look up as a node. - try { - let node = await alfrescoJsApi.nodes.getNodeChildren(storedNodeRef).then(data => { - let nodeId = data.list.entries.filter(node => { - return (node.entry.name.toLowerCase().trim() === nodeRef.toLowerCase().trim()) - }).map(entry => { - let nodeId = entry.entry.id; - return nodeId - })[0]; - - if (nodeId) { - return nodeId; + return alfrescoJsApi.nodes.getNodeChildren(storedNodeRef).then(data => { + let nodeId = data.list.entries.filter(node => { + return (node.entry.name.toLowerCase().trim() === nodeRef.toLowerCase().trim()) + }).map(entry => { + return entry.entry.id + })[0]; + + if (nodeId) { + return nodeId; + } else { + throw new Error("Unable to find a node with the matching name."); + } + }).catch(() => { + return alfrescoJsApi.nodes.getNodeInfo(nodeRef).then(function (data) { + vorpal.log(info(`Validated node [id=${data.id}][name=${data.name}]`)); + return nodeRef; + }).catch((error) => { + if (explicit) { + return Promise.reject(new Error("Unable to find an exact node match for the specified operation.")); } else { - throw new Error("Unable to find a node with the matching name."); + vorpal.log(info('This node does not exist. Trying context...')); + return storedNodeRef; } - }).catch(() => { - return alfrescoJsApi.nodes.getNodeInfo(nodeRef).then(function (data) { - vorpal.log('name: ' + data.name); - return nodeRef; - }, function (error) { - vorpal.log('This node does not exist'); - throw new Error("This node does not exist. Reverting to the current node.") - }) - }); - if (node) { - return node; - } - } catch (e) { - return storedNodeRef; - } + }) + }) } - return storedNodeRef; + if (!explicit) { + return storedNodeRef; + } else { + throw new Error("Unable to find a matching nodeRef.") + } } async function getDelimiter() {