Refactor billing code and add error monitoring

Refactored the code that retrieves the billing customer id by renaming the function getBillingCustomerId to getCustomerId. Also, bolstered error handling: implemented exception capture in particular scenarios across multiple files. If an error occurs, it's now captured and reported to the configured provider.
This commit is contained in:
giancarlo
2024-04-25 13:19:46 +07:00
parent 8d04624b1d
commit 03d06b64ba
10 changed files with 60 additions and 14 deletions

View File

@@ -32,6 +32,7 @@ The `enhanceAction` function takes two arguments:
The options object can contain the following properties:
- `captcha` - If true, the action will require a captcha to be passed to the body as `captchaToken`
- `schema` - A zod schema that the data will be validated against
- `captureException` - If true, the action will capture exceptions and report them to the configured provider. It is `true` by default.
When successfully called, the action will return the result of the action function.
@@ -70,10 +71,11 @@ export const POST = enhanceRouteHandler(({ request, body, user }) => {
// "user" is the user object from the session
// "request" is the raw request object passed by POST
// if "captcha" is true, the action will require a captcha
// if "captureException" is true, the action will capture exceptions and report them to the configured provider
}, {
captcha: true,
captureException: true,
schema: z.object({
id: z.number()
}),

View File

@@ -16,6 +16,7 @@
"devDependencies": {
"@kit/auth": "workspace:^",
"@kit/eslint-config": "workspace:*",
"@kit/monitoring": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/supabase": "workspace:^",
"@kit/tailwind-config": "workspace:*",

View File

@@ -10,7 +10,7 @@ import { verifyCaptchaToken } from '@kit/auth/captcha/server';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
import { zodParseFactory } from '../utils';
import { captureException, zodParseFactory } from '../utils';
/**
*
@@ -22,9 +22,10 @@ export function enhanceAction<
Schema extends z.ZodType<Omit<Args, 'captchaToken'>, z.ZodTypeDef>,
Response,
>(
fn: (params: z.infer<Schema>, user: User) => Response,
fn: (params: z.infer<Schema>, user: User) => Response | Promise<Response>,
config: {
captcha?: boolean;
captureException?: boolean;
schema: Schema;
},
) {
@@ -52,7 +53,20 @@ export function enhanceAction<
const parsed = zodParseFactory(config.schema);
const data = parsed(params);
// pass the data to the action
return fn(data, auth.data);
// capture exceptions if required
const shouldCaptureException = config.captureException ?? true;
if (shouldCaptureException) {
try {
return await fn(data, auth.data);
} catch (error) {
await captureException(error);
throw error;
}
} else {
// pass the data to the action
return fn(data, auth.data);
}
};
}

View File

@@ -11,7 +11,7 @@ import { verifyCaptchaToken } from '@kit/auth/captcha/server';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-client';
import { zodParseFactory } from '../utils';
import { captureException, zodParseFactory } from '../utils';
interface HandlerParams<Body> {
request: NextRequest;
@@ -53,6 +53,7 @@ export const enhanceRouteHandler = <
// Parameters object
params?: {
captcha?: boolean;
captureException?: boolean;
schema?: Schema;
},
) => {
@@ -92,8 +93,21 @@ export const enhanceRouteHandler = <
body = zodParseFactory(params.schema)(body);
}
// all good, call the handler with the request, body and user
return handler({ request, body, user });
const shouldCaptureException = params?.captureException ?? true;
if (shouldCaptureException) {
try {
return await handler({ request, body, user });
} catch (error) {
// capture the exception
await captureException(error);
throw error;
}
} else {
// all good, call the handler with the request, body and user
return handler({ request, body, user });
}
};
};

View File

@@ -12,3 +12,14 @@ export const zodParseFactory =
throw new Error(`Invalid data: ${err as string}`);
}
};
export async function captureException(exception: unknown) {
const { getServerMonitoringService } = await import('@kit/monitoring/server');
const service = await getServerMonitoringService();
const error =
exception instanceof Error ? exception : new Error(exception as string);
return service.captureException(error);
}