Skip to content

Commit 36461e2

Browse files
authored
Update formatting for errors and query info (#563)
* refactor formatQueryInfo into fauna module * pipe include option through to error formatting * update v4 and v10 error responses * revert dep on fauna * fix headers and existing tests * use includes middleware in shell * enable query info for v4 queries * add tests and fix linting * tweak args * formatting
1 parent 68f87fe commit 36461e2

15 files changed

+441
-203
lines changed

src/commands/database/create.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ async function createDatabase(argv) {
4747
faunaToCommandError({
4848
err: e,
4949
color: argv.color,
50+
include: argv.include,
5051
handler: (err) => {
5152
if (err instanceof ServiceError && err.code === "constraint_failure") {
5253
const cf = err.constraint_failures;

src/commands/database/delete.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ async function deleteDatabase(argv) {
3333
faunaToCommandError({
3434
err,
3535
color: argv.color,
36+
include: argv.include,
3637
handler: (err) => {
3738
if (err instanceof ServiceError && err.code === "document_not_found") {
3839
throw new CommandError(

src/commands/database/list.mjs

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ async function listDatabasesWithSecret(argv) {
3030
});
3131
return res.data;
3232
} catch (e) {
33-
return faunaToCommandError({ err: e, color: argv.color });
33+
return faunaToCommandError({
34+
err: e,
35+
color: argv.color,
36+
include: argv.include,
37+
});
3438
}
3539
}
3640

src/commands/query.mjs

+13-15
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,22 @@ async function queryCommand(argv) {
8282
validateDatabaseOrSecret(argv);
8383
validate(argv);
8484

85+
const secret = await getSecret(argv);
86+
const {
87+
url,
88+
timeout,
89+
typecheck,
90+
apiVersion,
91+
performanceHints,
92+
color,
93+
include,
94+
} = argv;
95+
8596
// resolve the input
8697
const expression = resolveInput(argv);
8798

8899
// get the query handler and run the query
89100
try {
90-
const secret = await getSecret(argv);
91-
const {
92-
url,
93-
timeout,
94-
typecheck,
95-
apiVersion,
96-
performanceHints,
97-
color,
98-
include,
99-
} = argv;
100-
101101
// If we're writing to a file, don't colorize the output regardless of the user's preference
102102
const useColor = argv.output || !isTTY() ? false : color;
103103

@@ -115,9 +115,7 @@ async function queryCommand(argv) {
115115
color: useColor,
116116
});
117117

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

149147
const { apiVersion, color } = argv;
150-
throw new CommandError(formatError(err, { apiVersion, color }), {
148+
throw new CommandError(formatError(err, { apiVersion, color, include }), {
151149
cause: err,
152150
});
153151
}

src/commands/shell.mjs

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import * as esprima from "esprima";
88
import { container } from "../config/container.mjs";
99
import { formatQueryResponse, getSecret } from "../lib/fauna-client.mjs";
1010
import { clearHistoryStorage, initHistoryStorage } from "../lib/file-util.mjs";
11-
import { validateDatabaseOrSecret } from "../lib/middleware.mjs";
11+
import {
12+
resolveIncludeOptions,
13+
validateDatabaseOrSecret,
14+
} from "../lib/middleware.mjs";
1215
import {
1316
ACCOUNT_OPTIONS,
1417
CORE_OPTIONS,
@@ -190,8 +193,7 @@ async function buildCustomEval(argv) {
190193
});
191194

192195
// If any query info should be displayed, print to stderr.
193-
// This is only supported in v10.
194-
if (include.length > 0 && apiVersion === "10") {
196+
if (include.length > 0) {
195197
const queryInfo = formatQueryInfo(res, {
196198
apiVersion,
197199
color,
@@ -202,7 +204,7 @@ async function buildCustomEval(argv) {
202204
}
203205
}
204206
} catch (err) {
205-
logger.stderr(formatError(err, { apiVersion, color }));
207+
logger.stderr(formatError(err, { apiVersion, color, include }));
206208
return cb(null);
207209
}
208210

@@ -227,6 +229,7 @@ function buildShellCommand(yargs) {
227229
.options(DATABASE_PATH_OPTIONS)
228230
.options(CORE_OPTIONS)
229231
.options(QUERY_OPTIONS)
232+
.middleware(resolveIncludeOptions)
230233
.example([
231234
[
232235
"$0 shell --database us/my_db",

src/config/setup-test-container.mjs

+2
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,15 @@ export function setupTestContainer() {
114114
runQuery: stub(),
115115
runQueryFromString: stub(),
116116
formatQueryResponse: faunaClientV10.formatQueryResponse,
117+
formatQueryInfo: faunaClientV10.formatQueryInfo,
117118
formatError: faunaClientV10.formatError,
118119
}),
119120
faunaClientV4: awilix.asValue({
120121
getClient: stub(),
121122
runQuery: stub(),
122123
runQueryFromString: stub(),
123124
formatQueryResponse: faunaClientV4.formatQueryResponse,
125+
formatQueryInfo: faunaClientV4.formatQueryInfo,
124126
formatError: faunaClientV4.formatError,
125127
}),
126128
};

src/lib/fauna-client.mjs

+16-102
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
//@ts-check
22

3-
import stripAnsi from "strip-ansi";
4-
53
import { container } from "../config/container.mjs";
64
import { isUnknownError } from "./errors.mjs";
75
import { faunaToCommandError } from "./fauna.mjs";
86
import { faunadbToCommandError } from "./faunadb.mjs";
9-
import { colorize, Format } from "./formatting/colorize.mjs";
10-
11-
/**
12-
* Regex to match the FQL diagnostic line.
13-
* @type {RegExp}
14-
*/
15-
export const FQL_DIAGNOSTIC_REGEX = /^(\s{2,}\|)|(\s*\d{1,}\s\|)/;
7+
import { Format } from "./formatting/colorize.mjs";
168

179
/**
1810
* Gets a secret for the current credentials.
@@ -103,16 +95,17 @@ export const runQueryFromString = (expression, argv) => {
10395
* @param {object} opts
10496
* @param {string} opts.apiVersion - The API version
10597
* @param {boolean} opts.color - Whether to colorize the error
98+
* @param {string[]} opts.include - The query info fields to include
10699
* @returns {string}
107100
*/
108-
export const formatError = (err, { apiVersion, color }) => {
101+
export const formatError = (err, { apiVersion, color, include }) => {
109102
const faunaV4 = container.resolve("faunaClientV4");
110103
const faunaV10 = container.resolve("faunaClientV10");
111104

112105
if (apiVersion === "4") {
113-
return faunaV4.formatError(err, { color });
106+
return faunaV4.formatError(err, { color, include });
114107
} else {
115-
return faunaV10.formatError(err, { color });
108+
return faunaV10.formatError(err, { color, include });
116109
}
117110
};
118111

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

135-
const { color } = argv;
128+
const { color, include } = argv;
136129
if (argv.apiVersion === "4") {
137-
faunadbToCommandError({ err, color });
130+
faunadbToCommandError({ err, color, include });
138131
} else {
139-
faunaToCommandError({ err, color });
132+
faunaToCommandError({ err, color, include });
140133
}
141134
}
142135

@@ -153,67 +146,15 @@ export const isQueryable = async (argv) => {
153146
* @returns {string}
154147
*/
155148
export const formatQueryResponse = (res, { apiVersion, color, format }) => {
156-
const faunaV4 = container.resolve("faunaClientV4");
157-
const faunaV10 = container.resolve("faunaClientV10");
158-
159149
if (apiVersion === "4") {
150+
const faunaV4 = container.resolve("faunaClientV4");
160151
return faunaV4.formatQueryResponse(res, { format, color });
161152
} else {
153+
const faunaV10 = container.resolve("faunaClientV10");
162154
return faunaV10.formatQueryResponse(res, { format, color });
163155
}
164156
};
165157

166-
/**
167-
* Formats a summary of a query from a fauna
168-
* @param {string} summary - The summary of the query
169-
* @returns {string}
170-
*/
171-
export const formatQuerySummary = (summary) => {
172-
if (!summary || typeof summary !== "string") {
173-
return "";
174-
}
175-
176-
try {
177-
const lines = summary.split("\n").map((line) => {
178-
if (!line.match(FQL_DIAGNOSTIC_REGEX)) {
179-
return line;
180-
}
181-
return colorize(line, { format: Format.FQL });
182-
});
183-
return lines.join("\n");
184-
} catch (err) {
185-
const logger = container.resolve("logger");
186-
logger.debug(`Unable to parse performance hint: ${err}`);
187-
return summary;
188-
}
189-
};
190-
191-
const getQueryInfoValue = (response, field) => {
192-
switch (field) {
193-
case "txnTs":
194-
return response.txn_ts;
195-
case "schemaVersion":
196-
return response.schema_version?.toString();
197-
case "summary":
198-
return response.summary;
199-
case "queryTags":
200-
return response.query_tags;
201-
case "stats":
202-
return response.stats;
203-
default:
204-
return undefined;
205-
}
206-
};
207-
208-
const getIncludedQueryInfo = (response, include) => {
209-
const queryInfo = {};
210-
include.forEach((field) => {
211-
const value = getQueryInfoValue(response, field);
212-
if (value) queryInfo[field] = value;
213-
});
214-
return queryInfo;
215-
};
216-
217158
/**
218159
*
219160
* @param {object} response - The v4 or v10 query response with query info
@@ -224,38 +165,11 @@ const getIncludedQueryInfo = (response, include) => {
224165
* @returns
225166
*/
226167
export const formatQueryInfo = (response, { apiVersion, color, include }) => {
227-
if (apiVersion === "4" && include.includes("stats")) {
228-
/** @type {import("faunadb").MetricsResponse} */
229-
const metricsResponse = response;
230-
const colorized = colorize(
231-
{ metrics: metricsResponse.metrics },
232-
{ color, format: Format.YAML },
233-
);
234-
235-
return `${colorized}\n`;
236-
} else if (apiVersion === "10") {
237-
const queryInfoToDisplay = getIncludedQueryInfo(response, include);
238-
239-
if (Object.keys(queryInfoToDisplay).length === 0) return "";
240-
241-
// We colorize the entire query info object as YAML, but then need to
242-
// colorize the diagnostic lines individually. To simplify this, we
243-
// strip the ansi when we're checking if the line is a diagnostic line.
244-
const colorized = colorize(queryInfoToDisplay, {
245-
color,
246-
format: Format.YAML,
247-
})
248-
.split("\n")
249-
.map((line) => {
250-
if (!stripAnsi(line).match(FQL_DIAGNOSTIC_REGEX)) {
251-
return line;
252-
}
253-
return colorize(line, { format: Format.FQL });
254-
})
255-
.join("\n");
256-
257-
return `${colorized}\n`;
168+
if (apiVersion === "4") {
169+
const faunaV4 = container.resolve("faunaClientV4");
170+
return faunaV4.formatQueryInfo(response, { color, include });
171+
} else {
172+
const faunaV10 = container.resolve("faunaClientV10");
173+
return faunaV10.formatQueryInfo(response, { color, include });
258174
}
259-
260-
return "";
261175
};

0 commit comments

Comments
 (0)