Skip to content

Commit

Permalink
Improve usability of npx convex run (#32706)
Browse files Browse the repository at this point in the history
A few small quality of life improvements:
* `npx convex run api.foo.bar` will be equivalent to `npx convex run foo:bar`
* `npx convex run convex/foo/bar` will be equivalent to `npx convex run foo/bar:default` -- this is useful since the file path usually autocompletes in the terminal.
* Support for `--identity` similar to the dashboard "acting as user" feature, so `npx convex run --identity '{ name: "sshader" }'` fakes out `auth.getUserIdentity` in my function as `{ name: "sshader" }`
* And nicer JSON parsing using JSON5, so things like `{ name: "sshader" }` actually parses instead of requiring the keys to all be quoted

GitOrigin-RevId: cab1d0469ab9133a019feecb885b12e861b07966
  • Loading branch information
sshader authored and Convex, Inc. committed Jan 3, 2025
1 parent 85cffca commit 676fddd
Show file tree
Hide file tree
Showing 14 changed files with 403 additions and 210 deletions.
18 changes: 11 additions & 7 deletions crates/local_backend/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,18 @@ fn must_be_admin_internal(
identity: &Identity,
needs_write_access: bool,
) -> anyhow::Result<AdminIdentityPrincipal> {
if let Identity::InstanceAdmin(admin_identity) = identity {
if needs_write_access && admin_identity.is_read_only() {
return Err(read_only_admin_key_error().into());
}
Ok(admin_identity.principal().clone())
} else {
Err(bad_admin_key_error(identity.instance_name()).into())
let admin_identity = match identity {
Identity::InstanceAdmin(admin_identity) => admin_identity,
Identity::ActingUser(admin_identity, _user_identity_attributes) => admin_identity,
Identity::System(_) | Identity::User(_) | Identity::Unknown => {
return Err(bad_admin_key_error(identity.instance_name()).into());
},
};

if needs_write_access && admin_identity.is_read_only() {
return Err(read_only_admin_key_error().into());
}
Ok(admin_identity.principal().clone())
}

pub fn must_be_admin_member_with_write_access(identity: &Identity) -> anyhow::Result<MemberId> {
Expand Down
3 changes: 3 additions & 0 deletions npm-packages/common/config/rush/pnpm-lock.yaml

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

1 change: 1 addition & 0 deletions npm-packages/convex/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@
"inquirer": "^9.1.4",
"inquirer-search-list": "~1.2.6",
"jsdom": "~25.0.1",
"json5": "~2.2.3",
"jwt-encode": "~1.0.1",
"knip": "~5.39.4",
"napi-wasm": "1.1.3",
Expand Down
15 changes: 7 additions & 8 deletions npm-packages/convex/src/cli/convexExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,14 @@ async function waitForStableExportState(
): Promise<SnapshotExportState> {
const [donePromise, onDone] = waitUntilCalled();
let snapshotExportState: SnapshotExportState;
await subscribe(
ctx,
await subscribe(ctx, {
deploymentUrl,
adminKey,
"_system/cli/exports:getLatest",
{},
undefined,
donePromise,
{
parsedFunctionName: "_system/cli/exports:getLatest",
parsedFunctionArgs: {},
componentPath: undefined,
until: donePromise,
callbacks: {
onChange: (value: any) => {
// NOTE: `value` would only be `null` if there has never been an export
// requested.
Expand All @@ -168,7 +167,7 @@ async function waitForStableExportState(
}
},
},
);
});
return snapshotExportState!;
}

Expand Down
15 changes: 7 additions & 8 deletions npm-packages/convex/src/cli/convexImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,15 +378,14 @@ export async function waitForStableImportState(
const [donePromise, onDone] = waitUntilCalled();
let snapshotImportState: SnapshotImportState;
let checkpointCount = 0;
await subscribe(
ctx,
await subscribe(ctx, {
deploymentUrl,
adminKey,
"_system/cli/queryImport",
{ importId },
undefined,
donePromise,
{
parsedFunctionName: "_system/cli/queryImport",
parsedFunctionArgs: { importId },
componentPath: undefined,
until: donePromise,
callbacks: {
onChange: (value: any) => {
snapshotImportState = value.state;
switch (snapshotImportState.state) {
Expand All @@ -409,7 +408,7 @@ export async function waitForStableImportState(
}
},
},
);
});
return snapshotImportState!;
}

Expand Down
24 changes: 11 additions & 13 deletions npm-packages/convex/src/cli/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
deploymentSelectionFromOptions,
fetchDeploymentCredentialsProvisionProd,
} from "./lib/api.js";
import { runPaginatedQuery } from "./lib/run.js";
import { runSystemPaginatedQuery } from "./lib/run.js";
import { parsePositiveInteger } from "./lib/utils/utils.js";
import { Command } from "@commander-js/extra-typings";
import { actionDescription } from "./lib/command.js";
Expand Down Expand Up @@ -87,14 +87,13 @@ async function listTables(
deploymentName: string | undefined,
componentPath: string,
) {
const tables = (await runPaginatedQuery(
ctx,
const tables = (await runSystemPaginatedQuery(ctx, {
deploymentUrl,
adminKey,
"_system/cli/tables",
functionName: "_system/cli/tables",
componentPath,
{},
)) as { name: string }[];
args: {},
})) as { name: string }[];
if (tables.length === 0) {
logError(
ctx,
Expand All @@ -120,18 +119,17 @@ async function listDocuments(
componentPath: string;
},
) {
const data = (await runPaginatedQuery(
ctx,
const data = (await runSystemPaginatedQuery(ctx, {
deploymentUrl,
adminKey,
"_system/cli/tableData",
options.componentPath,
{
functionName: "_system/cli/tableData",
componentPath: options.componentPath,
args: {
table: tableName,
order: options.order ?? "desc",
},
options.limit + 1,
)) as Record<string, Value>[];
limit: options.limit + 1,
})) as Record<string, Value>[];

if (data.length === 0) {
logError(ctx, "There are no documents in this table.");
Expand Down
17 changes: 8 additions & 9 deletions npm-packages/convex/src/cli/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,22 +252,21 @@ async function deployToNewPreviewDeployment(
logFinishedStep(ctx, `Deployed Convex functions to ${previewUrl}`);

if (options.previewRun !== undefined) {
await runFunctionAndLog(
ctx,
previewUrl,
previewAdminKey,
options.previewRun,
{},
undefined,
{
await runFunctionAndLog(ctx, {
deploymentUrl: previewUrl,
adminKey: previewAdminKey,
functionName: options.previewRun,
argsString: "{}",
componentPath: undefined,
callbacks: {
onSuccess: () => {
logFinishedStep(
ctx,
`Finished running function "${options.previewRun}"`,
);
},
},
);
});
}
}

Expand Down
72 changes: 35 additions & 37 deletions npm-packages/convex/src/cli/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,19 +375,18 @@ async function runFunctionInDev(
functionName: string,
componentPath: string | undefined,
) {
await runFunctionAndLog(
ctx,
credentials.url,
credentials.adminKey,
await runFunctionAndLog(ctx, {
deploymentUrl: credentials.url,
adminKey: credentials.adminKey,
functionName,
{},
argsString: "{}",
componentPath,
{
callbacks: {
onSuccess: () => {
logFinishedStep(ctx, `Finished running function "${functionName}"`);
},
},
);
});
}

function getTableWatch(
Expand All @@ -399,13 +398,13 @@ function getTableWatch(
tableName: string | null,
componentPath: string | undefined,
) {
return getFunctionWatch(
ctx,
credentials,
"_system/cli/queryTable",
() => (tableName !== null ? { tableName } : null),
return getFunctionWatch(ctx, {
deploymentUrl: credentials.url,
adminKey: credentials.adminKey,
parsedFunctionName: "_system/cli/queryTable",
getArgs: () => (tableName !== null ? { tableName } : null),
componentPath,
);
});
}

function getDeplymentEnvVarWatch(
Expand All @@ -416,42 +415,41 @@ function getDeplymentEnvVarWatch(
},
shouldRetryOnDeploymentEnvVarChange: boolean,
) {
return getFunctionWatch(
ctx,
credentials,
"_system/cli/queryEnvironmentVariables",
() => (shouldRetryOnDeploymentEnvVarChange ? {} : null),
undefined,
);
return getFunctionWatch(ctx, {
deploymentUrl: credentials.url,
adminKey: credentials.adminKey,
parsedFunctionName: "_system/cli/queryEnvironmentVariables",
getArgs: () => (shouldRetryOnDeploymentEnvVarChange ? {} : null),
componentPath: undefined,
});
}

function getFunctionWatch(
ctx: WatchContext,
credentials: {
url: string;
args: {
deploymentUrl: string;
adminKey: string;
parsedFunctionName: string;
getArgs: () => Record<string, Value> | null;
componentPath: string | undefined;
},
functionName: string,
getArgs: () => Record<string, Value> | null,
componentPath: string | undefined,
) {
const [stopPromise, stop] = waitUntilCalled();
return {
watch: async () => {
const args = getArgs();
if (args === null) {
const functionArgs = args.getArgs();
if (functionArgs === null) {
return waitForever();
}
let changes = 0;
return subscribe(
ctx,
credentials.url,
credentials.adminKey,
functionName,
args,
componentPath,
stopPromise,
{
return subscribe(ctx, {
deploymentUrl: args.deploymentUrl,
adminKey: args.adminKey,
parsedFunctionName: args.parsedFunctionName,
parsedFunctionArgs: functionArgs,
componentPath: args.componentPath,
until: stopPromise,
callbacks: {
onChange: () => {
changes++;
// First bump is just the initial results reporting
Expand All @@ -460,7 +458,7 @@ function getFunctionWatch(
}
},
},
);
});
},
stop: () => {
stop();
Expand Down
28 changes: 13 additions & 15 deletions npm-packages/convex/src/cli/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
fetchDeploymentCredentialsWithinCurrentProject,
} from "./lib/api.js";
import { actionDescription } from "./lib/command.js";
import { runQuery } from "./lib/run.js";
import { runSystemQuery } from "./lib/run.js";
import {
deploymentFetch,
ensureHasConvexDependency,
Expand Down Expand Up @@ -88,14 +88,13 @@ const envGet = new Command("get")
deploymentSelection,
);

const envVar = (await runQuery(
ctx,
url,
const envVar = (await runSystemQuery(ctx, {
deploymentUrl: url,
adminKey,
"_system/cli/queryEnvironmentVariables:get",
undefined,
{ name: envVarName },
)) as EnvVar | null;
functionName: "_system/cli/queryEnvironmentVariables:get",
componentPath: undefined,
args: { name: envVarName },
})) as EnvVar | null;
if (envVar === null) {
logFailure(ctx, `Environment variable "${envVarName}" not found.`);
return;
Expand Down Expand Up @@ -141,14 +140,13 @@ const envList = new Command("list")
deploymentSelection,
);

const envs = (await runQuery(
ctx,
url,
const envs = (await runSystemQuery(ctx, {
deploymentUrl: url,
adminKey,
"_system/cli/queryEnvironmentVariables",
undefined,
{},
)) as EnvVar[];
functionName: "_system/cli/queryEnvironmentVariables",
componentPath: undefined,
args: {},
})) as EnvVar[];
if (envs.length === 0) {
logMessage(ctx, "No environment variables set.");
return;
Expand Down
13 changes: 6 additions & 7 deletions npm-packages/convex/src/cli/functionSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
deploymentSelectionFromOptions,
fetchDeploymentCredentialsWithinCurrentProject,
} from "./lib/api.js";
import { runQuery } from "./lib/run.js";
import { runSystemQuery } from "./lib/run.js";
import { Command, Option } from "@commander-js/extra-typings";
import { actionDescription } from "./lib/command.js";

Expand All @@ -30,14 +30,13 @@ export const functionSpec = new Command("function-spec")
deploymentSelection,
);

const functions = (await runQuery(
ctx,
const functions = (await runSystemQuery(ctx, {
deploymentUrl,
adminKey,
"_system/cli/modules:apiSpec",
undefined,
{},
)) as any[];
functionName: "_system/cli/modules:apiSpec",
componentPath: undefined,
args: {},
})) as any[];

const output = JSON.stringify(
{ url: deploymentUrl, functions: functions },
Expand Down
Loading

0 comments on commit 676fddd

Please sign in to comment.