vtex
Version:
The platform for e-commerce apps
293 lines (292 loc) • 12.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.onError = void 0;
const tslib_1 = require("tslib");
const axios_1 = tslib_1.__importDefault(require("axios"));
const os_1 = tslib_1.__importDefault(require("os"));
const plugin_help_1 = tslib_1.__importDefault(require("@oclif/plugin-help"));
const errors_1 = require("@oclif/errors");
const featureFlag_1 = require("../../api/modules/featureFlag");
const env_1 = require("../../api/env");
const CLIPreTasks_1 = require("../../CLIPreTasks/CLIPreTasks");
const TelemetryCollector_1 = require("../../lib/telemetry/TelemetryCollector");
const hrTimeToMs_1 = require("../../lib/utils/hrTimeToMs");
const update_1 = require("../../update");
const logger_1 = tslib_1.__importDefault(require("../../api/logger"));
const pkg = tslib_1.__importStar(require("../../../package.json"));
const conf = tslib_1.__importStar(require("../../api/conf"));
const nps_1 = require("../../nps");
const login_1 = tslib_1.__importDefault(require("../../modules/auth/login"));
const SessionManager_1 = require("../../api/session/SessionManager");
const errors_2 = require("../../api/error/errors");
const ErrorReport_1 = require("../../api/error/ErrorReport");
const fse = tslib_1.__importStar(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const ramda_1 = require("ramda");
const utils_1 = require("./utils");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { initTimeStartTime } = require('../../../bin/run');
let loginPending = false;
const logToolbeltVersion = () => {
logger_1.default.debug(`Toolbelt version: ${pkg.version}`);
};
const checkLogin = async (command) => {
/**
* Commands for which previous login is not necessary. There's some exceptions:
* - link: It's necessary to be logged in, but there's some login logic there before running the link per se
*/
const allowedList = [
undefined,
'config',
'login',
'logout',
'switch',
'whoami',
'init',
'-v',
'--version',
'release',
'link',
];
if (!SessionManager_1.SessionManager.getSingleton().checkValidCredentials() && allowedList.indexOf(command) === -1) {
logger_1.default.debug('Requesting login before command:', command);
await login_1.default({});
}
};
const createSymlink = async (options) => {
try {
await fse.symlink(options.config.root, path_1.default.join(options.config.root, 'node_modules', 'vtex'));
}
catch (symLinkErr) {
if (symLinkErr.code === 'EEXIST') {
logger_1.default.error(`Symbolic link already exist, there is another error that couldn't be solved`);
}
else {
logger_1.default.error('Failed to create symbolic link. Please run this command on Administrator mode');
}
process.exit(1);
}
};
const checkAndFixSymlink = async (options) => {
try {
require('vtex');
}
catch (requireErr) {
if (requireErr.code === 'MODULE_NOT_FOUND') {
logger_1.default.error('Import VTEX error, trying to autofix...');
await createSymlink(options);
logger_1.default.info('Problem solved. Please, run the command again');
}
else {
logger_1.default.error('Unexpected behaviour with vtex package');
}
process.exit(1);
}
logger_1.default.debug('Import VTEX OK');
};
const main = async (options, calculateInitTime) => {
const cliPreTasksStart = process.hrtime();
CLIPreTasks_1.CLIPreTasks.getCLIPreTasks(pkg).runTasks(options.id);
TelemetryCollector_1.TelemetryCollector.getCollector().registerMetric({
command: 'not-applicable',
["cliPreTasks:latency" /* CLI_PRE_TASKS_LATENCY */]: hrTimeToMs_1.hrTimeToMs(process.hrtime(cliPreTasksStart)),
});
// Show update notification if newer version is available
update_1.updateNotify();
const args = process.argv.slice(2);
conf.saveEnvironment(conf.Environment.Production); // Just to be backwards compatible with who used staging previously
logToolbeltVersion();
logger_1.default.debug('node %s - %s %s', process.version, os_1.default.platform(), os_1.default.release());
logger_1.default.debug(args);
await checkLogin(options.id);
await checkAndFixSymlink(options);
await nps_1.checkAndOpenNPSLink();
if (calculateInitTime) {
const initTime = process.hrtime(initTimeStartTime);
const initTimeMetric = {
command: options.id,
["startTime" /* START_TIME */]: hrTimeToMs_1.hrTimeToMs(initTime),
};
TelemetryCollector_1.TelemetryCollector.getCollector().registerMetric(initTimeMetric);
}
};
exports.onError = async (e) => {
var _a, _b, _c, _d, _e, _f, _g;
const status = (_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status;
const statusText = (_b = e === null || e === void 0 ? void 0 : e.response) === null || _b === void 0 ? void 0 : _b.statusText;
const headers = (_c = e === null || e === void 0 ? void 0 : e.response) === null || _c === void 0 ? void 0 : _c.headers;
const data = (_d = e === null || e === void 0 ? void 0 : e.response) === null || _d === void 0 ? void 0 : _d.data;
const code = (e === null || e === void 0 ? void 0 : e.code) || null;
if (headers) {
logger_1.default.debug('Failed request headers:', headers);
}
if (status === 401) {
if (!loginPending) {
logger_1.default.error('There was an authentication error. Please login again');
// Try to login and re-issue the command.
loginPending = true;
login_1.default({}).then(() => {
main();
}); // TODO: catch with different handler for second error
}
return; // Prevent multiple login attempts
}
if (status) {
if (status >= 400) {
const message = data ? data.message : null;
const source = e.config.url;
logger_1.default.error('API:', status, statusText);
logger_1.default.error('Source:', source);
if ((_e = e.config) === null || _e === void 0 ? void 0 : _e.method) {
logger_1.default.error('Method:', e.config.method);
}
if (message) {
logger_1.default.error('Message:', message);
logger_1.default.debug('Raw error:', data);
}
else {
logger_1.default.error('Raw error:', {
data,
source,
});
}
}
else {
logger_1.default.error('Oops! There was an unexpected error:');
logger_1.default.error(e.read ? e.read().toString('utf8') : data);
}
}
else if (code) {
switch (code) {
case 'ENOTFOUND':
logger_1.default.error('Connection failure :(');
logger_1.default.error('Please check your internet');
break;
case 'EAI_AGAIN':
logger_1.default.error('A temporary failure in name resolution occurred :(');
break;
default:
logger_1.default.error('Unhandled exception');
logger_1.default.error('Please report the issue in https://github.com/vtex/toolbelt/issues');
if (((_f = e.config) === null || _f === void 0 ? void 0 : _f.url) && ((_g = e.config) === null || _g === void 0 ? void 0 : _g.method)) {
logger_1.default.error(`${e.config.method} ${e.config.url}`);
}
logger_1.default.debug(e);
}
}
else if (ErrorReport_1.ErrorReport.isFlowIssue(e)) {
if (e.message && e.message !== '') {
logger_1.default.error(e.message);
}
}
else if (e instanceof errors_2.SSEConnectionError) {
logger_1.default.error(e.message);
}
else {
logger_1.default.error('Unhandled exception');
logger_1.default.error('Please report the issue in https://github.com/vtex/toolbelt/issues');
logger_1.default.error('Raw error: ', e);
}
process.removeListener('unhandledRejection', exports.onError);
const errorReport = TelemetryCollector_1.TelemetryCollector.getCollector().registerError(e);
if (!ErrorReport_1.ErrorReport.isFlowIssue(e)) {
logger_1.default.error(`ErrorID: ${errorReport.metadata.errorId}`);
}
process.exit(1);
};
async function default_1(options) {
// overwrite Help#showCommandHelp to customize help formating
plugin_help_1.default.prototype.showHelp = function showHelp(_argv) {
const sortedTopics = () => {
let { topics } = this.config;
topics = topics.filter(t => this.opts.all || !t.hidden);
topics = ramda_1.sortBy((t) => t.name, topics);
topics = ramda_1.uniqBy((t) => t.name, topics);
return topics;
};
const showTopicHelp = (topic) => {
const { name } = topic;
const depth = name.split(':').length;
const subTopics = sortedTopics().filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1);
console.log(this.topic(topic));
if (subTopics.length > 0) {
console.log(this.topics(subTopics));
console.log('');
}
};
const showCommandHelp = (command) => {
const name = command.id;
const depth = name.split(':').length;
const subTopics = sortedTopics().filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1);
const title = command.description && this.render(command.description).split('\n')[0];
if (title)
console.log(`${title}\n`);
console.log(this.command(command));
console.log('');
if (subTopics.length > 0) {
console.log(this.topics(subTopics));
console.log('');
}
};
const subject = utils_1.getHelpSubject(_argv);
if (subject) {
const command = this.config.findCommand(subject);
if (command) {
showCommandHelp(command);
return;
}
const topic = this.config.findTopic(subject);
if (topic) {
showTopicHelp(topic);
return;
}
errors_1.error(`command ${subject} not found`);
}
const commandsGroup = featureFlag_1.FeatureFlag.getSingleton().getFeatureFlagInfo('COMMANDS_GROUP');
const commandsId = featureFlag_1.FeatureFlag.getSingleton().getFeatureFlagInfo('COMMANDS_GROUP_ID');
const commandsGroupLength = Object.keys(commandsId).length;
const commands = this.config.commands
.filter(c => !c.id.includes(':'))
.map(c => {
return { name: c.id, description: c.description };
});
const topics = this.config.topics
.filter(t => !t.name.includes(':'))
.map(c => {
return { name: c.name, description: c.description };
});
const allCommands = commands.concat(topics);
const groups = Object.keys(commandsId).map(_ => []);
const cachedObject = new Map();
allCommands.forEach((command) => {
if (cachedObject.has(command.name))
return;
cachedObject.set(command.name, true);
const commandGroupId = commandsGroup[command.name];
if (commandGroupId) {
groups[commandGroupId].push(command);
}
else {
groups[commandsGroupLength - 1].push(command);
}
});
const renderedCommands = utils_1.renderCommands(commandsId, groups, {
render: this.render,
opts: this.opts,
config: this.config,
});
console.log(renderedCommands);
};
axios_1.default.interceptors.request.use(config => {
if (env_1.envCookies()) {
config.headers.Cookie = `${env_1.envCookies()}; ${config.headers.Cookie || ''}`;
}
return config;
});
process.on('unhandledRejection', exports.onError);
process.on('exit', () => {
TelemetryCollector_1.TelemetryCollector.getCollector().flush();
});
await main(options, true);
}
exports.default = default_1;