Skip to content

Commit

Permalink
Merge branch 'v3' into persist-secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
mwilde345 committed Nov 20, 2024
2 parents 33e0f8d + 24dab06 commit 6c0487e
Show file tree
Hide file tree
Showing 24 changed files with 571 additions and 101 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
- runner: windows-latest
os: windows
arch: x64
# github-hosted (free) linux arm64 runner is planned by end of 2024
# it's currently available on team/enterprise github plans:
# https://github.blog/news-insights/product-news/arm64-on-github-actions-powering-faster-more-efficient-build-systems/

runs-on: ${{ matrix.runner }}
steps:
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs

name: Lint

on:
# temporarily "v3"; change to "main" after merge
push:
branches: ["v3"]
pull_request:
branches: ["v3"]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Lint (nodeJS 22.x)
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: "npm"
- run: npm ci
- run: npm run lint
env:
TERM: xterm-256color
# Set to the correct color level; 2 is 256 colors
# https://github.com/chalk/chalk?tab=readme-ov-file#supportscolor
FORCE_COLOR: 2
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18
22
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default [
languageOptions: {
globals: {
...globals.mocha,
...globals.nodeBuiltin,

// cjs globals
require: "readonly",
Expand Down
2 changes: 2 additions & 0 deletions src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import evalCommand from "./commands/eval.mjs";
import keyCommand from "./commands/key.mjs";
import loginCommand from "./commands/login.mjs";
import schemaCommand from "./commands/schema/schema.mjs";
import shellCommand from "./commands/shell.mjs";
import { authNZMiddleware } from "./lib/auth/authNZ.mjs";
import { checkForUpdates, fixPaths, logArgv } from "./lib/middleware.mjs";

Expand Down Expand Up @@ -65,6 +66,7 @@ function buildYargs(argvInput) {
.middleware([checkForUpdates, logArgv], true)
.middleware([fixPaths, authNZMiddleware], false)
.command("eval", "evaluate a query", evalCommand)
.command("shell", "start an interactive shell", shellCommand)
.command("login", "login via website", loginCommand)
.command(keyCommand)
.command(schemaCommand)
Expand Down
2 changes: 2 additions & 0 deletions src/commands/database.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//@ts-check

import { container } from "../cli.mjs";

async function listDatabases(profile) {
Expand Down
75 changes: 32 additions & 43 deletions src/commands/eval.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import util from "util";
import { container } from "../cli.mjs";
import {
// ensureDbScopeClient,
commonQueryOptions,
commonConfigurableQueryOptions,
} from "../lib/command-helpers.mjs";
import * as misc from "../lib/misc.mjs";

Expand All @@ -17,12 +17,12 @@ const { runQuery } = misc;
/**
* Write json encoded output
*
* @param {String} file Target filename
* @param {String | undefined} file Target filename
* @param {any} data Data to encode
*/
async function writeFormattedJson(file, data) {
let str = JSON.stringify(data);
if (file === null) {
if (file === undefined) {
return str;
} else {
// await writeFile(file, str);
Expand All @@ -33,11 +33,13 @@ async function writeFormattedJson(file, data) {
/**
* Write fauna shell encoded output
*
* @param {String} file Target filename
* @param {String | undefined} file Target filename
* @param {any} str Data to encode
*/
async function writeFormattedShell(file, str) {
if (file === null) {
// TODO: this should really normalize the line endings
// using os.EOL.
if (file === undefined) {
return str;
} else {
// await writeFile(file, str);
Expand All @@ -56,7 +58,8 @@ async function writeFormattedOutput(file, data, format) {
if (format === "json") {
return writeFormattedJson(file, data);
} else if (format === "shell") {
return writeFormattedShell(file, util.inspect(data, { depth: null }));
const fmtd = util.inspect(data, { depth: null, compact: false });
return writeFormattedShell(file, fmtd);
} else {
throw new Error(`Unrecognized format ${format}.`);
}
Expand Down Expand Up @@ -95,7 +98,7 @@ async function writeFormattedOutputV10(file, res, format) {
}
}

async function performV10Query(client, fqlQuery, outputFile, flags) {
export async function performV10Query(client, fqlQuery, outputFile, flags) {
let format;
if (flags.format === "shell") {
format = "decorated";
Expand All @@ -113,7 +116,7 @@ async function performV10Query(client, fqlQuery, outputFile, flags) {
return writeFormattedOutputV10(outputFile, res, flags.format);
}

async function performV4Query(client, fqlQuery, outputFile, flags) {
export async function performV4Query(client, fqlQuery, outputFile, flags) {
const faunadb = (await import("faunadb")).default;

// why...?
Expand All @@ -123,7 +126,12 @@ async function performV4Query(client, fqlQuery, outputFile, flags) {

try {
const response = await runQuery(fqlQuery, client);
return await writeFormattedOutput(outputFile, response, flags.format);
const formatted = await writeFormattedOutput(
outputFile,
response,
flags.format,
);
return formatted;
} catch (error) {
if (error.faunaError === undefined) {
// this happens when wrapQueries fails during the runInContext step
Expand Down Expand Up @@ -151,18 +159,21 @@ async function performV4Query(client, fqlQuery, outputFile, flags) {
*
* @param {Object} client - An instance of the client used to execute the query.
* @param {string} fqlQuery - The FQL v4 query to be executed.
* @param {string} outputFile - Target filename
* @param {Object} flags - Options for the query execution.
* @param {("4" | "10")} flags.version - FQL version number
* @param {("json" | "json-tagged" | "shell")} flags.format - Result format
* @param {boolean} [flags.typecheck] - (Optional) Flag to enable typechecking
* @param {string | undefined} outputFile - Target filename
*
* @param {Object} argv - Options for the query execution.
* @param {("4" | "10")} argv.version - FQL version number
* @param {("json" | "json-tagged" | "shell")} argv.format - Result format
* @param {boolean} [argv.typecheck] - (Optional) Flag to enable typechecking
*/
export async function performQuery(client, fqlQuery, outputFile, flags) {
if (flags.version === "4") {
const res = performV4Query(client, fqlQuery, outputFile, flags);
return res;
export async function performQuery(client, fqlQuery, outputFile, argv) {
const performV4Query = container.resolve("performV4Query");
const performV10Query = container.resolve("performV10Query");

if (argv.version === "4") {
return performV4Query(client, fqlQuery, outputFile, argv);
} else {
return performV10Query(client, fqlQuery, outputFile, flags);
return performV10Query(client, fqlQuery, outputFile, argv);
}
}

Expand Down Expand Up @@ -213,8 +224,6 @@ async function doEval(argv) {

const format = argv.format ?? (process.stdout.isTTY ? "shell" : "json");

const performQuery = container.resolve("performQuery");

const result = await performQuery(
client,
queryFromFile || argv.query,
Expand Down Expand Up @@ -259,35 +268,15 @@ function buildEvalCommand(yargs) {
output: {
type: "string",
description: "file to write output to",
default: null,
default: undefined,
},
format: {
type: "string",
description: "output format",
default: "shell",
options: EVAL_OUTPUT_FORMATS,
},
version: {
description: "which FQL version to use",
type: "string",
alias: "v",
default: "10",
choices: ["4", "10"],
},
// TODO: is this unused? i think it might be
timeout: {
type: "number",
description: "connection timeout in milliseconds",
default: 5000,
},

// v10 specific options
typecheck: {
type: "boolean",
description: "enable typechecking",
default: undefined,
},
...commonQueryOptions,
...commonConfigurableQueryOptions,
})
.example([
['$0 eval "Collection.all()"'],
Expand Down
2 changes: 2 additions & 0 deletions src/commands/key.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//@ts-check

import { container } from "../cli.mjs";
import { getAccountKey, getDBKey } from "../lib/auth/authNZ.mjs";

Expand Down
111 changes: 111 additions & 0 deletions src/commands/shell.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//@ts-check

import repl from "node:repl";

import { container } from "../cli.mjs";
import {
// ensureDbScopeClient,
commonConfigurableQueryOptions,
} from "../lib/command-helpers.mjs";
import { performQuery } from "./eval.mjs";

async function doShell(argv) {
const logger = container.resolve("logger");
let completionPromise;

if (argv.dbPath) logger.stdout(`Starting shell for database ${argv.dbPath}`);
logger.stdout("Type Ctrl+D or .exit to exit the shell");

/** @type {import('node:repl').ReplOptions} */
const replArgs = {
prompt: `${argv.db_path || ""}> `,
ignoreUndefined: true,
preview: argv.version !== "10",
// TODO: integrate with fql-analyzer for completions
completer: argv.version === "10" ? () => [] : undefined,
output: container.resolve("stdoutStream"),
input: container.resolve("stdinStream"),
eval: await buildCustomEval(argv),
terminal: true,
};

const shell = repl.start(replArgs);
// eslint-disable-next-line no-console
shell.on("error", console.error);

completionPromise = new Promise((resolve) => {
shell.on("exit", resolve);
});

[
{
cmd: "clear",
help: "Clear the repl",
action: () => {
// eslint-disable-next-line no-console
console.clear();
shell.prompt();
},
},
{
cmd: "last_error",
help: "Display the last error",
action: () => {
logger.stdout(shell.context.lastError);
shell.prompt();
},
},
].forEach(({ cmd, ...cmdOptions }) => shell.defineCommand(cmd, cmdOptions));

return completionPromise;
}

// caches the logger, client, and performQuery for subsequent shell calls
async function buildCustomEval(argv) {
const client = await container.resolve("getSimpleClient")(argv);

return async (cmd, ctx, filename, cb) => {
try {
const logger = container.resolve("logger");

if (cmd.trim() === "") return cb();

let res;
try {
res = await performQuery(client, cmd, undefined, {
...argv,
format: "shell",
});
} catch (err) {
let errString = "";
if (err.code) {
errString = errString.concat(`${err.code}\n`);
}
errString = errString.concat(err.message);
logger.stderr(errString);
return cb(null);
}

logger.stdout(res);

return cb(null);
} catch (e) {
return cb(e);
}
};
}

function buildShellCommand(yargs) {
return yargs
.options({
...commonConfigurableQueryOptions,
})
.example([["$0 shell"], ["$0 shell root_db/child_db"]])
.version(false)
.help("help", "show help");
}

export default {
builder: buildShellCommand,
handler: doShell,
};
14 changes: 10 additions & 4 deletions src/config/setup-container.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import open from "open";
import updateNotifier from "update-notifier";

import { parseYargs } from "../cli.mjs";
import { performQuery } from "../commands/eval.mjs";
import { performV4Query, performV10Query } from "../commands/eval.mjs";
import { makeAccountRequest } from "../lib/account.mjs";
import OAuthClient from "../lib/auth/oauth-client.mjs";
import { getSimpleClient } from "../lib/command-helpers.mjs";
import { makeFaunaRequest } from "../lib/db.mjs";
import { FaunaAccountClient } from "../lib/fauna-account-client.mjs";
import fetchWrapper from "../lib/fetch-wrapper.mjs";
import { AccountKey, SecretKey } from "../lib/file-util.mjs";
import logger from "../lib/logger.mjs";
import buildLogger from "../lib/logger.mjs";
import {
deleteUnusedSchemaFiles,
gatherFSL,
Expand Down Expand Up @@ -51,6 +51,11 @@ export function setupCommonContainer() {
/** @typedef {Resolvers<injectables>} modifiedInjectables */

export const injectables = {
// process specifics
stdinStream: awilix.asValue(process.stdin),
stdoutStream: awilix.asValue(process.stdout),
stderrStream: awilix.asValue(process.stderr),

// node libraries
exit: awilix.asValue(exit),
fetch: awilix.asValue(fetchWrapper),
Expand All @@ -66,8 +71,9 @@ export const injectables = {

// generic lib (homemade utilities)
parseYargs: awilix.asValue(parseYargs),
logger: awilix.asValue(logger),
performQuery: awilix.asValue(performQuery),
logger: awilix.asFunction(buildLogger),
performV4Query: awilix.asValue(performV4Query),
performV10Query: awilix.asValue(performV10Query),
getSimpleClient: awilix.asValue(getSimpleClient),
accountClient: awilix.asClass(FaunaAccountClient, {
lifetime: Lifetime.SCOPED,
Expand Down
Loading

0 comments on commit 6c0487e

Please sign in to comment.