Skip to content

Commit

Permalink
handle validation of select better
Browse files Browse the repository at this point in the history
  • Loading branch information
battis committed Dec 9, 2023
1 parent 43f7d20 commit c5c0638
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 38 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
"@inquirer/core": "^3.1.2",
"@inquirer/select": "^1.3.1",
"@inquirer/type": "^1.1.5",
"@types/lodash": "^4.14.202",
"app-root-path": "^3.1.0",
"lodash": "^4.17.21",
"rword": "^3.2.1"
},
"devDependencies": {
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 14 additions & 7 deletions src/billing/projects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@ export async function enable({
account,
projectId
}: { account?: string; projectId?: string } = {}) {
account = await accounts.selectidentifier({ name: account });
// TODO create a new billing account interactively
account = await accounts.selectidentifier({
name: account,
purpose: 'to link to the project'
});
if (account) {
projectId = await rootProjects.selectIdentifier({ projectId });
projectId = await rootProjects.selectIdentifier({
projectId,
purpose: `to link to billing account ${cli.colors.value(account)}`
});
await shell.gcloudBeta(
`billing projects link ${projectId} --billing-account=${account}`,
{ includeProjectIdFlag: false }
);
} else {
// TODO create a new billing account interactively
await cli.prompts.confirm({
message:
'Confirm that you have created a billing account for this project'
});
throw new Error(
`Billing accounts must be create interactively at ${cli.colors.url(
'https://console.cloud.google.com/billing'
)}`
);
}
}
78 changes: 47 additions & 31 deletions src/lib/prompts/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import cli from '@battis/qui-cli';
import Active from '../Active';
import Descriptor from '../Descriptor';
import * as core from './core';
import confirm from './confirm';
import _ from 'lodash';

export async function select<ChoiceType = string, ReturnType = string>({
arg,
Expand All @@ -18,21 +20,29 @@ export async function select<ChoiceType = string, ReturnType = string>({
}: select.Parameters<ChoiceType, ReturnType>): Promise<ReturnType> {
let selection: ChoiceType | undefined;

// handle a potential initial argument
// convert argument to valid selection, if possible
if (arg) {
if (argTransform) {
selection = await argTransform(arg);
} else {
selection = arg as ChoiceType;
}
if (selection && !validate) {
if (transform) {
return transform(selection);
}
}

// skip validation if requested
if (selection && validate === false) {
if (transform) {
return await transform(selection);
} else {
return selection as ReturnType;
}
}

// disqualify selection if invalidated
if (selection && validate instanceof Function && !validate(arg)) {
selection = undefined;
}

// load choices
if (choices instanceof Promise) {
choices = await choices;
Expand All @@ -41,39 +51,47 @@ export async function select<ChoiceType = string, ReturnType = string>({
choices = await choices();
}

// validate selection, if possible
if (selection) {
if (validate === true) {
selection = choices.find(
(choice: select.Choice<ChoiceType>) => choice.value == selection
)?.value;
} else if (validate instanceof Function) {
selection = validate(arg) === true ? selection : undefined;
}
// validate selection, if necessary
if (selection && validate === true) {
selection = choices.find((choice) =>
_.isEqual(choice.value, selection)
)?.value;
}

// create if necessary
if (create && !selection) {
selection = await create(arg);
if (activateIfCreated && active) {
active.activate(selection);
// still no selection, but only a single choice
if (!selection && choices.length === 1) {
// can't create or choose to: reuse existing
if (
!create ||
(await confirm({
message: `${message}${cli.colors.value(
choices[0].name || choices[0].value
)}${core.pad(purpose)}`
}))
) {
selection = choices[0].value;
} else {
// create a new selection
selection = await create(arg);
}
}

// interactively make selection
selection =
selection ||
(await cli.prompts.select({
// interactively make selection if not yet made
if (!selection) {
selection = await cli.prompts.select({
message: `${message}${core.pad(purpose)}`,
choices
}));
choices,
validate,
...rest
});
}

// activate if necessary
// activate selection if necessary
if (active) {
active.activate(selection);
}

// transform if necessary
// transform selection if necessary
if (transform) {
return await transform(selection);
} else {
Expand All @@ -89,20 +107,18 @@ export namespace select {
(ChoiceType extends string
? { argTransform?: never }
: {
argTransform: (
arg: string
) => ChoiceType | Promise<ChoiceType | undefined> | undefined;
argTransform: Transform<string, ChoiceType | undefined>;
}) & {
message: string;
choices: Choices<ChoiceType>;
validate?: boolean | ((value?: string) => boolean | string);
transform?: Transform<ChoiceType, ReturnType>;
active?: ChoiceType extends Descriptor ? Active<ChoiceType> : never;
create?: ChoiceType extends Descriptor ? Create<ChoiceType> : never;
activateIfCreated?: ChoiceType extends Descriptor ? boolean : never;
} & (ChoiceType extends ReturnType
? { transform?: never }
: { transform: Transform<ChoiceType, ReturnType> });

export type Choice<ChoiceType = string> = (ChoiceType extends string
? { name?: string }
: { name: string }) & {
Expand Down

0 comments on commit c5c0638

Please sign in to comment.