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

v3 colorLogs #523

Closed
wants to merge 159 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
24b0313
wip
echo-bravo-yahoo Sep 26, 2024
955faa3
wip
echo-bravo-yahoo Sep 27, 2024
4116599
move files
echo-bravo-yahoo Sep 27, 2024
5d2fba8
fix project so older, tsc version can coexist with newer, not-built v…
echo-bravo-yahoo Sep 30, 2024
1a223d7
login poc (#370)
wildemat Oct 1, 2024
9b43e48
add schema push (#371)
echo-bravo-yahoo Oct 2, 2024
d0c8039
login test (#372)
wildemat Oct 3, 2024
66f8123
improve schema commands, testing (#373)
echo-bravo-yahoo Oct 3, 2024
7f43633
add first draft of remaining schema commands
echo-bravo-yahoo Oct 3, 2024
7eb9618
add first draft of remaining schema tests
echo-bravo-yahoo Oct 3, 2024
b2c9a01
update dependencies
echo-bravo-yahoo Oct 3, 2024
b3648c6
fix logger breaking at runtime
echo-bravo-yahoo Oct 3, 2024
a01f6b3
add tests for schema diff and schema status
echo-bravo-yahoo Oct 3, 2024
244eb5a
update eslint, move eslint config files, make changes
echo-bravo-yahoo Oct 4, 2024
2939639
run eslint and prettier
echo-bravo-yahoo Oct 4, 2024
2cff039
Updated config.yml
echo-bravo-yahoo Oct 4, 2024
192df25
Updated config.yml
echo-bravo-yahoo Oct 4, 2024
ca6d6e7
fix entrypoint
echo-bravo-yahoo Oct 4, 2024
e64fb67
update dependencies
echo-bravo-yahoo Oct 4, 2024
2bbd130
more entrypoint fixes
echo-bravo-yahoo Oct 4, 2024
ba2ef44
cleanup
echo-bravo-yahoo Oct 4, 2024
eb185a1
refactor error handler, argv, no more double invocations
echo-bravo-yahoo Oct 4, 2024
36bcb33
Junit test reports (#376)
echo-bravo-yahoo Oct 7, 2024
8ae0a0b
infer types via JSDoc through IoC container (#377)
echo-bravo-yahoo Oct 7, 2024
1ac236e
test fixes
echo-bravo-yahoo Oct 7, 2024
f9a2bcd
add tscheck annotations, add JSDoc types to makeFaunaRequest (#379)
echo-bravo-yahoo Oct 8, 2024
70088ed
Persist Account keys on login (#375)
wildemat Oct 8, 2024
8336240
Stash resolutions (#384)
echo-bravo-yahoo Oct 10, 2024
f99229d
misc improvements (#385)
echo-bravo-yahoo Oct 11, 2024
baea78d
Removing things (#386)
echo-bravo-yahoo Oct 11, 2024
e20c08d
makeAccountRequest wrapper. Basic create key
wildemat Oct 11, 2024
ffb9854
Test additions (#390)
echo-bravo-yahoo Oct 11, 2024
6d44c0b
move shared options after command-specific options
echo-bravo-yahoo Oct 11, 2024
d4ce8d3
persist secrets (#393)
wildemat Oct 21, 2024
c819808
check for updates weekly on run (#402)
echo-bravo-yahoo Oct 22, 2024
cccc904
swap to npm (#401)
echo-bravo-yahoo Oct 22, 2024
63ffffe
Add Github action for testing (#404)
echo-bravo-yahoo Oct 22, 2024
ccad6a7
use @fauna/typescript for prettier, eslint configs (#405)
echo-bravo-yahoo Oct 22, 2024
587ebee
move prettier from json config to js config
echo-bravo-yahoo Oct 23, 2024
989e2ec
build: use prettier as authoritative style formatter (#406)
echo-bravo-yahoo Oct 24, 2024
7054ce5
eslint proposal (#407)
echo-bravo-yahoo Oct 24, 2024
653ff5f
first draft of SEA build (mac only)
echo-bravo-yahoo Oct 25, 2024
492a383
fix linting and make runnable in more envs
echo-bravo-yahoo Oct 25, 2024
7677510
update @fauna/typescript
echo-bravo-yahoo Oct 25, 2024
c41f6c3
fix: update CI tests
echo-bravo-yahoo Oct 25, 2024
cc6a473
build SEA binaries with github action
echo-bravo-yahoo Oct 25, 2024
0527a92
run prettier on every file
echo-bravo-yahoo Oct 25, 2024
d8e148c
move to --no-input instead of --force
echo-bravo-yahoo Oct 21, 2024
3199c26
catch schema commands up to main branch
echo-bravo-yahoo Oct 21, 2024
665db93
address PR feedback
echo-bravo-yahoo Oct 29, 2024
b5f4e99
clean up schema commands and tests, fix complexity lint warns
echo-bravo-yahoo Oct 29, 2024
3b45962
prevent SEA installations from attempting an update check
echo-bravo-yahoo Oct 29, 2024
df3ae7e
login redirect (#415)
wildemat Nov 19, 2024
f8f6916
update nvmrc version
echo-bravo-yahoo Nov 19, 2024
54d93f1
add ts-check annotations to more files, clean up tests
echo-bravo-yahoo Nov 19, 2024
de23074
add shell command
echo-bravo-yahoo Nov 13, 2024
ac50ebb
fix tests
echo-bravo-yahoo Nov 19, 2024
cbd2569
improve fn name
echo-bravo-yahoo Nov 19, 2024
7954da1
add v4 test and extend tests for 2+ shell commands
echo-bravo-yahoo Nov 20, 2024
7c87a02
tweak tests
echo-bravo-yahoo Nov 20, 2024
c0b0138
add typecheck test
echo-bravo-yahoo Nov 20, 2024
ff73fec
add another test
echo-bravo-yahoo Nov 20, 2024
53a92e7
fix linting
echo-bravo-yahoo Nov 20, 2024
24dab06
add github linting workflow step (#418)
echo-bravo-yahoo Nov 20, 2024
d337bf1
fix the build in github
echo-bravo-yahoo Nov 20, 2024
672c6d5
persist secrets (#417)
wildemat Nov 20, 2024
51b6eaa
update dependencies (#421)
echo-bravo-yahoo Nov 21, 2024
b31f354
fix cloud login failing on DI
echo-bravo-yahoo Nov 21, 2024
ddaaaa2
add timeouts to github action jobs
echo-bravo-yahoo Nov 21, 2024
a3c58b5
move to mocha-multi-reporter
echo-bravo-yahoo Nov 21, 2024
f59d94f
fix CLI packaging
echo-bravo-yahoo Nov 21, 2024
dd80caf
add stubs for additional tests
echo-bravo-yahoo Nov 21, 2024
04c5d4e
update package-lock
echo-bravo-yahoo Nov 21, 2024
95f1f7f
prevent nodeJS warnings in production
echo-bravo-yahoo Nov 21, 2024
3d2b287
address PR feedback
echo-bravo-yahoo Nov 21, 2024
d78560f
[FE-6125] Stub out create database (#424)
henryfauna Nov 22, 2024
0a937a1
Install fauna (#426)
cleve-fauna Nov 22, 2024
2c268e7
Remove unused jira workflows (#427)
Nov 22, 2024
7204707
If no command is given show help text; and instructions on sub-help. …
cleve-fauna Nov 22, 2024
844b39a
Add some more Github standardization (#428)
Nov 22, 2024
805385b
add test for incorrect flags
echo-bravo-yahoo Nov 22, 2024
82d4fa9
Update test/general-cli.mjs
cleve-fauna Nov 22, 2024
40d10bd
improve type inference on things pulled from awilix (#433)
echo-bravo-yahoo Nov 23, 2024
10dabb3
Create Database Command (#435)
henryfauna Nov 25, 2024
347bd5d
Clarify quick start text (#430)
cleve-fauna Nov 25, 2024
25615ca
Use `homedir` provided by the container (#436)
ptpaterson Nov 26, 2024
5bb7f4a
[FE-6127] Delete Database (#437)
henryfauna Nov 26, 2024
e37507e
Config (#422)
echo-bravo-yahoo Nov 26, 2024
0285ab4
Fauna Query Options (#441)
henryfauna Nov 26, 2024
33271d3
fix config tests (#442)
echo-bravo-yahoo Nov 26, 2024
ecc0524
Improve credential handling (#432)
wildemat Nov 27, 2024
cbdc8cd
Rework eval and shell to fully implement eval (#443)
Dec 2, 2024
22c64c5
login and account keys should reference 'user' instead of 'profile' (…
wildemat Dec 2, 2024
1153265
Support running commands against container. (#447)
cleve-fauna Dec 3, 2024
33ea345
FE-5990 Set argv.secret to 'secret' when --local passed in and --secr…
cleve-fauna Dec 3, 2024
216715f
Move middleware to separate method for testability. Add debug logging…
cleve-fauna Dec 3, 2024
7fce0fa
Finish the `database list` command (#448)
henryfauna Dec 3, 2024
aca56f4
Confirm correct usage of Fauna instead of the Account API when --loca…
cleve-fauna Dec 3, 2024
daf9815
fix help text not displaying (#446)
echo-bravo-yahoo Dec 3, 2024
af18f6b
Add colorized output to database, query, and shell commands (#456)
Dec 4, 2024
ecd8d58
add methods to command signature for database, schema (#458)
echo-bravo-yahoo Dec 4, 2024
6c7acb6
FE-6024 Implement shell history (#453)
ptpaterson Dec 4, 2024
8bbdb35
Review help strings (#451)
jrodewig Dec 4, 2024
1e6a2ea
[FE-6155] - Support `--database` in database commands (#454)
henryfauna Dec 4, 2024
3059802
improve pre-argv parsing (#459)
echo-bravo-yahoo Dec 4, 2024
798c39c
enable --version flag (#457)
echo-bravo-yahoo Dec 4, 2024
4030666
Improve error handling (#462)
Dec 4, 2024
8d1fb06
group options (#463)
echo-bravo-yahoo Dec 4, 2024
f165610
[FE-6169] Support Shorthand Region Groups (#464)
henryfauna Dec 4, 2024
f91d109
Fix verbosity check (#470)
Dec 4, 2024
3b48bad
disable strict options parsing (#460)
echo-bravo-yahoo Dec 4, 2024
9abff40
Add dynamic key support to schema (#466)
Dec 4, 2024
f6bc79f
Remove some options from login command (#472)
ptpaterson Dec 4, 2024
40ab83f
remove test reliance on color (#471)
echo-bravo-yahoo Dec 5, 2024
e3dd92e
lint and prettify on PR push/change (#465)
echo-bravo-yahoo Dec 5, 2024
33f4369
Don't let config parse for help or version when getting profile (#474)
Dec 5, 2024
f1344de
document --no-<arg> for the remaining default true boolean options (#…
henryfauna Dec 5, 2024
dd4799a
default oauth to prod client (#476)
wildemat Dec 5, 2024
9585314
Bare strings break colorize (#473)
Dec 5, 2024
05dec44
Review examples and help strings (#477)
jrodewig Dec 5, 2024
4165d88
Consolidate Validation Helpers (#467)
henryfauna Dec 5, 2024
66411f2
Remove 'login options' group from login cmd (#480)
jrodewig Dec 5, 2024
d52a149
Credentials Middleware tests (#461)
wildemat Dec 5, 2024
8347082
add -h alias for help (#483)
echo-bravo-yahoo Dec 5, 2024
4fd660a
make command descriptions more consistent (#482)
echo-bravo-yahoo Dec 5, 2024
a7227a3
Create Key (#468)
cleve-fauna Dec 5, 2024
593d754
FE-6181 validate profile when no config is found (#481)
ptpaterson Dec 5, 2024
deff6bc
FE-6168 Allow users to scope secrets when running with --local (#488)
cleve-fauna Dec 6, 2024
f7bb80d
[FE-6183] Update Permissions on Credentials Files (#487)
henryfauna Dec 6, 2024
c61a437
Review `fauna key create` examples + help text (#486)
jrodewig Dec 6, 2024
88b3a22
Fast succeed login when --local arg is given (#489)
cleve-fauna Dec 9, 2024
38f5cab
Prettify Login Success Page (#485)
henryfauna Dec 9, 2024
de234ba
remove --dir option from schema commands that don't use it (#484)
echo-bravo-yahoo Dec 9, 2024
40242a4
don't compute role from key name string when calling frontdoor (#493)
wildemat Dec 9, 2024
fabed04
turn login tests back on (#492)
wildemat Dec 9, 2024
53bf7cc
Rename "extra" option to "raw" (#490)
ptpaterson Dec 10, 2024
5951ab7
[FE-6195] Disable JS Completions (#495)
henryfauna Dec 10, 2024
8047c4d
Clean up output formatting and support colorizing FQL results (#496)
Dec 10, 2024
4eed51c
Require a database or secret on schema commands (#498)
henryfauna Dec 10, 2024
42d02d6
handle accountKey arg with user and secret (#500)
wildemat Dec 10, 2024
b1f1cbd
Update use of schema API for the pull command (#499)
ptpaterson Dec 10, 2024
79681b6
Add performance hints (#501)
Dec 11, 2024
b3b82eb
Update usage of the schema API for push, status, and diff (#502)
ptpaterson Dec 11, 2024
69a1217
Handle No Local Schema Files (#505)
henryfauna Dec 11, 2024
5a5c028
Move error handling to its own module and don't colorize if it alread…
Dec 11, 2024
a520215
Do not display a scary message if summary is empty (#508)
Dec 11, 2024
e25ef6d
Catch all yargs validation errors in isYargsError (#504)
Dec 11, 2024
fb4fdd2
provide a more actionable error message on invalid database names (#510)
henryfauna Dec 11, 2024
82fd6d4
Notify on `schema push` if no schema files are found (#507)
henryfauna Dec 11, 2024
02b5166
Fail the shell fast if you cannot query the database (#509)
Dec 11, 2024
1801770
Command to start a local container (#491)
cleve-fauna Dec 12, 2024
45ddf9c
don't use a default profile (#514)
wildemat Dec 12, 2024
531a17d
Update README (#513)
jrodewig Dec 12, 2024
2071e2e
Redact secrets in logArgv (#511)
Dec 12, 2024
0c7a40d
Remove duplicate arg aliases from database list (#515)
ptpaterson Dec 12, 2024
8ac0681
Clarify `--raw` flag help (#516)
jrodewig Dec 12, 2024
c963ec9
Handle network errors with better messaging (#519)
Dec 13, 2024
2b9c521
Detect if port for local Fauna is already occupied. Allow users to co…
cleve-fauna Dec 13, 2024
af5f309
Color log lines in fauna local command.
cleve-fauna Dec 13, 2024
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
Prev Previous commit
Next Next commit
misc improvements (#385)
* watch mode doesn't work; change script back to one-shot

* normalize paths and resolve ~ in paths

* clean up types, enable @ts-check in more places

* fix schema push and pull commands

also adds a few types and another test
echo-bravo-yahoo authored Oct 11, 2024
commit f99229d8fabbfca7823179fe6845343ee1bb61b8
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -120,7 +120,7 @@
"pretest": "yarn fixlint",
"local": "export $(cat .env | xargs); node bin/run",
"local-test-old": "export $(cat .env | xargs); mocha \"test/**/*.test.{js,ts}\"",
"local-test": "mocha --watch --recursive ./yargs-test --require ./yargs-test/mocha-root-hooks.mjs --experimental-require-module",
"local-test": "mocha --recursive ./yargs-test --require ./yargs-test/mocha-root-hooks.mjs",
"lint": "eslint .",
"fixlint": "eslint . --fix",
"test-old": "c8 -r html mocha --forbid-only \"test/**/*.test.{js,ts}\"",
18 changes: 10 additions & 8 deletions src/cli.mjs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import evalCommand from "./yargs-commands/eval.mjs";
import loginCommand from "./yargs-commands/login.mjs";
import schemaCommand from "./yargs-commands/schema/schema.mjs";
import databaseCommand from "./yargs-commands/database.mjs";
import { logArgv } from "./lib/middleware.mjs";
import { logArgv, fixPaths } from "./lib/middleware.mjs";

/** @typedef {import('awilix').AwilixContainer<import('./config/setup-container.mjs').modifiedInjectables>} cliContainer */

@@ -17,8 +17,7 @@ export let container;
export let builtYargs;

/**
* @function run
* @param {string} argvInput - The command string provided by the user or test. Parsed by yargs into an argv object.
* @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.
*/
export async function run(argvInput, _container) {
@@ -40,16 +39,18 @@ export async function run(argvInput, _container) {
}
}

// we split this out so it can be injected/mocked
// this lets us record calls against it and, e.g.,
// ensure it's only run once per command invocation
/**
* This is split out so it can be injected & spied on. This allows ensuring that,
* e.g., it's only run once per command invocation.
* @param {import('yargs').Argv<any>} builtYargs
* @returns {Promise<any>} builtYargs
*/
export async function parseYargs(builtYargs) {
return builtYargs.parseAsync();
}

/**
* @function buildYargs
* @param {string} argvInput
* @param {string|string[]} argvInput
* @returns {import('yargs').Argv<any>}
*/
function buildYargs(argvInput) {
@@ -61,6 +62,7 @@ function buildYargs(argvInput) {
yargsInstance
.scriptName("fauna")
.middleware([logArgv], true)
.middleware([fixPaths], false)
.command("eval", "evaluate a query", evalCommand)
.command("login", "login via website", loginCommand)
.command(schemaCommand)
9 changes: 8 additions & 1 deletion src/config/setup-container.mjs
Original file line number Diff line number Diff line change
@@ -23,6 +23,9 @@ import open from "open";
import OAuthClient from "../lib/auth/oauth-client.mjs";
import { Lifetime } from "awilix";
import fs from "node:fs";
import * as fsp from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { AccountKey } from "../lib/file-util.mjs";
import { parseYargs } from "../cli.mjs";

@@ -47,11 +50,15 @@ export function setupCommonContainer() {
*/

/** @typedef {Resolvers<injectables>} modifiedInjectables */

export const injectables = {
// node libraries
exit: awilix.asValue(exit),
fetch: awilix.asValue(fetchWrapper),
fs: awilix.asValue(fs),
fsp: awilix.asValue(fsp),
normalize: awilix.asValue(path.normalize),
homedir: awilix.asValue(os.homedir),

// third-party libraries
confirm: awilix.asValue(confirm),
@@ -82,7 +89,7 @@ export const injectables = {
};

export function setupRealContainer() {
/** @type {awilix.AwilixContainer<injectables>} */
/** @type {awilix.AwilixContainer<modifiedInjectables>} */
const container = setupCommonContainer();

container.register(injectables);
8 changes: 8 additions & 0 deletions src/config/setup-test-container.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import fs from "node:fs";
import { normalize } from "node:path";

import * as awilix from "awilix/lib/awilix.module.mjs";
import { setupCommonContainer, injectables } from "./setup-container.mjs";
import { f } from "../../yargs-test/helpers.mjs";

import { stub, spy } from "sinon";
import { parseYargs } from "../cli.mjs";
@@ -42,6 +44,10 @@ export function setupTestContainer() {
// real implementation
parseYargs: awilix.asValue(spy(parseYargs)),
fs: awilix.asValue(stub(fs)),
fsp: awilix.asValue({
unlink: stub(),
writeFile: stub(),
}),
logger: awilix.asValue({
// use these for making dev, support tickets easier.
// they're not mocked because we shouldn't test them
@@ -71,6 +77,8 @@ export function setupTestContainer() {
error.code = exitCode;
throw error;
}),
normalize: awilix.asValue(spy(normalize)),
fetch: awilix.asValue(stub().resolves(f({}))),
};

confirmManualMocks(manualMocks, thingsToManuallyMock);
25 changes: 16 additions & 9 deletions src/lib/db.mjs
Original file line number Diff line number Diff line change
@@ -3,15 +3,22 @@
import { container } from "../cli.mjs";

/**
* @function makeFaunaRequest
* @param {object} args
* @param {string} args.secret - The secret to include in the AUTHORIZATION header of the request.
* @param {string} args.baseUrl - The base URL from the scheme up through the top level domain and optional port; defaults to "https://db.fauna.com:443".
* @param {string} args.path - The path part of the URL. Added to the baseUrl and params to build the full URL.
* @param {Record<string, string>} [args.params] - The parameters (and their values) to set in the query string.
* @param {('GET'|'HEAD'|'OPTIONS'|'PATCH'|'PUT'|'POST'|'DELETE'|'PATCH')} args.method - The HTTP method to use when making the request.
* @param {object} [args.body] - The body to include in the request.
* @param {boolean} [args.shouldThrow] - Whether or not to throw if the network request succeeds but is not a 2XX. If this is set to false, makeFaunaRequest will return the error instead of throwing.
* @typedef {('GET'|'HEAD'|'OPTIONS'|'PATCH'|'PUT'|'POST'|'DELETE'|'PATCH')} method
*/

/**
* @typedef {Object} fetchParameters
* @property {string} secret - The secret to include in the AUTHORIZATION header of the request.
* @property {string} baseUrl - The base URL from the scheme up through the top level domain and optional port; defaults to "https://db.fauna.com:443".
* @property {string} path - The path part of the URL. Added to the baseUrl and params to build the full URL.
* @property {Record<string, string>} [params] - The parameters (and their values) to set in the query string.
* @property {method} method - The HTTP method to use when making the request.
* @property {object} [body] - The body to include in the request.
* @property {boolean} [shouldThrow=true] - Whether or not to throw if the network request succeeds but is not a 2XX. If this is set to false, makeFaunaRequest will return the error instead of throwing.
*/

/**
* @param {fetchParameters} args
*/
export async function makeFaunaRequest({
secret,
34 changes: 25 additions & 9 deletions src/lib/file-util.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
//@ts-check

import fs from "node:fs";
import { normalize } from "node:path";
import * as os from "node:os";
import os from "node:os";
import { container } from "../cli.mjs";

// path: string, returns boolean
/**
* Fixes paths by normalizing them (.. => parent directory) and resolving ~ to homedir.
* @param {string} path - The path to fix.
* @returns {string}
*/
export function fixPath(path) {
const normalize = container.resolve("normalize");
const homedir = container.resolve("homedir");
return normalize(path.replace(/^~/, homedir));
}

/**
* @function dirExists
* @param {string} path - The path to check existence for.
* @returns {boolean}
*/
export function dirExists(path) {
// TODO: needs to resolve home dir (~); node path libs won't do that for us
const stat = fs.statSync(normalize(path), {
const stat = fs.statSync(fixPath(path), {
// returns undefined instead of throwing if the file doesn't exist
throwIfNoEntry: false,
});
@@ -19,10 +32,14 @@ export function dirExists(path) {
}
}

// path: string, returns boolean
/**
* @function dirIsWriteable
* @param {string} path - The path to check existence for.
* @returns {boolean}
*/
export function dirIsWriteable(path) {
try {
fs.accessSync(path, fs.constants.W_OK);
fs.accessSync(fixPath(path), fs.constants.W_OK);
} catch (e) {
return false;
}
@@ -38,7 +55,7 @@ export function dirIsWriteable(path) {
*/
function fileExists(path) {
try {
fs.readFileSync(path);
fs.readFileSync(fixPath(path));
return true;
} catch (e) {
return false;
@@ -56,7 +73,6 @@ export class Credentials {
*/
constructor(filename = "") {
this.logger = container.resolve("logger");
this.exit = container.resolve("exit");
this.filename = filename;
this.credsDir = `${os.homedir()}/.fauna/credentials`;
if (!dirExists(this.credsDir)) {
9 changes: 9 additions & 0 deletions src/lib/middleware.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
//@ts-check

import { container } from "../cli.mjs";
import { fixPath } from "../lib/file-util.mjs";

export function logArgv(argv) {
const logger = container.resolve("logger");
logger.debug(JSON.stringify(argv, null, 4), "argv", argv);
return argv;
}

export function fixPaths(argv) {
if (argv.dir) {
return { ...argv, dir: fixPath(argv.dir) };
} else {
return argv;
}
}
117 changes: 85 additions & 32 deletions src/lib/schema.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
//@ts-check

import * as path from "path";
import { dirExists, dirIsWriteable } from "./file-util.mjs";
import { container } from "../cli.mjs";
import { makeFaunaRequest } from "../lib/db.mjs";

/**
* @param {string} dir - The directory path to check for existence and write access
*/
function checkDirUsability(dir) {
if (!dirExists(dir)) {
throw new Error(`The project fsl directory: ${dir} does not exist.`);
@@ -11,10 +16,15 @@ function checkDirUsability(dir) {
}
}

// Reads the files using their relative-to-`basedir` paths and returns their
// contents paired with the relative path.
// Fails if the total size of the files is too large.
// relpaths: string[]
/**
* Reads files using their relative-to-`dir` paths and returns their contents
* paired with their relative paths. Fails if the total size of the files
* is too large.
*
* @param {string} dir - The path to the root directory the FSL files are stored in
* @param {string[]} relpaths - A list of paths (relative to `dir`) to individual FSL files
* @returns {LocalFSLFileDescription[]}
*/
function read(dir, relpaths) {
const fs = container.resolve("fs");
const logger = container.resolve("logger");
@@ -39,12 +49,14 @@ function read(dir, relpaths) {
return curr;
}

// Gathers all FSL files in the directory rooted at `basedir` and returns a
// list of relative paths.
// Fails if there are too many files.
// returns string[]
/**
* Gathers all FSL files in the directory rooted at `dir` and returns a list of
* relative paths. Fails if there are too many files.
*
* @param {string} dir - The path to the directory to delete unused FSL files from
* @returns {Promise<string[]>}
*/
export async function gatherRelativeFSLFilePaths(dir) {
const logger = container.resolve("logger");
const fs = container.resolve("fs");

const FILE_LIMIT = 32000;
@@ -70,21 +82,36 @@ export async function gatherRelativeFSLFilePaths(dir) {
};
const files = go("", []);
if (files.length > FILE_LIMIT) {
logger.stderr(`Too many files: ${files.length} > ${FILE_LIMIT}`);
throw new Error(`Too many files: ${files.length} > ${FILE_LIMIT}`);
}
return files;
}

/**
* @param {string} dir - The path to the directory to delete unused FSL files from
* @param {string[]} filesToDelete - A dictionary of filenames to their contents
* @returns {Promise<void>}
*/
export async function deleteUnusedSchemaFiles(dir, filesToDelete) {
const fs = container.resolve("fs");
const fsp = container.resolve("fsp");
const promises = [];
for (const fileName of filesToDelete) {
promises.push(fs.unlink(path.join(dir, fileName)));
promises.push(fsp.unlink(path.join(dir, fileName)));
}

return Promise.all(promises);
await Promise.all(promises);
}

/**
* @typedef LocalFSLFileDescription
* @property {string} name
* @property {string} content
*/

/**
* @param {string} dir - The path to the directory to gather FSL files from
* @returns {Promise<LocalFSLFileDescription[]>}
*/
export async function gatherFSL(dir) {
const gatherRelativeFSLFilePaths = container.resolve(
"gatherRelativeFSLFilePaths"
@@ -93,64 +120,90 @@ export async function gatherFSL(dir) {
checkDirUsability(dir);
const fps = await gatherRelativeFSLFilePaths(dir);
const files = read(dir, fps);
return JSON.stringify(files);
return files;
}

export async function writeSchemaFiles(dir, filenameToContentsHash) {
/**
* @param {string} dir - The path to the directory to write FSL files to
* @param {Record<string, string>} filenameToContentsDict - A dictionary of filenames to their contents
* @returns {Promise<void>}
*/
export async function writeSchemaFiles(dir, filenameToContentsDict) {
const fs = container.resolve("fs");
const fsp = container.resolve("fsp");
fs.mkdirSync(path.dirname(dir), { recursive: true });

const promises = [];
for (const [filename, fileContents] of Object.entries(
filenameToContentsHash
filenameToContentsDict
)) {
const fp = path.join(dir, filename);
promises.push(fs.writeFile(fp, fileContents));
promises.push(fsp.writeFile(fp, fileContents));
}

return Promise.all(promises);
await Promise.all(promises);
}

export async function getAllSchemaFileContents(
filenames,
{ ...overrides } = {}
) {
/** @typedef {import('./db.mjs').fetchParameters} fetchParameters */

/**
* @param {string[]} filenames - A list of schema file names to fetch
* @param {Omit<fetchParameters, "path"|"method">} overrides
* @returns {Promise<Record<string, string>>} A map of schema file names to their contents.
*/
export async function getAllSchemaFileContents(filenames, { ...overrides }) {
const promises = [];
const fileContents = {};
/* @type Record<string, string> */
const fileContentCollection = {};
for (const filename of filenames) {
promises.push(
getSchemaFile(filename, overrides).then((fileContent) => {
fileContents[filename] = fileContent;
getSchemaFile(filename, overrides).then(({ content }) => {
fileContentCollection[filename] = content;
})
);
}

return Promise.all(promises);
await Promise.all(promises);

return fileContentCollection;
}

export async function getSchemaFiles({ ...overrides } = {}) {
/**
* @param {Omit<fetchParameters, "path"|"method">} overrides
*/
export async function getSchemaFiles({ ...overrides }) {
/** @type {fetchParameters} */
const args = {
...overrides,
path: "/schema/1/files",
method: "GET",
...overrides,
};
return makeFaunaRequest({ ...args });
}

export async function getSchemaFile(filename, { ...overrides } = {}) {
/**
* @param {string} filename
* @param {Omit<fetchParameters, "path"|"method">} overrides
*/
export async function getSchemaFile(filename, { ...overrides }) {
/** @type {fetchParameters} */
const args = {
...overrides,
path: `/schema/1/files/${encodeURIComponent(filename)}`,
method: "GET",
...overrides,
};
return makeFaunaRequest({ ...args });
}

export async function getStagedSchemaStatus({ ...overrides } = {}) {
/**
* @param {Omit<fetchParameters, "path"|"method">} overrides
*/
export async function getStagedSchemaStatus({ ...overrides }) {
/** @type {fetchParameters} */
const args = {
...overrides,
path: "/schema/1/staged/status",
method: "GET",
...overrides,
};
return makeFaunaRequest({ ...args });
}
2 changes: 0 additions & 2 deletions src/yargs-commands/eval.mjs
Original file line number Diff line number Diff line change
@@ -69,10 +69,8 @@ async function writeFormattedOutput(file, data, format) {
* @param {boolean} [flags.typecheck] - (Optional) Flag to enable typechecking
*/
export async function performQuery(client, fqlQuery, outputFile, flags) {
console.log("query!");
if (flags.version === "4") {
const res = performV4Query(client, fqlQuery, outputFile, flags);
console.log(res);
return res;
} else {
return performV10Query(client, fqlQuery, outputFile, flags);
4 changes: 2 additions & 2 deletions src/yargs-commands/schema/pull.mjs
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ async function doPull(argv) {
.sort();

// Gather local .fsl files to overwrite or delete.
const existing = await gatherFSL(argv.dir);
const existing = (await gatherFSL(argv.dir)).map((file) => file.name);

// Summarize file changes.
const adds = [];
@@ -62,7 +62,7 @@ async function doPull(argv) {
}
deletes.sort();

logger.stdout("Pull makes the following changes:");
logger.stdout("Pull will make the following changes:");
if (argv.delete) {
for (const deleteme of deletes) {
logger.stdout(`delete: ${deleteme}`);
4 changes: 3 additions & 1 deletion yargs-entrypoint.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/usr/bin/env node

//@ts-check

import { hideBin } from "yargs/helpers";
import { run } from "./src/cli.mjs";
import { setupRealContainer as setupContainer } from "./src/config/setup-container.mjs";

run(hideBin(process.argv), await setupContainer());
run(hideBin(process.argv), setupContainer());
21 changes: 21 additions & 0 deletions yargs-test/schema/diff.mjs
Original file line number Diff line number Diff line change
@@ -104,4 +104,25 @@ describe("schema diff", function () {
expect(logger.stdout).to.have.been.calledWith("No schema differences");
expect(logger.stderr).to.not.have.been.called;
});

it("can parse relative paths", async function () {
const gatherFSL = container.resolve("gatherFSL");

await run(
`schema diff --secret "secret" --dir /all/but/the/leaf/..`,
container
);

expect(gatherFSL).to.have.been.calledWith("/all/but/the");
});

it("can parse home directory paths", async function () {
const gatherFSL = container.resolve("gatherFSL");
const homedir = container.resolve("homedir");
homedir.returns("/Users/test-user");

await run(`schema diff --secret "secret" --dir ~`, container);

expect(gatherFSL).to.have.been.calledWith("/Users/test-user");
});
});
16 changes: 10 additions & 6 deletions yargs-test/schema/pull.mjs
Original file line number Diff line number Diff line change
@@ -42,9 +42,13 @@ describe("schema pull", function () {

it("can pull schema", async function () {
const gatherFSL = container.resolve("gatherFSL");
gatherFSL.resolves(
'[{"name":"coll.fsl","content":"collection MyColl {\\n name: String\\n index byName {\\n terms [.name]\\n }\\n}\\n"}]'
);
gatherFSL.resolves([
{
name: "coll.fsl",
content:
"collection MyColl {\\n name: String\\n index byName {\\n terms [.name]\\n }\\n}\\n",
},
]);

// user accepts the changes in the interactive prompt
confirm.resolves(true);
@@ -127,7 +131,7 @@ describe("schema pull", function () {
expect(logger.stdout).to.have.been.calledWith("add: second.fsl");
expect(logger.stdout).to.have.been.calledWith("add: third.fsl");
expect(logger.stdout).to.have.been.calledWith(
"Pull makes the following changes:"
"Pull will make the following changes:"
);
// expect(writeSchemaFiles).to.have.been.calledWith([{
// }])
@@ -139,7 +143,7 @@ describe("schema pull", function () {

it("can be cancelled by the user without modifying the filesystem", async function () {
const gatherFSL = container.resolve("gatherFSL");
gatherFSL.resolves("");
gatherFSL.resolves([]);

// user rejects the changes in the interactive prompt
confirm.resolves(false);
@@ -167,7 +171,7 @@ describe("schema pull", function () {
expect(logger.stdout).to.have.been.calledWith("add: second.fsl");
expect(logger.stdout).to.have.been.calledWith("add: third.fsl");
expect(logger.stdout).to.have.been.calledWith(
"Pull makes the following changes:"
"Pull will make the following changes:"
);
expect(logger.stdout).to.have.been.calledWith("Change cancelled");
expect(fs.writeFile).to.have.not.been.called;
12 changes: 11 additions & 1 deletion yargs-test/schema/push.mjs
Original file line number Diff line number Diff line change
@@ -16,7 +16,6 @@ describe("schema push", function () {

it("can force push schema", async function () {
const fetch = container.resolve("fetch");
fetch.resolves({ json: async () => ({}) });

const gatherFSL = container.resolve("gatherFSL");
gatherFSL.resolves(
@@ -48,5 +47,16 @@ describe("schema push", function () {

it.skip("can be cancelled by the user before making mutating network calls", async function () {});

it("can push schema from another directory", async function () {
const gatherFSL = container.resolve("gatherFSL");

await run(
`schema push --secret "secret" --force --dir "/absolute/path/elsewhere"`,
container
);

expect(gatherFSL).to.have.been.calledWith("/absolute/path/elsewhere");
});

it.skip("warns when attempting to push an empty diff", async function () {});
});