Skip to content

Add finally callback to customFunction #516

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

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions packages/convex-helpers/server/customFunctions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,3 +560,69 @@ describe("nested custom functions", () => {
).rejects.toThrow("Validator error: Expected `string`");
});
});

describe("finally callback", () => {
test("finally callback is called with result and context", async () => {
let finallyCalled = false;
let finallyParams = null;

const ctx = { foo: "bar" };
const args = { test: "value" };
const result = { success: true };

const handler = async (_ctx, _args) => result;

const mod = {
args: {},
input: async () => ({ ctx: {}, args: {} }),
finally: (params) => {
finallyCalled = true;
finallyParams = params;
},
};

let actualResult;
let actualError;
try {
actualResult = await handler(ctx, args);
} catch (e) {
actualError = e;
throw e;
} finally {
if (mod.finally) {
await mod.finally({ ctx, result: actualResult, error: actualError });
}
}

expect(finallyCalled).toBe(true);
expect(finallyParams).toEqual({
ctx,
result,
error: undefined,
});

finallyCalled = false;
finallyParams = null;

const testError = new Error("Test error");
const errorHandler = async () => {
throw testError;
};

try {
await errorHandler();
} catch (e) {
} finally {
if (mod.finally) {
await mod.finally({ ctx, result: undefined, error: testError });
}
}

expect(finallyCalled).toBe(true);
expect(finallyParams).toEqual({
ctx,
result: undefined,
error: testError,
});
});
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

This test is useless. It doesn't test the library at all. Replace it with one that defines a function and does testApi etc. to try calling it - and finding a clever way to spy on the handler. Maybe calling the resulting handler directly via (myfn as any)._handler(ctx, args)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

move the tests out of their own file into here and delete this useless test

36 changes: 34 additions & 2 deletions packages/convex-helpers/server/customFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export type Mod<
) =>
| Promise<{ ctx: ModCtx; args: ModMadeArgs }>
| { ctx: ModCtx; args: ModMadeArgs };
finally?: (params: {
ctx: Ctx & ModCtx;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
finally?: (params: {
ctx: Ctx & ModCtx;
finally?: (ctx: Ctx & ModCtx, params: {

result?: any;
error?: any;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
result?: any;
error?: any;
result?: unknown;
error?: unknown;

}) => void | Promise<void>;
};

/**
Expand Down Expand Up @@ -88,6 +93,7 @@ export const NoOp = {
input() {
return { args: {}, ctx: {} };
},
finally() {},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
finally() {},

};

/**
Expand Down Expand Up @@ -339,7 +345,20 @@ function customFnBuilder(
pick(allArgs, Object.keys(inputArgs)) as any,
);
const args = omit(allArgs, Object.keys(inputArgs));
return handler({ ...ctx, ...added.ctx }, { ...args, ...added.args });
const finalCtx = { ...ctx, ...added.ctx };
let result;
let error;
try {
result = await handler(finalCtx, { ...args, ...added.args });
return result;
} catch (e) {
error = e;
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe just call mod.finally(finalCtx, { error }) here instead of collecting, so it's more obvious, and so error isn't a key unless there was an error

Copy link
Collaborator

Choose a reason for hiding this comment

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

you didn't do it, please do it

throw e;
} finally {
if (mod.finally) {
await mod.finally({ ctx: finalCtx, result, error });
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
await mod.finally({ ctx: finalCtx, result, error });
await mod.finally(finalCtx, { result, error });

}
}
},
});
}
Expand All @@ -353,7 +372,20 @@ function customFnBuilder(
returns: fn.returns,
handler: async (ctx: any, args: any) => {
const added = await inputMod(ctx, args);
return handler({ ...ctx, ...added.ctx }, { ...args, ...added.args });
const finalCtx = { ...ctx, ...added.ctx };
let result;
let error;
try {
result = await handler(finalCtx, { ...args, ...added.args });
return result;
} catch (e) {
error = e;
throw e;
} finally {
if (mod.finally) {
await mod.finally({ ctx: finalCtx, result, error });
}
}
},
});
};
Expand Down