Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render errors as-is if they already have ansi characters #506

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -4,10 +4,8 @@ import chalk from "chalk";
import path from "path";

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 Expand Up @@ -82,7 +82,7 @@
async getOrRefreshKey() {
if (this.keySource === "credentials-file") {
const key = this.keyStore.get();
// TODO: track ttl for account and refresh keys

Check warning on line 85 in src/lib/auth/accountKeys.mjs

View workflow job for this annotation

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: track ttl for account and refresh...'
if (!key) {
this.logger.debug(
"Found account key, but it is expired. Refreshing...",
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 Expand Up @@ -63,7 +63,7 @@
this.accountKeys.keyStore.save({
accountKey,
refreshToken,
// TODO: set expiration

Check warning on line 66 in src/lib/auth/credentials.mjs

View workflow job for this annotation

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: set expiration'
});
this.accountKeys.key = accountKey;
}
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
Loading