Skip to content

Commit d1462ba

Browse files
committed
add extra params to customFunction
1 parent c0570e7 commit d1462ba

File tree

3 files changed

+83
-25
lines changed

3 files changed

+83
-25
lines changed

convex/testingFunctions.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ export const testingQuery = customQuery(query, {
2121

2222
export const testingMutation = customMutation(mutation, {
2323
args: {},
24-
input: async (_ctx, _args) => {
24+
input: async (_ctx, _args, { devOnly }: { devOnly: boolean }) => {
2525
if (process.env.IS_TEST === undefined) {
2626
throw new Error(
2727
"Calling a test only function in an unexpected environment",
2828
);
2929
}
30+
if (devOnly && process.env.IS_PROD) {
31+
throw new Error("This function is only available in development");
32+
}
3033
return { ctx: {}, args: {} };
3134
},
3235
});
@@ -43,13 +46,16 @@ export const testingAction = customAction(action, {
4346
},
4447
});
4548

46-
export const clearAll = testingMutation(async ({ db, scheduler, storage }) => {
47-
for (const table of Object.keys(schema.tables)) {
48-
const docs = await db.query(table as any).collect();
49-
await Promise.all(docs.map((doc) => db.delete(doc._id)));
50-
}
51-
const scheduled = await db.system.query("_scheduled_functions").collect();
52-
await Promise.all(scheduled.map((s) => scheduler.cancel(s._id)));
53-
const storedFiles = await db.system.query("_storage").collect();
54-
await Promise.all(storedFiles.map((s) => storage.delete(s._id)));
49+
export const clearAll = testingMutation({
50+
devOnly: true,
51+
handler: async ({ db, scheduler, storage }) => {
52+
for (const table of Object.keys(schema.tables)) {
53+
const docs = await db.query(table as any).collect();
54+
await Promise.all(docs.map((doc) => db.delete(doc._id)));
55+
}
56+
const scheduled = await db.system.query("_scheduled_functions").collect();
57+
await Promise.all(scheduled.map((s) => scheduler.cancel(s._id)));
58+
const storedFiles = await db.system.query("_storage").collect();
59+
await Promise.all(storedFiles.map((s) => storage.delete(s._id)));
60+
},
5561
});

packages/convex-helpers/server/customFunctions.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,22 @@ export const outerRemoves = outerRemover({
359359
},
360360
});
361361

362+
/**
363+
* Adding extra args to `input`
364+
*/
365+
const extraArgQueryBuilder = customQuery(query, {
366+
args: { a: v.string() },
367+
input: async (_ctx, args, { extraArg }: { extraArg: string }) => ({ ctx: {extraArg}, args }),
368+
});
369+
export const extraArgQuery = extraArgQueryBuilder({
370+
args: {},
371+
extraArg: "foo",
372+
handler: async (ctx, args) => {
373+
return { ctxA: ctx.extraArg };
374+
},
375+
});
376+
queryMatches(extraArgQuery, {}, { ctxA: "foo" });
377+
362378
/**
363379
* Test helpers
364380
*/
@@ -388,6 +404,7 @@ const testApi: ApiFromModules<{
388404
create: typeof create;
389405
outerAdds: typeof outerAdds;
390406
outerRemoves: typeof outerRemoves;
407+
extraArgQuery: typeof extraArgQuery;
391408
};
392409
}>["fns"] = anyApi["customFunctions.test"] as any;
393410

@@ -569,3 +586,12 @@ describe("nested custom functions", () => {
569586
).rejects.toThrow("Validator error: Expected `string`");
570587
});
571588
});
589+
590+
describe("extra args", () => {
591+
test("add extra args", async () => {
592+
const t = convexTest(schema, modules);
593+
expect(await t.query(testApi.extraArgQuery, { a: "foo" })).toMatchObject({
594+
ctxA: "foo",
595+
});
596+
});
597+
});

packages/convex-helpers/server/customFunctions.ts

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ export type Mod<
5151
ModArgsValidator extends PropertyValidators,
5252
ModCtx extends Record<string, any>,
5353
ModMadeArgs extends Record<string, any>,
54+
ExtraArgs extends Record<string, any> = {},
5455
> = {
5556
args: ModArgsValidator;
5657
input: (
5758
ctx: Ctx,
5859
args: ObjectType<ModArgsValidator>,
60+
extra: ExtraArgs,
5961
) =>
6062
| Promise<{ ctx: ModCtx; args: ModMadeArgs }>
6163
| { ctx: ModCtx; args: ModMadeArgs };
@@ -147,17 +149,25 @@ export function customQuery<
147149
ModMadeArgs extends Record<string, any>,
148150
Visibility extends FunctionVisibility,
149151
DataModel extends GenericDataModel,
152+
ExtraArgs extends Record<string, any> = {},
150153
>(
151154
query: QueryBuilder<DataModel, Visibility>,
152-
mod: Mod<GenericQueryCtx<DataModel>, ModArgsValidator, ModCtx, ModMadeArgs>,
155+
mod: Mod<
156+
GenericQueryCtx<DataModel>,
157+
ModArgsValidator,
158+
ModCtx,
159+
ModMadeArgs,
160+
ExtraArgs
161+
>,
153162
) {
154163
return customFnBuilder(query, mod) as CustomBuilder<
155164
"query",
156165
ModArgsValidator,
157166
ModCtx,
158167
ModMadeArgs,
159168
GenericQueryCtx<DataModel>,
160-
Visibility
169+
Visibility,
170+
ExtraArgs
161171
>;
162172
}
163173

@@ -219,13 +229,15 @@ export function customMutation<
219229
ModMadeArgs extends Record<string, any>,
220230
Visibility extends FunctionVisibility,
221231
DataModel extends GenericDataModel,
232+
ExtraArgs extends Record<string, any> = {},
222233
>(
223234
mutation: MutationBuilder<DataModel, Visibility>,
224235
mod: Mod<
225236
GenericMutationCtx<DataModel>,
226237
ModArgsValidator,
227238
ModCtx,
228-
ModMadeArgs
239+
ModMadeArgs,
240+
ExtraArgs
229241
>,
230242
) {
231243
return customFnBuilder(mutation, mod) as CustomBuilder<
@@ -234,7 +246,8 @@ export function customMutation<
234246
ModCtx,
235247
ModMadeArgs,
236248
GenericMutationCtx<DataModel>,
237-
Visibility
249+
Visibility,
250+
ExtraArgs
238251
>;
239252
}
240253

@@ -298,44 +311,55 @@ export function customAction<
298311
ModMadeArgs extends Record<string, any>,
299312
Visibility extends FunctionVisibility,
300313
DataModel extends GenericDataModel,
314+
ExtraArgs extends Record<string, any> = {},
301315
>(
302316
action: ActionBuilder<DataModel, Visibility>,
303-
mod: Mod<GenericActionCtx<DataModel>, ModArgsValidator, ModCtx, ModMadeArgs>,
317+
mod: Mod<
318+
GenericActionCtx<DataModel>,
319+
ModArgsValidator,
320+
ModCtx,
321+
ModMadeArgs,
322+
ExtraArgs
323+
>,
304324
): CustomBuilder<
305325
"action",
306326
ModArgsValidator,
307327
ModCtx,
308328
ModMadeArgs,
309329
GenericActionCtx<DataModel>,
310-
Visibility
330+
Visibility,
331+
ExtraArgs
311332
> {
312333
return customFnBuilder(action, mod) as CustomBuilder<
313334
"action",
314335
ModArgsValidator,
315336
ModCtx,
316337
ModMadeArgs,
317338
GenericActionCtx<DataModel>,
318-
Visibility
339+
Visibility,
340+
ExtraArgs
319341
>;
320342
}
321343

322344
function customFnBuilder(
323345
builder: (args: any) => any,
324-
mod: Mod<any, any, any, any>,
346+
mod: Mod<any, any, any, any, any>,
325347
) {
326348
// Looking forward to when input / args / ... are optional
327349
const inputMod = mod.input ?? NoOp.input;
328350
const inputArgs = mod.args ?? NoOp.args;
329351
return function customBuilder(fn: any): any {
330-
const handler = fn.handler ?? fn;
331-
if ("args" in fn) {
352+
// N.B.: This is fine if it's a function
353+
const { args, handler = fn, returns, ...extra } = fn;
354+
if (args) {
332355
return builder({
333-
args: addArgs(fn.args, inputArgs),
334-
returns: fn.returns,
356+
args: addArgs(args, inputArgs),
357+
returns,
335358
handler: async (ctx: any, allArgs: any) => {
336359
const added = await inputMod(
337360
ctx,
338361
pick(allArgs, Object.keys(inputArgs)) as any,
362+
extra,
339363
);
340364
const args = omit(allArgs, Object.keys(inputArgs));
341365
return handler({ ...ctx, ...added.ctx }, { ...args, ...added.args });
@@ -351,7 +375,7 @@ function customFnBuilder(
351375
return builder({
352376
returns: fn.returns,
353377
handler: async (ctx: any, args: any) => {
354-
const added = await inputMod(ctx, args);
378+
const added = await inputMod(ctx, args, extra);
355379
return handler({ ...ctx, ...added.ctx }, { ...args, ...added.args });
356380
},
357381
});
@@ -430,6 +454,7 @@ export type CustomBuilder<
430454
ModMadeArgs extends Record<string, any>,
431455
InputCtx,
432456
Visibility extends FunctionVisibility,
457+
ExtraArgs extends Record<string, any>,
433458
> = {
434459
<
435460
ArgsValidator extends PropertyValidators | void | Validator<any, any, any>,
@@ -439,14 +464,14 @@ export type CustomBuilder<
439464
ArgsArrayForOptionalValidator<ArgsValidator> = DefaultArgsForOptionalValidator<ArgsValidator>,
440465
>(
441466
func:
442-
| {
467+
| ({
443468
args?: ArgsValidator;
444469
returns?: ReturnsValidator;
445470
handler: (
446471
ctx: Overwrite<InputCtx, ModCtx>,
447472
...args: ArgsForHandlerType<OneOrZeroArgs, ModMadeArgs>
448473
) => ReturnValue;
449-
}
474+
} & ExtraArgs)
450475
| {
451476
(
452477
ctx: Overwrite<InputCtx, ModCtx>,
@@ -474,6 +499,7 @@ export type CustomCtx<Builder> =
474499
infer ModCtx,
475500
any,
476501
infer InputCtx,
502+
any,
477503
any
478504
>
479505
? Overwrite<InputCtx, ModCtx>

0 commit comments

Comments
 (0)