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

Update formatting for errors and query info #563

Merged
merged 13 commits into from
Jan 17, 2025
1 change: 1 addition & 0 deletions src/commands/database/create.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ async function createDatabase(argv) {
faunaToCommandError({
err: e,
color: argv.color,
include: argv.include,
handler: (err) => {
if (err instanceof ServiceError && err.code === "constraint_failure") {
const cf = err.constraint_failures;
Expand Down
1 change: 1 addition & 0 deletions src/commands/database/delete.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ async function deleteDatabase(argv) {
faunaToCommandError({
err,
color: argv.color,
include: argv.include,
handler: (err) => {
if (err instanceof ServiceError && err.code === "document_not_found") {
throw new CommandError(
Expand Down
6 changes: 5 additions & 1 deletion src/commands/database/list.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ async function listDatabasesWithSecret(argv) {
});
return res.data;
} catch (e) {
return faunaToCommandError({ err: e, color: argv.color });
return faunaToCommandError({
err: e,
color: argv.color,
include: argv.include,
});
}
}

Expand Down
28 changes: 13 additions & 15 deletions src/commands/query.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,22 @@ async function queryCommand(argv) {
validateDatabaseOrSecret(argv);
validate(argv);

const secret = await getSecret(argv);
const {
url,
timeout,
typecheck,
apiVersion,
performanceHints,
color,
include,
} = argv;

// resolve the input
const expression = resolveInput(argv);

// get the query handler and run the query
try {
const secret = await getSecret(argv);
const {
url,
timeout,
typecheck,
apiVersion,
performanceHints,
color,
include,
} = argv;

// If we're writing to a file, don't colorize the output regardless of the user's preference
const useColor = argv.output || !isTTY() ? false : color;

Expand All @@ -115,9 +115,7 @@ async function queryCommand(argv) {
color: useColor,
});

// If any query info should be displayed, print to stderr.
// This is only supported in v10.
if (include.length > 0 && apiVersion === "10") {
if (include.length > 0) {
const queryInfo = formatQueryInfo(results, {
apiVersion,
color: useColor,
Expand Down Expand Up @@ -147,7 +145,7 @@ async function queryCommand(argv) {
}

const { apiVersion, color } = argv;
throw new CommandError(formatError(err, { apiVersion, color }), {
throw new CommandError(formatError(err, { apiVersion, color, include }), {
cause: err,
});
}
Expand Down
11 changes: 7 additions & 4 deletions src/commands/shell.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import * as esprima from "esprima";
import { container } from "../config/container.mjs";
import { formatQueryResponse, getSecret } from "../lib/fauna-client.mjs";
import { clearHistoryStorage, initHistoryStorage } from "../lib/file-util.mjs";
import { validateDatabaseOrSecret } from "../lib/middleware.mjs";
import {
resolveIncludeOptions,
validateDatabaseOrSecret,
} from "../lib/middleware.mjs";
import {
ACCOUNT_OPTIONS,
CORE_OPTIONS,
Expand Down Expand Up @@ -190,8 +193,7 @@ async function buildCustomEval(argv) {
});

// If any query info should be displayed, print to stderr.
// This is only supported in v10.
if (include.length > 0 && apiVersion === "10") {
if (include.length > 0) {
const queryInfo = formatQueryInfo(res, {
apiVersion,
color,
Expand All @@ -202,7 +204,7 @@ async function buildCustomEval(argv) {
}
}
} catch (err) {
logger.stderr(formatError(err, { apiVersion, color }));
logger.stderr(formatError(err, { apiVersion, color, include }));
return cb(null);
}

Expand All @@ -227,6 +229,7 @@ function buildShellCommand(yargs) {
.options(DATABASE_PATH_OPTIONS)
.options(CORE_OPTIONS)
.options(QUERY_OPTIONS)
.middleware(resolveIncludeOptions)
.example([
[
"$0 shell --database us/my_db",
Expand Down
2 changes: 2 additions & 0 deletions src/config/setup-test-container.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,15 @@ export function setupTestContainer() {
runQuery: stub(),
runQueryFromString: stub(),
formatQueryResponse: faunaClientV10.formatQueryResponse,
formatQueryInfo: faunaClientV10.formatQueryInfo,
formatError: faunaClientV10.formatError,
}),
faunaClientV4: awilix.asValue({
getClient: stub(),
runQuery: stub(),
runQueryFromString: stub(),
formatQueryResponse: faunaClientV4.formatQueryResponse,
formatQueryInfo: faunaClientV4.formatQueryInfo,
formatError: faunaClientV4.formatError,
}),
};
Expand Down
118 changes: 16 additions & 102 deletions src/lib/fauna-client.mjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
//@ts-check

import stripAnsi from "strip-ansi";

import { container } from "../config/container.mjs";
import { isUnknownError } from "./errors.mjs";
import { faunaToCommandError } from "./fauna.mjs";
import { faunadbToCommandError } from "./faunadb.mjs";
import { colorize, Format } from "./formatting/colorize.mjs";

/**
* Regex to match the FQL diagnostic line.
* @type {RegExp}
*/
export const FQL_DIAGNOSTIC_REGEX = /^(\s{2,}\|)|(\s*\d{1,}\s\|)/;
import { Format } from "./formatting/colorize.mjs";

/**
* Gets a secret for the current credentials.
Expand Down Expand Up @@ -103,16 +95,17 @@ export const runQueryFromString = (expression, argv) => {
* @param {object} opts
* @param {string} opts.apiVersion - The API version
* @param {boolean} opts.color - Whether to colorize the error
* @param {string[]} opts.include - The query info fields to include
* @returns {string}
*/
export const formatError = (err, { apiVersion, color }) => {
export const formatError = (err, { apiVersion, color, include }) => {
const faunaV4 = container.resolve("faunaClientV4");
const faunaV10 = container.resolve("faunaClientV10");

if (apiVersion === "4") {
return faunaV4.formatError(err, { color });
return faunaV4.formatError(err, { color, include });
} else {
return faunaV10.formatError(err, { color });
return faunaV10.formatError(err, { color, include });
}
};

Expand All @@ -132,11 +125,11 @@ export const isQueryable = async (argv) => {
throw err;
}

const { color } = argv;
const { color, include } = argv;
if (argv.apiVersion === "4") {
faunadbToCommandError({ err, color });
faunadbToCommandError({ err, color, include });
} else {
faunaToCommandError({ err, color });
faunaToCommandError({ err, color, include });
}
}

Expand All @@ -153,67 +146,15 @@ export const isQueryable = async (argv) => {
* @returns {string}
*/
export const formatQueryResponse = (res, { apiVersion, color, format }) => {
const faunaV4 = container.resolve("faunaClientV4");
const faunaV10 = container.resolve("faunaClientV10");

if (apiVersion === "4") {
const faunaV4 = container.resolve("faunaClientV4");
return faunaV4.formatQueryResponse(res, { format, color });
} else {
const faunaV10 = container.resolve("faunaClientV10");
return faunaV10.formatQueryResponse(res, { format, color });
}
};

/**
* Formats a summary of a query from a fauna
* @param {string} summary - The summary of the query
* @returns {string}
*/
export const formatQuerySummary = (summary) => {
if (!summary || typeof summary !== "string") {
return "";
}

try {
const lines = summary.split("\n").map((line) => {
if (!line.match(FQL_DIAGNOSTIC_REGEX)) {
return line;
}
return colorize(line, { format: Format.FQL });
});
return lines.join("\n");
} catch (err) {
const logger = container.resolve("logger");
logger.debug(`Unable to parse performance hint: ${err}`);
return summary;
}
};

const getQueryInfoValue = (response, field) => {
switch (field) {
case "txnTs":
return response.txn_ts;
case "schemaVersion":
return response.schema_version?.toString();
case "summary":
return response.summary;
case "queryTags":
return response.query_tags;
case "stats":
return response.stats;
default:
return undefined;
}
};

const getIncludedQueryInfo = (response, include) => {
const queryInfo = {};
include.forEach((field) => {
const value = getQueryInfoValue(response, field);
if (value) queryInfo[field] = value;
});
return queryInfo;
};

/**
*
* @param {object} response - The v4 or v10 query response with query info
Expand All @@ -224,38 +165,11 @@ const getIncludedQueryInfo = (response, include) => {
* @returns
*/
export const formatQueryInfo = (response, { apiVersion, color, include }) => {
if (apiVersion === "4" && include.includes("stats")) {
/** @type {import("faunadb").MetricsResponse} */
const metricsResponse = response;
const colorized = colorize(
{ metrics: metricsResponse.metrics },
{ color, format: Format.YAML },
);

return `${colorized}\n`;
} else if (apiVersion === "10") {
const queryInfoToDisplay = getIncludedQueryInfo(response, include);

if (Object.keys(queryInfoToDisplay).length === 0) return "";

// We colorize the entire query info object as YAML, but then need to
// colorize the diagnostic lines individually. To simplify this, we
// strip the ansi when we're checking if the line is a diagnostic line.
const colorized = colorize(queryInfoToDisplay, {
color,
format: Format.YAML,
})
.split("\n")
.map((line) => {
if (!stripAnsi(line).match(FQL_DIAGNOSTIC_REGEX)) {
return line;
}
return colorize(line, { format: Format.FQL });
})
.join("\n");

return `${colorized}\n`;
if (apiVersion === "4") {
const faunaV4 = container.resolve("faunaClientV4");
return faunaV4.formatQueryInfo(response, { color, include });
} else {
const faunaV10 = container.resolve("faunaClientV10");
return faunaV10.formatQueryInfo(response, { color, include });
}

return "";
};
Loading
Loading