UNPKG

vtex

Version:

The platform for e-commerce apps

282 lines (281 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.onError = exports.checkLogin = 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 globalMetrics_1 = require("../../api/metrics/globalMetrics"); 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, argv) => { // Help invocations only render static text — never require login for them. if ((0, utils_1.isHelpInvocation)(command, argv)) { return; } /** * 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 (0, login_1.default)({}); } }; exports.checkLogin = checkLogin; 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) => { var _a; const cliPreTasksStart = process.hrtime(); CLIPreTasks_1.CLIPreTasks.getCLIPreTasks(pkg).runTasks(options.id); TelemetryCollector_1.TelemetryCollector.getCollector().registerMetric({ command: 'not-applicable', ["cliPreTasks:latency" /* MetricNames.CLI_PRE_TASKS_LATENCY */]: (0, hrTimeToMs_1.hrTimeToMs)(process.hrtime(cliPreTasksStart)), }); // Show update notification if newer version is available (0, 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 (0, exports.checkLogin)(options.id, (_a = options.argv) !== null && _a !== void 0 ? _a : []); await checkAndFixSymlink(options); await (0, nps_1.checkAndOpenNPSLink)(); if (calculateInitTime) { const initTime = process.hrtime(initTimeStartTime); const initTimeMetric = { command: options.id, ["startTime" /* MetricNames.START_TIME */]: (0, hrTimeToMs_1.hrTimeToMs)(initTime), }; TelemetryCollector_1.TelemetryCollector.getCollector().registerMetric(initTimeMetric); } }; const 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; (0, 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); }; exports.onError = onError; async function default_1(options) { /** * Plugins may bundle their own copy of @vtex/api and build clients without going * through Toolbelt's IOClientFactory (which sets this up on its own). `global.metrics` * is process-wide and `setupGlobalMetrics` is idempotent, so doing it here as well * covers every command — including plugin commands — at no cost. */ (0, globalMetrics_1.setupGlobalMetrics)(); // 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 = (0, ramda_1.sortBy)((t) => t.name, topics); topics = (0, 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 = (0, 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; } (0, errors_1.error)(`command ${subject} not found`); } const allCommands = (0, utils_1.collectCommands)(this.config.commands, this.config.topics); const { commandsId, groups } = (0, utils_1.buildCommandGroups)(featureFlag_1.FeatureFlag.getSingleton().getFeatureFlagInfo('COMMANDS_GROUP'), featureFlag_1.FeatureFlag.getSingleton().getFeatureFlagInfo('COMMANDS_GROUP_ID'), allCommands); const renderedCommands = (0, 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 ((0, env_1.envCookies)()) { config.headers.Cookie = `${(0, 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;