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

implement basic path completions #438

Merged
merged 1 commit into from
Dec 14, 2024
Merged

implement basic path completions #438

merged 1 commit into from
Dec 14, 2024

Conversation

echo-bravo-yahoo
Copy link
Collaborator

@echo-bravo-yahoo echo-bravo-yahoo commented Nov 26, 2024

this change adds:

  • the default yargs completions for commands, options, etc.
  • custom completions for profiles (given that you have a config specified)
  • custom completions for database path (given that you have specified enough information to make a request to fauna)

@echo-bravo-yahoo echo-bravo-yahoo force-pushed the completions branch 3 times, most recently from d982c34 to c41cda0 Compare December 2, 2024 20:56
@echo-bravo-yahoo echo-bravo-yahoo force-pushed the completions branch 12 times, most recently from b6cadb9 to e11d0a4 Compare December 12, 2024 20:08
@echo-bravo-yahoo echo-bravo-yahoo marked this pull request as ready for review December 12, 2024 20:08
@echo-bravo-yahoo echo-bravo-yahoo requested a review from a team as a code owner December 12, 2024 20:08
Comment on lines +60 to +68
export async function listDatabases(argv) {
let databases;
if (argv.secret) {
databases = await listDatabasesWithSecret(argv);
} else {
databases = await listDatabasesWithAccountAPI(argv);
}
return databases;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored this so listDatabases is a generic API layer, and the rendering is done in doListDatabases, which is the command handler

Comment on lines +212 to +225
/**
* retry an assertion repeatedly until it succeeds
* @param {function} evaluator - any function that throws if a condition isn't met.
* @param {number} [ms=50] - the number of milliseconds to wait for the condition. set it lower than mocha's timeout to re-throw the underlying error and have usable test failure logs.
*/
export async function eventually(evaluator, ms = 50) {
try {
return evaluator();
} catch (e) {
if (ms <= 0) throw e;
await new Promise((resolve) => setTimeout(resolve, 1)); // eslint-disable-line no-promise-executor-return
return eventually(evaluator, ms - 1);
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this helper because deep sigh yargs' parse command resolves the promise it returns when it's done parsing... which almost always happens before it's done evaluating async completions. so we can't rely on awaiting the parse promise like we do everywhere else.

Comment on lines +120 to +159
.completion(
"completion",
async function (currentWord, argv, defaultCompletions, done) {
// this is pretty hard to debug - if you need to, run
// `fauna --get-yargs-completions <command> <flag> <string to match>`
// for example: `fauna --get-yargs-completions --profile he`
// note that you need to have empty quotes to get all matches:
// `fauna --get-yargs-completions --profile ""`

// then, call the done callback with an array of strings for debugging, like:
// done(
// [
// `currentWord: ${currentWord}, currentWordFlag: ${currentWordFlag}, argv: ${JSON.stringify(argv)}`,
// ],
// );
const previousWord = process.argv.slice(-2, -1)[0].replace(/-/g, "");
const currentWordFlag = Object.keys(argv)
.filter((key) => previousWord === key)
.pop();

// TODO: this doesn't handle aliasing, and it needs to
if (
currentWord === "--profile" ||
currentWordFlag === "profile" ||
currentWord === "-p" ||
currentWordFlag === "p"
) {
done(getProfileCompletions(currentWord, argv));
} else if (
currentWord === "--database" ||
currentWordFlag === "database" ||
currentWord === "-d" ||
currentWordFlag === "d"
) {
done(await getDbCompletions(currentWord, argv));
} else {
defaultCompletions();
}
},
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the meat of the change: yargs calls this method when zsh/bash calls fauna --get-yargs-completions ..., and then we do some switching based on yargs argv and process.argv and call methods that either sync or async return a list of strings.

we have to use process.argv as well because yargs argv doesn't include the order of the tokens, so we can't easily find currentWordFlag...

return regionGroups;
} else {
const { pageSize } = argv;
buildCredentials({ ...argv, user: "default" });
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a glorious, if silly, hack for when we need credentials for making network calls (usually before the credentials middleware runs): just import and run the credential middleware.

const { pageSize } = argv;
buildCredentials({ ...argv, user: "default" });
const accountClient = new FaunaAccountClient();
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get some docstrings detailing the order of operations here for future us. Looks like we query exactly path, and one directory down if that doesn't work?

if (!getRegionGroup(currentWord)) {
return regionGroups;
} else {
const { pageSize } = argv;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to debounce this code path. I pushed tab 5 times waiting for a response...which means I probably made >= 5 requests for the same exact data.

Copy link
Collaborator Author

@echo-bravo-yahoo echo-bravo-yahoo Dec 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not really debounceable in a straightforward fashion - zsh invokes fauna once per TAB input, fauna runs as a process briefly, outputs the completions to stdout (which zsh reads), then exits.

since we don't have a persistent process, the only paths i see for debouncing are to customize the zsh completion script to debounce at the zsh wrapper layer, or to write/read a debounce timestamp to/from disk. both feel really icky from a maintenance, complexity, and shell portability standpoint.

i think we should have confidence in our rate limiting / service layer and just use the existing throttle protections.

this change adds:
- the default yargs completions for commands, options, etc.
- custom completions for profiles (given that you have a config
  specified)
- custom completions for database path (given that you have specified
  enough information to make a request to fauna)
@ecooper ecooper merged commit 507a72b into v3 Dec 14, 2024
4 checks passed
@ecooper ecooper deleted the completions branch December 14, 2024 00:13
@mwilde345 mwilde345 mentioned this pull request Dec 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants