Skip to content

Commit

Permalink
wrap function handlers to support independently exported handler (#30…
Browse files Browse the repository at this point in the history
…078)

i've been doing this pattern so i can call my handler from other functions and from unit tests

```ts
export async function listHandler(ctx: QueryCtx) {
  return await ctx.db.query("messages").collect();
};

export const list = query({
  handler: listHandler,
});
```

unfortunately, this makes both `list` and `listHandler` get analyzed as callable queries -- in the dashboard function view, the function runner, `npx convex run`, in components codegen, etc.
l
To fix, we can avoid `query`, `mutation`, and friends modifying the handler in place. We re-wrap in a separate function, and attach the handler as `.handler` on this separate function so analyze can get source maps from it.

Tested that the dashboard still finds the function just fine.

<img width="1321" alt="Screenshot 2024-09-23 at 5 30 25 PM" src="https://github.com/user-attachments/assets/c6e37cc1-110b-4097-b817-661f1917e62d">

GitOrigin-RevId: ff0ecfdd44bf40f38ef9c377d86694b3747c15a4
  • Loading branch information
ldanilek authored and Convex, Inc. committed Sep 30, 2024
1 parent 58dc97e commit 84a8bba
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 7 deletions.
41 changes: 34 additions & 7 deletions src/server/impl/registration_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,13 @@ function exportReturns(functionDefinition: FunctionDefinition) {
export const mutationGeneric: MutationBuilder<any, "public"> = ((
functionDefinition: FunctionDefinition,
) => {
const func = (
const handler = (
typeof functionDefinition === "function"
? functionDefinition
: functionDefinition.handler
) as RegisteredMutation<"public", any, any>;
const func = ((ctx: any, args: any) =>
handler(ctx, args)) as RegisteredMutation<"public", any, any>;

// Helpful runtime check that functions are only be registered once
if (func.isRegistered) {
Expand All @@ -190,6 +192,7 @@ export const mutationGeneric: MutationBuilder<any, "public"> = ((
func.invokeMutation = (argsStr) => invokeMutation(func, argsStr);
func.exportArgs = exportArgs(functionDefinition);
func.exportReturns = exportReturns(functionDefinition);
func._handler = handler;
return func;
}) as MutationBuilder<any, "public">;

Expand All @@ -209,11 +212,13 @@ export const mutationGeneric: MutationBuilder<any, "public"> = ((
export const internalMutationGeneric: MutationBuilder<any, "internal"> = ((
functionDefinition: FunctionDefinition,
) => {
const func = (
const handler = (
typeof functionDefinition === "function"
? functionDefinition
: functionDefinition.handler
) as RegisteredMutation<"internal", any, any>;
const func = ((ctx: any, args: any) =>
handler(ctx, args)) as RegisteredMutation<"internal", any, any>;

// Helpful runtime check that functions are only be registered once
if (func.isRegistered) {
Expand All @@ -226,6 +231,7 @@ export const internalMutationGeneric: MutationBuilder<any, "internal"> = ((
func.invokeMutation = (argsStr) => invokeMutation(func, argsStr);
func.exportArgs = exportArgs(functionDefinition);
func.exportReturns = exportReturns(functionDefinition);
func._handler = handler;
return func;
}) as MutationBuilder<any, "internal">;

Expand Down Expand Up @@ -263,11 +269,16 @@ async function invokeQuery<
export const queryGeneric: QueryBuilder<any, "public"> = ((
functionDefinition: FunctionDefinition,
) => {
const func = (
const handler = (
typeof functionDefinition === "function"
? functionDefinition
: functionDefinition.handler
) as RegisteredQuery<"public", any, any>;
const func = ((ctx: any, args: any) => handler(ctx, args)) as RegisteredQuery<
"public",
any,
any
>;

// Helpful runtime check that functions are only be registered once
if (func.isRegistered) {
Expand All @@ -280,6 +291,7 @@ export const queryGeneric: QueryBuilder<any, "public"> = ((
func.invokeQuery = (argsStr) => invokeQuery(func, argsStr);
func.exportArgs = exportArgs(functionDefinition);
func.exportReturns = exportReturns(functionDefinition);
func._handler = handler;
return func;
}) as QueryBuilder<any, "public">;

Expand All @@ -299,11 +311,16 @@ export const queryGeneric: QueryBuilder<any, "public"> = ((
export const internalQueryGeneric: QueryBuilder<any, "internal"> = ((
functionDefinition: FunctionDefinition,
) => {
const func = (
const handler = (
typeof functionDefinition === "function"
? functionDefinition
: functionDefinition.handler
) as RegisteredQuery<"internal", any, any>;
const func = ((ctx: any, args: any) => handler(ctx, args)) as RegisteredQuery<
"internal",
any,
any
>;

// Helpful runtime check that functions are only be registered once
if (func.isRegistered) {
Expand All @@ -316,6 +333,7 @@ export const internalQueryGeneric: QueryBuilder<any, "internal"> = ((
func.invokeQuery = (argsStr) => invokeQuery(func as any, argsStr);
func.exportArgs = exportArgs(functionDefinition);
func.exportReturns = exportReturns(functionDefinition);
func._handler = handler;
return func;
}) as QueryBuilder<any, "internal">;

Expand Down Expand Up @@ -349,11 +367,13 @@ async function invokeAction<
export const actionGeneric: ActionBuilder<any, "public"> = ((
functionDefinition: FunctionDefinition,
) => {
const func = (
const handler = (
typeof functionDefinition === "function"
? functionDefinition
: functionDefinition.handler
) as RegisteredAction<"public", any, any>;
const func = ((ctx: any, args: any) =>
handler(ctx, args)) as RegisteredAction<"public", any, any>;

// Helpful runtime check that functions are only be registered once
if (func.isRegistered) {
Expand All @@ -367,6 +387,7 @@ export const actionGeneric: ActionBuilder<any, "public"> = ((
invokeAction(func, requestId, argsStr);
func.exportArgs = exportArgs(functionDefinition);
func.exportReturns = exportReturns(functionDefinition);
func._handler = handler;
return func;
}) as ActionBuilder<any, "public">;

Expand All @@ -384,11 +405,13 @@ export const actionGeneric: ActionBuilder<any, "public"> = ((
export const internalActionGeneric: ActionBuilder<any, "internal"> = ((
functionDefinition: FunctionDefinition,
) => {
const func = (
const handler = (
typeof functionDefinition === "function"
? functionDefinition
: functionDefinition.handler
) as RegisteredAction<"internal", any, any>;
const func = ((ctx: any, args: any) =>
handler(ctx, args)) as RegisteredAction<"internal", any, any>;

// Helpful runtime check that functions are only be registered once
if (func.isRegistered) {
Expand All @@ -402,6 +425,7 @@ export const internalActionGeneric: ActionBuilder<any, "internal"> = ((
invokeAction(func, requestId, argsStr);
func.exportArgs = exportArgs(functionDefinition);
func.exportReturns = exportReturns(functionDefinition);
func._handler = handler;
return func;
}) as ActionBuilder<any, "internal">;

Expand Down Expand Up @@ -437,7 +461,9 @@ export const httpActionGeneric = (
request: Request,
) => Promise<Response>,
): PublicHttpAction => {
const q = func as unknown as PublicHttpAction;
const handler = func as unknown as PublicHttpAction;
const q = ((ctx: any, request: any) =>
handler(ctx, request)) as PublicHttpAction;
// Helpful runtime check that functions are only be registered once
if (q.isRegistered) {
throw new Error("Function registered twice " + func);
Expand All @@ -446,6 +472,7 @@ export const httpActionGeneric = (
q.isRegistered = true;
q.isHttp = true;
q.invokeHttpAction = (request) => invokeHttpAction(func as any, request);
q._handler = func;
return q;
};

Expand Down
11 changes: 11 additions & 0 deletions src/server/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ export type RegisteredMutation<

/** @internal */
exportReturns(): string;

/** @internal */
_handler: (ctx: GenericMutationCtx<any>, args: Args) => Returns;
} & VisibilityProperties<Visibility>;

/**
Expand Down Expand Up @@ -392,6 +395,9 @@ export type RegisteredQuery<

/** @internal */
exportReturns(): string;

/** @internal */
_handler: (ctx: GenericQueryCtx<any>, args: Args) => Returns;
} & VisibilityProperties<Visibility>;

/**
Expand Down Expand Up @@ -421,6 +427,9 @@ export type RegisteredAction<

/** @internal */
exportReturns(): string;

/** @internal */
_handler: (ctx: GenericActionCtx<any>, args: Args) => Returns;
} & VisibilityProperties<Visibility>;

/**
Expand All @@ -438,6 +447,8 @@ export type PublicHttpAction = {

/** @internal */
invokeHttpAction(request: Request): Promise<Response>;
/** @internal */
_handler: (ctx: GenericActionCtx<any>, request: Request) => Promise<Response>;
};

/**
Expand Down

0 comments on commit 84a8bba

Please sign in to comment.