zapier-platform-cli
Version:
The CLI for managing integrations in Zapier Developer Platform.
276 lines (239 loc) • 7.08 kB
JavaScript
// could we explore https://www.npmjs.com/package/columnify
// to simplify the columns/tables? the | - decoration is big
const Table = require('cli-table3');
const colors = require('colors/safe');
const stringLength = require('string-length');
const _ = require('lodash');
const ora = require('ora');
const { CHECK_REF_DOC_LINK } = require('../constants');
const notUndef = (s) => String(s === undefined ? '' : s).trim();
const prettyJSONstringify = (obj) => JSON.stringify(obj, null, ' ');
// Convert rows from keys to column labels.
const rewriteLabels = (rows, columnDefs) => {
return rows.map((row) => {
const consumptionRow = {};
columnDefs.forEach((columnDef) => {
const [label, key, _default] = columnDef;
const val = _.get(row, key || label, _default);
consumptionRow[label] = notUndef(val);
});
return consumptionRow;
});
};
const makePlainSingle = (row) => {
return _.map(row, (value, key) => {
return (colors.grey('==') + ' ' + colors.bold(key) + '\n' + value).trim();
}).join('\n');
};
// An easier way to print rows for copy paste accessibility.
const makePlain = (rows, columnDefs) => {
return (
rewriteLabels(rows, columnDefs)
.map(makePlainSingle)
.join(colors.grey('\n\n - - - - - - - - - - - - - - - - - - \n\n')) + '\n'
);
};
const isTooWideForWindow = (str) => {
const widestRow = str.split('\n').reduce((coll, row) => {
if (stringLength(row) > coll) {
return stringLength(row);
} else {
return coll;
}
}, 0);
return widestRow > process.stdout.columns;
};
const ansiTrim = (s) =>
_.trim(s, [
'\r',
'\n',
' ',
// '\u001b[39m',
// '\u001b[90m',
]);
const TABLE_CHARS_DEFAULT = {
// 'top': '', 'top-mid': '', 'top-left': '', 'top-right': '',
// 'bottom': ' ', 'bottom-mid': ' ', 'bottom-left': ' ', 'bottom-right': ' '
};
const TABLE_CHARS_NONE = {
top: '',
'top-mid': '',
'top-left': '',
'top-right': '',
bottom: '',
'bottom-mid': '',
'bottom-left': '',
'bottom-right': '',
left: '',
'left-mid': '',
mid: '',
'mid-mid': '',
right: '',
'right-mid': '',
middle: ' ',
};
// Similar to makeTable, but prints the column headings in the left-hand column
// and the values in the right-hand column, in rows
const makeRowBasedTable = (rows, columnDefs, { includeIndex = true } = {}) => {
const tableOptions = {
chars: TABLE_CHARS_DEFAULT,
style: {
compact: true,
},
};
const table = new Table(tableOptions);
const maxLabelLength = _.reduce(
columnDefs,
(maxLength, columnDef) => {
if (columnDef[0] && stringLength(columnDef[0]) > maxLength) {
return stringLength(columnDef[0]);
}
return maxLength;
},
1,
);
const widthForValue = process.stdout.columns - maxLabelLength - 15; // The last bit accounts for some padding and borders
if (widthForValue < 1) {
return makePlain(rows, columnDefs); // There's not enough space to display the table
}
rows.forEach((row, index) => {
if (includeIndex) {
table.push([{ colSpan: 2, content: colors.grey(`= ${index + 1} =`) }]);
}
columnDefs.forEach((columnDef) => {
const consumptionRow = {};
const [label, key, _default] = columnDef;
let val = _.get(row, key || label, _default);
val = notUndef(val);
if (stringLength(val) > widthForValue) {
try {
val = prettyJSONstringify(JSON.parse(val));
} catch (err) {
// Wasn't JSON, so splice in newlines so that word wraping works properly
let rest = val;
val = '';
while (stringLength(rest) > 0) {
val += rest.slice(0, widthForValue);
if (val.indexOf('\n') === -1) {
val += '\n';
}
rest = rest.slice(widthForValue);
}
}
}
let colLabel = ' ' + colors.bold(label);
if (!includeIndex) {
colLabel = colors.bold(label) + ' ';
}
consumptionRow[colLabel] = val.trim();
table.push(consumptionRow);
});
if (index < rows.length - 1) {
table.push([{ colSpan: 2, content: ' ' }]);
}
});
const strTable = ansiTrim(table.toString());
if (isTooWideForWindow(strTable)) {
return makePlain(rows, columnDefs);
}
return strTable;
};
// Wraps the cli-table3 library. Rows is an array of objects, headers
// an ordered sub-array [[label, key, (optional_default)], ...].
const makeTable = (
rows,
columnDefs,
showHeaders = true,
hasBorder = true,
style = undefined,
) => {
const tableOptions = {
head: showHeaders ? columnDefs.map(([label]) => label) : undefined,
chars: hasBorder ? TABLE_CHARS_DEFAULT : TABLE_CHARS_NONE,
style: style ?? {
compact: true,
head: ['bold'],
},
};
const table = new Table(tableOptions);
rows.forEach((row) => {
const consumptionRow = [];
columnDefs.forEach((columnDef) => {
const [label, key, _default] = columnDef;
const val = _.get(row, key || label, _default);
consumptionRow.push(notUndef(val));
});
table.push(consumptionRow);
});
const strTable = ansiTrim(table.toString());
if (isTooWideForWindow(strTable)) {
return makeRowBasedTable(rows, columnDefs, { includeIndex: false });
}
return strTable;
};
const makeJSON = (rows, columnDefs) =>
prettyJSONstringify(rewriteLabels(rows, columnDefs));
const makeRawJSON = (rows) => prettyJSONstringify(rows);
const formatStyles = {
plain: makePlain,
json: makeJSON,
raw: makeRawJSON,
row: makeRowBasedTable,
table: makeTable,
};
// single global instance of the spinner
const spinner = ora();
const startSpinner = (msg) => {
spinner.start(msg);
};
const endSpinner = (success = true, message) => {
// only stop if it was started in the first place
if (!spinner.isSpinning) {
return;
}
if (success) {
spinner.succeed(message);
} else {
spinner.fail(message);
}
};
const flattenCheckResult = (checkResult) => {
const res = [];
for (const severity in checkResult) {
const results = checkResult[severity].results;
if (!results) {
continue;
}
const displaySeverity = checkResult[severity].display_label || severity;
for (const issueGroup of results) {
if (!issueGroup.violations) {
break;
}
const opType =
{
write: 'creates',
read: 'triggers',
auth: 'authentication',
}[issueGroup.type] || issueGroup.type;
for (const violation of issueGroup.violations) {
for (const result of violation.results) {
res.push({
category: displaySeverity,
method: `${opType}.${violation.type}`,
description: `${result.message} (${result.tag})`,
link: `${CHECK_REF_DOC_LINK}#${result.tag}`,
});
}
}
}
}
return res;
};
module.exports = {
endSpinner,
flattenCheckResult,
formatStyles,
makeTable, // exported for tests
prettyJSONstringify,
startSpinner,
};