Table output, more fields, and --sort for './oputil.js user list'
This commit is contained in:
parent
5b1c11b1bc
commit
b971876795
|
@ -69,6 +69,18 @@ Actions:
|
|||
info arguments:
|
||||
--security Include security information in output
|
||||
|
||||
list arguments:
|
||||
--sort SORT_BY Specify field to sort by
|
||||
|
||||
Valid SORT_BY values:
|
||||
id : User ID
|
||||
username : Username
|
||||
realname : Real name
|
||||
status : Account status
|
||||
created : Account creation date
|
||||
lastlogin : Last login timestamp
|
||||
logins : Login count
|
||||
|
||||
2fa-otp arguments:
|
||||
--qr-type TYPE Specify QR code type
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ const async = require('async');
|
|||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const fs = require('fs-extra');
|
||||
const Table = require('easy-table');
|
||||
|
||||
exports.handleUserCommand = handleUserCommand;
|
||||
|
||||
|
@ -340,7 +341,7 @@ function showUserInfo(user) {
|
|||
|
||||
const statusDesc = () => {
|
||||
const status = user.properties[UserProps.AccountStatus];
|
||||
return _.invert(User.AccountStatus)[status] || 'unknown';
|
||||
return _.invert(User.AccountStatus)[status] || 'N/A';
|
||||
};
|
||||
|
||||
const created = () => {
|
||||
|
@ -508,7 +509,6 @@ function listUsers() {
|
|||
// :TODO: --created-since SPEC and --last-called SPEC
|
||||
// --created-since SPEC
|
||||
// SPEC can be TIMESTAMP or e.g. "-1hour" or "-90days"
|
||||
// :TODO: --sort name|id
|
||||
let listWhat;
|
||||
if (argv._.length > 2) {
|
||||
listWhat = argv._[argv._.length - 1];
|
||||
|
@ -516,6 +516,8 @@ function listUsers() {
|
|||
listWhat = 'all';
|
||||
}
|
||||
|
||||
const sortBy = (argv.sort || 'id').toLowerCase();
|
||||
|
||||
const User = require('../../core/user');
|
||||
if (!['all'].concat(Object.keys(User.AccountStatus)).includes(listWhat)) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
|
@ -527,7 +529,13 @@ function listUsers() {
|
|||
const UserProps = require('../../core/user_property');
|
||||
|
||||
const userListOpts = {
|
||||
properties: [UserProps.AccountStatus],
|
||||
properties: [
|
||||
UserProps.RealName,
|
||||
UserProps.AccountStatus,
|
||||
UserProps.AccountCreated,
|
||||
UserProps.LastLoginTs,
|
||||
UserProps.LoginCount,
|
||||
],
|
||||
};
|
||||
|
||||
User.getUserList(userListOpts, (err, userList) => {
|
||||
|
@ -550,10 +558,94 @@ function listUsers() {
|
|||
});
|
||||
},
|
||||
(userList, callback) => {
|
||||
// default sort: by ID
|
||||
const sortById = (left, right) => {
|
||||
return left.userId - right.userId;
|
||||
};
|
||||
|
||||
const sortByLogin = prop => (left, right) => {
|
||||
return parseInt(right[prop]) - parseInt(left[prop]);
|
||||
};
|
||||
|
||||
const sortByString = prop => (left, right) => {
|
||||
return left[prop].localeCompare(right[prop], {
|
||||
sensitivity: false,
|
||||
numeric: true,
|
||||
});
|
||||
};
|
||||
|
||||
const sortByTimestamp = prop => (left, right) => {
|
||||
return moment(left[prop]) - moment(right[prop]);
|
||||
};
|
||||
|
||||
let sorter;
|
||||
switch (sortBy) {
|
||||
case 'username':
|
||||
sorter = sortByString('userName');
|
||||
break;
|
||||
case 'realname':
|
||||
sorter = sortByString(UserProps.RealName);
|
||||
break;
|
||||
case 'status':
|
||||
sorter = sortByString(UserProps.AccountStatus);
|
||||
break;
|
||||
case 'created':
|
||||
sorter = sortByTimestamp(UserProps.AccountCreated);
|
||||
break;
|
||||
case 'lastlogin':
|
||||
sorter = sortByTimestamp(UserProps.LastLoginTs);
|
||||
break;
|
||||
case 'logins':
|
||||
sorter = sortByLogin(UserProps.LoginCount);
|
||||
break;
|
||||
|
||||
case 'id':
|
||||
default:
|
||||
sorter = sortById;
|
||||
break;
|
||||
}
|
||||
|
||||
userList.sort(sorter);
|
||||
|
||||
const StatusNames = _.invert(User.AccountStatus);
|
||||
|
||||
const propOrNA = (user, prop) => {
|
||||
return user[prop] || 'N/A';
|
||||
};
|
||||
|
||||
const timestampOrNA = (user, prop, format) => {
|
||||
let ts = user[prop];
|
||||
return ts ? moment(ts).format(format) : 'N/A';
|
||||
};
|
||||
|
||||
const makeAccountStatus = status => {
|
||||
return StatusNames[status] || 'N/A';
|
||||
};
|
||||
|
||||
const table = new Table();
|
||||
userList.forEach(user => {
|
||||
console.info(`${user.userId}: ${user.userName}`);
|
||||
table.cell('ID', user.userId);
|
||||
table.cell('Username', user.userName);
|
||||
table.cell('Real Name', user[UserProps.RealName]);
|
||||
table.cell(
|
||||
'Status',
|
||||
makeAccountStatus(user[UserProps.AccountStatus])
|
||||
);
|
||||
table.cell(
|
||||
'Created',
|
||||
timestampOrNA(user, UserProps.AccountCreated, 'YYYY-MM-DD')
|
||||
);
|
||||
table.cell(
|
||||
'Last Login',
|
||||
timestampOrNA(user, UserProps.LastLoginTs, 'YYYY-MM-DD HH::mm')
|
||||
);
|
||||
table.cell('Logins', propOrNA(user, UserProps.LoginCount));
|
||||
|
||||
table.newRow();
|
||||
});
|
||||
|
||||
console.info(table.toString());
|
||||
|
||||
return callback(null);
|
||||
},
|
||||
],
|
||||
|
|
|
@ -59,7 +59,8 @@
|
|||
"uuid": "8.3.2",
|
||||
"uuid-parse": "1.1.0",
|
||||
"ws": "7.4.3",
|
||||
"yazl": "^2.5.1"
|
||||
"yazl": "^2.5.1",
|
||||
"easy-table": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.21.0",
|
||||
|
|
|
@ -602,6 +602,15 @@ dtrace-provider@~0.8:
|
|||
dependencies:
|
||||
nan "^2.10.0"
|
||||
|
||||
easy-table@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.2.0.tgz#ba9225d7138fee307bfd4f0b5bc3c04bdc7c54eb"
|
||||
integrity sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
optionalDependencies:
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
|
|
Loading…
Reference in New Issue