Improve userFriendlyMessage devX (#16815)
Two challenges with error messages - always provide a useful/meaningful error message for the end user instead of the generic one. eg: show "Wrong password" and not "An error occured" - avoid technical details unless error regards a technical feature. eg: show "An error occured" and not "Invalid post-hook payload."; but do show "Invalid issuer URL." as it occurs while configuring SSO What this PR does - Make userFriendlyMessage mandatory for widely used GraphqlQueryRunnerException and CommonQueryRunnerException, so that developers are forced to ask themselves what the error message should be, and as it contains very wide error codes (eg: "Bad request") which should not be mapped to just one default message - Keep userFriendlyMessage optional for service-specific exceptions (eg: workflowStepExecutorException), but convert the error code to userFriendlyMessage mapper to a switch case function with a typecheck ensuring that all codes are mapped to a message. These default messages are still overridable where they are thrown.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
@@ -33,35 +34,61 @@ export enum BillingExceptionCode {
|
||||
BILLING_CREDITS_EXHAUSTED = 'BILLING_CREDITS_EXHAUSTED',
|
||||
}
|
||||
|
||||
const billingExceptionUserFriendlyMessages: Record<
|
||||
BillingExceptionCode,
|
||||
MessageDescriptor
|
||||
> = {
|
||||
[BillingExceptionCode.BILLING_CUSTOMER_NOT_FOUND]: msg`Billing customer not found.`,
|
||||
[BillingExceptionCode.BILLING_PLAN_NOT_FOUND]: msg`Billing plan not found.`,
|
||||
[BillingExceptionCode.BILLING_PRODUCT_NOT_FOUND]: msg`Billing product not found.`,
|
||||
[BillingExceptionCode.BILLING_PRICE_NOT_FOUND]: msg`Billing price not found.`,
|
||||
[BillingExceptionCode.BILLING_METER_NOT_FOUND]: msg`Billing meter not found.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_NOT_FOUND]: msg`Subscription not found.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_ITEM_NOT_FOUND]: msg`Subscription item not found.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_INVALID]: msg`Invalid subscription.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_EVENT_WORKSPACE_NOT_FOUND]: msg`Workspace not found for subscription event.`,
|
||||
[BillingExceptionCode.BILLING_CUSTOMER_EVENT_WORKSPACE_NOT_FOUND]: msg`Workspace not found for customer event.`,
|
||||
[BillingExceptionCode.BILLING_ACTIVE_SUBSCRIPTION_NOT_FOUND]: msg`No active subscription found.`,
|
||||
[BillingExceptionCode.BILLING_METER_EVENT_FAILED]: msg`Failed to record billing event.`,
|
||||
[BillingExceptionCode.BILLING_MISSING_REQUEST_BODY]: msg`Missing request body.`,
|
||||
[BillingExceptionCode.BILLING_UNHANDLED_ERROR]: msg`An unexpected billing error occurred.`,
|
||||
[BillingExceptionCode.BILLING_STRIPE_ERROR]: msg`A payment processing error occurred.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_NOT_IN_TRIAL_PERIOD]: msg`Subscription is not in trial period.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_INTERVAL_NOT_SWITCHABLE]: msg`Cannot switch subscription interval.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_INTERVAL_INVALID]: msg`Invalid subscription interval.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_PLAN_NOT_SWITCHABLE]: msg`Cannot switch subscription plan.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_ITEM_INVALID]: msg`Invalid subscription item.`,
|
||||
[BillingExceptionCode.BILLING_PRICE_INVALID_TIERS]: msg`Invalid pricing tiers.`,
|
||||
[BillingExceptionCode.BILLING_PRICE_INVALID]: msg`Invalid price.`,
|
||||
[BillingExceptionCode.BILLING_SUBSCRIPTION_PHASE_NOT_FOUND]: msg`Subscription phase not found.`,
|
||||
[BillingExceptionCode.BILLING_TOO_MUCH_SUBSCRIPTIONS_FOUND]: msg`Multiple subscriptions found where one was expected.`,
|
||||
[BillingExceptionCode.BILLING_CREDITS_EXHAUSTED]: msg`You have exhausted your credits. Please upgrade your plan to continue.`,
|
||||
const getBillingExceptionUserFriendlyMessage = (code: BillingExceptionCode) => {
|
||||
switch (code) {
|
||||
case BillingExceptionCode.BILLING_CUSTOMER_NOT_FOUND:
|
||||
return msg`Billing customer not found.`;
|
||||
case BillingExceptionCode.BILLING_PLAN_NOT_FOUND:
|
||||
return msg`Billing plan not found.`;
|
||||
case BillingExceptionCode.BILLING_PRODUCT_NOT_FOUND:
|
||||
return msg`Billing product not found.`;
|
||||
case BillingExceptionCode.BILLING_PRICE_NOT_FOUND:
|
||||
return msg`Billing price not found.`;
|
||||
case BillingExceptionCode.BILLING_METER_NOT_FOUND:
|
||||
return msg`Billing meter not found.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_NOT_FOUND:
|
||||
return msg`Subscription not found.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_ITEM_NOT_FOUND:
|
||||
return msg`Subscription item not found.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_INVALID:
|
||||
return msg`Invalid subscription.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_EVENT_WORKSPACE_NOT_FOUND:
|
||||
return msg`Workspace not found for subscription event.`;
|
||||
case BillingExceptionCode.BILLING_CUSTOMER_EVENT_WORKSPACE_NOT_FOUND:
|
||||
return msg`Workspace not found for customer event.`;
|
||||
case BillingExceptionCode.BILLING_ACTIVE_SUBSCRIPTION_NOT_FOUND:
|
||||
return msg`No active subscription found.`;
|
||||
case BillingExceptionCode.BILLING_METER_EVENT_FAILED:
|
||||
return msg`Failed to record billing event.`;
|
||||
case BillingExceptionCode.BILLING_MISSING_REQUEST_BODY:
|
||||
return msg`Missing request body.`;
|
||||
case BillingExceptionCode.BILLING_UNHANDLED_ERROR:
|
||||
return msg`An unexpected billing error occurred.`;
|
||||
case BillingExceptionCode.BILLING_STRIPE_ERROR:
|
||||
return msg`A payment processing error occurred.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_NOT_IN_TRIAL_PERIOD:
|
||||
return msg`Subscription is not in trial period.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_INTERVAL_NOT_SWITCHABLE:
|
||||
return msg`Cannot switch subscription interval.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_INTERVAL_INVALID:
|
||||
return msg`Invalid subscription interval.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_PLAN_NOT_SWITCHABLE:
|
||||
return msg`Cannot switch subscription plan.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_ITEM_INVALID:
|
||||
return msg`Invalid subscription item.`;
|
||||
case BillingExceptionCode.BILLING_PRICE_INVALID_TIERS:
|
||||
return msg`Invalid pricing tiers.`;
|
||||
case BillingExceptionCode.BILLING_PRICE_INVALID:
|
||||
return msg`Invalid price.`;
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_PHASE_NOT_FOUND:
|
||||
return msg`Subscription phase not found.`;
|
||||
case BillingExceptionCode.BILLING_TOO_MUCH_SUBSCRIPTIONS_FOUND:
|
||||
return msg`Multiple subscriptions found where one was expected.`;
|
||||
case BillingExceptionCode.BILLING_CREDITS_EXHAUSTED:
|
||||
return msg`You have exhausted your credits. Please upgrade your plan to continue.`;
|
||||
default:
|
||||
assertUnreachable(code);
|
||||
}
|
||||
};
|
||||
|
||||
export class BillingException extends CustomException<BillingExceptionCode> {
|
||||
@@ -72,7 +99,7 @@ export class BillingException extends CustomException<BillingExceptionCode> {
|
||||
) {
|
||||
super(message, code, {
|
||||
userFriendlyMessage:
|
||||
userFriendlyMessage ?? billingExceptionUserFriendlyMessages[code],
|
||||
userFriendlyMessage ?? getBillingExceptionUserFriendlyMessage(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user