Skip to content

Commit

Permalink
improve shell error handling
Browse files Browse the repository at this point in the history
FE-4795
see above jira for more details

1. shell will not exit on errors
2. shell will correctly reconnect to core in the presence of an
   intermittent connection error
  • Loading branch information
fauna-chase committed Nov 28, 2023
1 parent 2ba90e8 commit 18109cf
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 97 deletions.
30 changes: 13 additions & 17 deletions src/commands/eval.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,25 +171,21 @@ class EvalCommand extends FaunaCommand {
}

async performV10Query(client, fqlQuery, outputFile, flags) {
try {
let format;
if (flags.format === "shell") {
format = "decorated";
} else if (flags.format === "json-tagged") {
format = "tagged";
} else {
format = "simple";
}
let format;
if (flags.format === "shell") {
format = "decorated";
} else if (flags.format === "json-tagged") {
format = "tagged";
} else {
format = "simple";
}

const res = await client.query(fqlQuery, {
format,
typecheck: flags.typecheck,
});
const res = await client.query(fqlQuery, {
format,
typecheck: flags.typecheck,
});

return await this.writeFormattedOutputV10(outputFile, res, flags.format);
} catch (error) {
this.error(`${error.code}\n\n${error.queryInfo.summary}`);
}
return this.writeFormattedOutputV10(outputFile, res, flags.format);
}

async performV4Query(client, fqlQuery, outputFile, flags) {
Expand Down
19 changes: 12 additions & 7 deletions src/commands/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,22 @@ class ShellCommand extends EvalCommand {
if (cmd.trim() === "") return cb();

if (this.flags.version === "10") {
const res = await this.performV10Query(
this.connection.client,
cmd,
null,
{
let res;
try {
res = await this.performV10Query(this.connection.client, cmd, null, {
format: "shell",
version: "10",
typecheck: this.flags.typecheck,
});
} catch (err) {
let errString = "";
if (err.code) {
errString = errString.concat(`${err.code}\n`);
}
);

errString = errString.concat(err.message);
console.error(errString);
return cb(null);
}
console.log(res);

return cb(null);
Expand Down
10 changes: 6 additions & 4 deletions src/lib/environment-factory.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { confirm, input } from "@inquirer/prompts";
import { Command, ux } from "@oclif/core";
import { Endpoint, ShellConfig } from "./config";
import FaunaClient, { QuerySuccess } from "./fauna-client";
import FaunaClient, { QueryFailure, QuerySuccess } from "./fauna-client";
import { searchSelect } from "./search-select";

export interface AddEnvironmentParams {
Expand Down Expand Up @@ -112,7 +112,9 @@ export class EnvironmentFactory {
database: databaseName,
};
this.config.saveProjectConfig();
console.log(`Saved environment ${name} to ${this.config.projectConfigFile()}`);
console.log(
`Saved environment ${name} to ${this.config.projectConfigFile()}`
);
}

promptDatabasePath = async (endpoint: Endpoint): Promise<string> => {
Expand All @@ -124,7 +126,7 @@ export class EnvironmentFactory {

const res = await client.query("0");
if (res.status !== 200) {
this.cmd.error(`${res.body.error.code}`);
this.cmd.error(`${(res as QueryFailure).body.error.code}`);
}

const databasePaths = await this.getDatabasePaths(client);
Expand Down Expand Up @@ -199,7 +201,7 @@ export class EnvironmentFactory {
}
);
if (databases.status !== 200) {
this.cmd.error(`Error: ${databases.body.error.code}`);
this.cmd.error(`Error: ${(databases as QueryFailure).body.error.code}`);
}

const dbs = (databases as QuerySuccess<any>).body.data;
Expand Down
107 changes: 38 additions & 69 deletions src/lib/fauna-client.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
import { connect, constants } from "http2";

// Copied from the fauna-js driver:
// https://github.com/fauna/fauna-js/blob/main/src/http-client/node-http2-client.ts
import fetch from "node-fetch";

export type QueryResponse<T> = QuerySuccess<T> | QueryFailure;

export type QueryInfo = {
headers: any;
body: {
summary: string;
};
};

export type QuerySuccess<T> = QueryInfo & {
export type QuerySuccess<T> = {
status: 200;
body: {
data: T;
};
};

export type QueryFailure = QueryInfo & {
status: 400;
export type QueryFailure = {
status: number;
body: {
summary?: string;
error: {
code: string;
message?: string;
Expand All @@ -30,16 +21,12 @@ export type QueryFailure = QueryInfo & {
};

export default class FaunaClient {
session: any;
endpoint: string;
secret: string;
timeout?: number;

constructor(opts: { endpoint: string; secret: string; timeout?: number }) {
this.session = connect(opts.endpoint, {
peerMaxConcurrentStreams: 50,
})
.once("error", () => this.close())
.once("goaway", () => this.close());
this.endpoint = opts.endpoint;
this.secret = opts.secret;
this.timeout = opts.timeout;
}
Expand All @@ -57,57 +44,39 @@ export default class FaunaClient {
typecheck: opts?.typecheck ?? undefined,
secret: opts?.secret ?? this.secret,
};
return new Promise((resolvePromise, rejectPromise) => {
let req: any;
const onResponse = (http2ResponseHeaders: any) => {
const status = http2ResponseHeaders[constants.HTTP2_HEADER_STATUS];
let responseData = "";
const url = new URL(this.endpoint);
url.pathname = "/query/1";
const res = await fetch(url, {
method: "POST",
headers: {
authorization: `Bearer ${secret ?? this.secret}`,
"x-fauna-source": "Fauna Shell",
...(typecheck !== undefined && { "x-typecheck": typecheck.toString() }),
...(format !== undefined && { "x-format": format }),
},
body: JSON.stringify({ query }),
});

req.on("data", (chunk: any) => {
responseData += chunk;
});
const json = await res.json();

req.on("end", () => {
resolvePromise({
status,
body: JSON.parse(responseData),
headers: http2ResponseHeaders,
});
});
if (res.status === 200 || res.status === 201) {
return {
status: 200,
body: {
data: json.data as T,
},
};

try {
const httpRequestHeaders = {
Authorization: `Bearer ${secret}`,
"x-format": format,
"X-Fauna-Source": "Fauna Shell",
[constants.HTTP2_HEADER_PATH]: "/query/1",
[constants.HTTP2_HEADER_METHOD]: "POST",
...((typecheck && { "x-typecheck": typecheck }) ?? {}),
...((this.timeout && { "x-query-timeout-ms": this.timeout }) ?? {}),
};

req = this.session
.request(httpRequestHeaders)
.setEncoding("utf8")
.on("error", (error: any) => rejectPromise(error))
.on("response", onResponse);

req.write(JSON.stringify({ query }), "utf8");

// req.setTimeout must be called before req.end()
req.setTimeout((this.timeout ?? 0) + 5000, () => {
req.destroy(new Error(`Client timeout`));
});

req.end();
} catch (error) {
rejectPromise(error);
}
});
}

async close() {
this.session.close();
} else {
return {
status: res.status,
body: {
summary: json.summary,
error: {
code: json.error?.code,
message: json.error?.message,
},
},
};
}
}
}

0 comments on commit 18109cf

Please sign in to comment.