Skip to content

Commit

Permalink
Move error handling to its own module and don't colorize if it alread…
Browse files Browse the repository at this point in the history
…y has ansi
  • Loading branch information
ecooper committed Dec 11, 2024
1 parent b3b82eb commit f75bb2b
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 152 deletions.
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"esprima": "^4.0.1",
"fauna": "^2.4.0",
"faunadb": "^4.5.4",
"has-ansi": "^6.0.0",
"inquirer": "^12.0.0",
"luxon": "^3.5.0",
"open": "10.1.0",
Expand Down
47 changes: 2 additions & 45 deletions src/cli.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @ts-check

import { util } from "chai";
import chalk from "chalk";
import yargs from "yargs";

Expand All @@ -10,8 +9,8 @@ import queryCommand from "./commands/query.mjs";
import schemaCommand from "./commands/schema/schema.mjs";
import shellCommand from "./commands/shell.mjs";
import { buildCredentials } from "./lib/auth/credentials.mjs";
import { isUnknownError } from "./lib/command-helpers.mjs";
import { configParser } from "./lib/config/config.mjs";
import { handleParseYargsError } from "./lib/errors.mjs";
import {
applyLocalArg,
checkForUpdates,
Expand All @@ -26,8 +25,6 @@ export let container;
/** @type {import('yargs').Argv} */
export let builtYargs;

const BUG_REPORT_MESSAGE = `If you believe this is a bug, please report this issue on GitHub: https://github.com/fauna/fauna-shell/issues`;

/**
* @param {string|string[]} _argvInput - The command string provided by the user or test. Parsed by yargs into an argv object.
* @param {cliContainer} _container - A built and ready for use awilix container with registered injectables.
Expand All @@ -45,47 +42,7 @@ export async function run(_argvInput, _container) {
builtYargs = buildYargs(argvInput);
await parseYargs(builtYargs);
} catch (e) {
let subMessage = chalk.reset(
"Use 'fauna <command> --help' for more information about a command.",
);
let epilogue = "";

if (argvInput.length > 0) {
// If the error isn't one of our known errors, wrap it in a generic error message.
if (isUnknownError(e)) {
subMessage = chalk.red(
`An unexpected error occurred...\n\n${e.message}`,
);
epilogue = `\n${BUG_REPORT_MESSAGE}`;

logger.debug(`unknown error thrown: ${e.name}`, "error");
logger.debug(util.inspect(e, true, 2, false), "error");
} else {
// Otherwise, just use the error message
subMessage = chalk.red(e.message);
}
}

// If the error has a truthy hideHelp property, do not render the help text. Otherwise, just use the error message.
logger.stderr(
`${e.hideHelp ? "" : `${chalk.reset(await builtYargs.getHelp())}\n\n`}${subMessage}`,
);

if (epilogue) {
logger.stderr(chalk.red(epilogue));
}

// Log the stack if it exists
logger.fatal(e.stack, "error");
if (e.cause) {
logger.fatal(e.cause?.stack, "error");
}

// If the error has an exitCode property, use that. Otherwise, use 1.
container.resolve("errorHandler")(
e,
e.exitCode !== undefined ? e.exitCode : 1,
);
await handleParseYargsError(e, { argvInput, builtYargs, logger });
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/commands/query.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import { container } from "../cli.mjs";
import {
CommandError,
isUnknownError,
resolveFormat,
validateDatabaseOrSecret,
ValidationError,
yargsWithCommonConfigurableQueryOptions,
} from "../lib/command-helpers.mjs";
import {
CommandError,
isUnknownError,
ValidationError,
} from "../lib/errors.mjs";
import {
formatError,
formatQueryResponse,
Expand Down Expand Up @@ -83,10 +85,10 @@ async function queryCommand(argv) {
timeout,
typecheck,
apiVersion,
color,
raw,
performanceHints,
summary,
color,
raw,
} = argv;

// If we're writing to a file, don't colorize the output regardless of the user's preference
Expand Down Expand Up @@ -123,7 +125,7 @@ async function queryCommand(argv) {
if (argv.output) {
container.resolve("fs").writeFileSync(argv.output, output);
} else {
logger.stdout(output);
container.resolve("logger").stdout(output);
}

return results;
Expand Down
6 changes: 2 additions & 4 deletions src/commands/schema/abandon.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//@ts-check

import { container } from "../../cli.mjs";
import {
CommandError,
yargsWithCommonQueryOptions,
} from "../../lib/command-helpers.mjs";
import { yargsWithCommonQueryOptions } from "../../lib/command-helpers.mjs";
import { CommandError } from "../../lib/errors.mjs";
import { getSecret } from "../../lib/fauna-client.mjs";

async function doAbandon(argv) {
Expand Down
6 changes: 2 additions & 4 deletions src/commands/schema/commit.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//@ts-check

import { container } from "../../cli.mjs";
import {
CommandError,
yargsWithCommonQueryOptions,
} from "../../lib/command-helpers.mjs";
import { yargsWithCommonQueryOptions } from "../../lib/command-helpers.mjs";
import { CommandError } from "../../lib/errors.mjs";
import { getSecret } from "../../lib/fauna-client.mjs";

async function doCommit(argv) {
Expand Down
6 changes: 2 additions & 4 deletions src/commands/schema/diff.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import chalk from "chalk";

import { container } from "../../cli.mjs";
import {
ValidationError,
yargsWithCommonQueryOptions,
} from "../../lib/command-helpers.mjs";
import { yargsWithCommonQueryOptions } from "../../lib/command-helpers.mjs";
import { ValidationError } from "../../lib/errors.mjs";
import { getSecret } from "../../lib/fauna-client.mjs";
import { reformatFSL } from "../../lib/schema.mjs";
import { localSchemaOptions } from "./schema.mjs";
Expand Down
6 changes: 2 additions & 4 deletions src/commands/schema/status.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import chalk from "chalk";

import { container } from "../../cli.mjs";
import {
CommandError,
yargsWithCommonQueryOptions,
} from "../../lib/command-helpers.mjs";
import { yargsWithCommonQueryOptions } from "../../lib/command-helpers.mjs";
import { CommandError } from "../../lib/errors.mjs";
import { getSecret } from "../../lib/fauna-client.mjs";
import { reformatFSL } from "../../lib/schema.mjs";
import { localSchemaOptions } from "./schema.mjs";
Expand Down
2 changes: 1 addition & 1 deletion src/lib/auth/accountKeys.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { container } from "../../cli.mjs";
import { CommandError } from "../command-helpers.mjs";
import { CommandError } from "../errors.mjs";
import { FaunaAccountClient } from "../fauna-account-client.mjs";
import { AccountKeyStorage } from "../file-util.mjs";
import { InvalidCredsError } from "../misc.mjs";
Expand Down
2 changes: 1 addition & 1 deletion src/lib/auth/credentials.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { asValue, Lifetime } from "awilix";

import { container } from "../../cli.mjs";
import { ValidationError } from "../command-helpers.mjs";
import { ValidationError } from "../errors.mjs";
import { FaunaAccountClient } from "../fauna-account-client.mjs";
import { AccountKeys } from "./accountKeys.mjs";
import { DatabaseKeys } from "./databaseKeys.mjs";
Expand Down
2 changes: 1 addition & 1 deletion src/lib/auth/databaseKeys.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { container } from "../../cli.mjs";
import { CommandError } from "../command-helpers.mjs";
import { CommandError } from "../errors.mjs";
import { FaunaAccountClient } from "../fauna-account-client.mjs";
import { SecretKeyStorage } from "../file-util.mjs";

Expand Down
72 changes: 1 addition & 71 deletions src/lib/command-helpers.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ts-check

import { container } from "../cli.mjs";
import { ValidationError } from "./errors.mjs";
import { Format } from "./formatting/colorize.mjs";

const COMMON_OPTIONS = {
Expand Down Expand Up @@ -78,77 +79,6 @@ const COMMON_QUERY_OPTIONS = {
},
};

/**
* An error that is thrown by commands that is not a validation error, but
* a known error state that should be communicated to the user.
*/
export class CommandError extends Error {
/**
* @param {string} message
* @param {object} [opts]
* @param {number} [opts.exitCode]
* @param {boolean} [opts.hideHelp]
* @param {Error} [opts.cause]
*/
constructor(message, { exitCode = 1, hideHelp = true, cause } = {}) {
super(message);
this.exitCode = exitCode;
this.hideHelp = hideHelp;
this.cause = cause;
}
}

/**
* An error that is thrown when the user provides invalid input, but
* isn't caught until command execution.
*/
export class ValidationError extends CommandError {
/**
* @param {string} message
* @param {object} [opts]
* @param {number} [opts.exitCode]
* @param {boolean} [opts.hideHelp]
* @param {Error} [opts.cause]
*/
constructor(message, { exitCode = 1, hideHelp = false, cause } = {}) {
super(message, { exitCode, hideHelp, cause });
}
}

/**
* Returns true if the error is an error potentially thrown by yargs
* @param {Error} error
* @returns {boolean}
*/
function isYargsError(error) {
// Sometimes they are named YError. This seems to the case in middleware.
if (error.name === "YError") {
return true;
}

// Usage errors from yargs are thrown as plain old Error. The best
// you can do is check for the message.
if (
error.message &&
(error.message.startsWith("Unknown argument") ||
error.message.startsWith("Missing required argument") ||
error.message.startsWith("Unknown command"))
) {
return true;
}

return false;
}

/**
* Returns true if the error is not an error yargs or one we've thrown ourselves in a command
* @param {Error} error
* @returns {boolean}
*/
export function isUnknownError(error) {
return !isYargsError(error) && !(error instanceof CommandError);
}

export const resolveFormat = (argv) => {
const logger = container.resolve("logger");

Expand Down
2 changes: 1 addition & 1 deletion src/lib/config/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import yaml from "yaml";
import yargsParser from "yargs-parser";

import { container } from "../../cli.mjs";
import { ValidationError } from "../command-helpers.mjs";
import { ValidationError } from "../errors.mjs";

export const validDefaultConfigNames = [
"fauna.config.yaml",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/db.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//@ts-check

import { container } from "../cli.mjs";
import { CommandError } from "./command-helpers.mjs";
import { CommandError } from "./errors.mjs";
import { retryInvalidCredsOnce } from "./fauna-client.mjs";

function buildParamsString({ argv, params, path }) {
Expand Down
Loading

0 comments on commit f75bb2b

Please sign in to comment.