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:
+11
-6
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
|
||||
@@ -7,11 +8,15 @@ export enum WorkspaceCleanerExceptionCode {
|
||||
BILLING_SUBSCRIPTION_NOT_FOUND = 'BILLING_SUBSCRIPTION_NOT_FOUND',
|
||||
}
|
||||
|
||||
const workspaceCleanerExceptionUserFriendlyMessages: Record<
|
||||
WorkspaceCleanerExceptionCode,
|
||||
MessageDescriptor
|
||||
> = {
|
||||
[WorkspaceCleanerExceptionCode.BILLING_SUBSCRIPTION_NOT_FOUND]: msg`Billing subscription not found.`,
|
||||
const getWorkspaceCleanerExceptionUserFriendlyMessage = (
|
||||
code: WorkspaceCleanerExceptionCode,
|
||||
) => {
|
||||
switch (code) {
|
||||
case WorkspaceCleanerExceptionCode.BILLING_SUBSCRIPTION_NOT_FOUND:
|
||||
return msg`Billing subscription not found.`;
|
||||
default:
|
||||
assertUnreachable(code);
|
||||
}
|
||||
};
|
||||
|
||||
export class WorkspaceCleanerException extends CustomException<WorkspaceCleanerExceptionCode> {
|
||||
@@ -23,7 +28,7 @@ export class WorkspaceCleanerException extends CustomException<WorkspaceCleanerE
|
||||
super(message, code, {
|
||||
userFriendlyMessage:
|
||||
userFriendlyMessage ??
|
||||
workspaceCleanerExceptionUserFriendlyMessages[code],
|
||||
getWorkspaceCleanerExceptionUserFriendlyMessage(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+25
-13
@@ -1,5 +1,6 @@
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
appendCommonExceptionCode,
|
||||
@@ -16,18 +17,29 @@ export const WorkspaceMigrationRunnerExceptionCode = appendCommonExceptionCode({
|
||||
FLAT_ENTITY_NOT_FOUND: 'FLAT_ENTITY_NOT_FOUND',
|
||||
} as const);
|
||||
|
||||
const workspaceMigrationRunnerExceptionUserFriendlyMessages: Record<
|
||||
keyof typeof WorkspaceMigrationRunnerExceptionCode,
|
||||
MessageDescriptor
|
||||
> = {
|
||||
FIELD_METADATA_NOT_FOUND: msg`Field metadata not found.`,
|
||||
OBJECT_METADATA_NOT_FOUND: msg`Object metadata not found.`,
|
||||
ENUM_OPERATION_FAILED: msg`Enum operation failed.`,
|
||||
UNSUPPORTED_COMPOSITE_COLUMN_TYPE: msg`Unsupported composite column type.`,
|
||||
NOT_SUPPORTED: msg`This operation is not supported.`,
|
||||
INVALID_ACTION_TYPE: msg`Invalid action type.`,
|
||||
FLAT_ENTITY_NOT_FOUND: msg`Entity not found.`,
|
||||
INTERNAL_SERVER_ERROR: msg`An unexpected error occurred.`,
|
||||
const getWorkspaceMigrationRunnerExceptionUserFriendlyMessage = (
|
||||
code: keyof typeof WorkspaceMigrationRunnerExceptionCode,
|
||||
) => {
|
||||
switch (code) {
|
||||
case WorkspaceMigrationRunnerExceptionCode.FIELD_METADATA_NOT_FOUND:
|
||||
return msg`Field metadata not found.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
return msg`Object metadata not found.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.ENUM_OPERATION_FAILED:
|
||||
return msg`Enum operation failed.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.UNSUPPORTED_COMPOSITE_COLUMN_TYPE:
|
||||
return msg`Unsupported composite column type.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.NOT_SUPPORTED:
|
||||
return msg`This operation is not supported.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.INVALID_ACTION_TYPE:
|
||||
return msg`Invalid action type.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.FLAT_ENTITY_NOT_FOUND:
|
||||
return msg`Entity not found.`;
|
||||
case WorkspaceMigrationRunnerExceptionCode.INTERNAL_SERVER_ERROR:
|
||||
return msg`An unexpected error occurred.`;
|
||||
default:
|
||||
assertUnreachable(code);
|
||||
}
|
||||
};
|
||||
|
||||
export class WorkspaceMigrationRunnerException extends CustomException<
|
||||
@@ -41,7 +53,7 @@ export class WorkspaceMigrationRunnerException extends CustomException<
|
||||
super(message, code, {
|
||||
userFriendlyMessage:
|
||||
userFriendlyMessage ??
|
||||
workspaceMigrationRunnerExceptionUserFriendlyMessages[code],
|
||||
getWorkspaceMigrationRunnerExceptionUserFriendlyMessage(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user