04c596817a
## Summary
This PR enforces that all custom exceptions must provide a
`userFriendlyMessage`, ensuring end users always see readable error
messages.
## Changes
### Core Changes
- **`CustomException` simplified**: Removed the `ForceFriendlyMessage`
generic parameter - `userFriendlyMessage` is now always required
- **Type safety**: The constructor now requires `{ userFriendlyMessage:
MessageDescriptor }` (no longer optional)
### Updated Files
- **74+ exception classes** updated to provide default user-friendly
messages using Lingui `msg` macro
- Each exception class has a sensible fallback message (e.g., `msg\`An
authentication error occurred.\``)
- Exception classes that had code-specific message maps retain their
behavior
## Benefits
- **Compile-time enforcement**: Forgetting to add a user-friendly
message now causes a TypeScript error
- **Better UX**: End users always see a localized, human-readable error
message
- **Simpler API**: No more boolean generic parameter to think about
## Testing
- `npx nx run twenty-server:typecheck` passes
- `npx nx run twenty-server:lint` passes
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Enforces `userFriendlyMessage` on `CustomException` and updates all
exception classes to supply localized default messages, with
filters/tests adjusted accordingly.
>
> - **Core**:
> - Enforce required `userFriendlyMessage` in `CustomException` (remove
optional generic; constructor now requires `{ userFriendlyMessage:
MessageDescriptor }`).
> - **Exceptions**:
> - Update ~70+ exception classes to set default localized messages via
Lingui `msg` maps and pass them in constructors (e.g., `AuthException`,
`ObjectMetadataException`, `FieldMetadataException`, etc.).
> - Add fallback messages where needed (e.g., `INTERNAL_SERVER_ERROR` or
domain-specific defaults).
> - **HTTP/GraphQL Filters**:
> - Ensure fallbacks create `UnknownException` with `msg` for
user-friendly text in REST/GraphQL exception filters.
> - **Tests**:
> - Adjust unit tests to pass `userFriendlyMessage` to exceptions.
> - Update Jest snapshots to include `extensions.userFriendlyMessage` or
message objects where applicable.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
221004fdfc0d97b7d152a258b347bf571e70f10e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
110 lines
3.2 KiB
TypeScript
110 lines
3.2 KiB
TypeScript
import { type MessageDescriptor } from '@lingui/core';
|
|
import { msg } from '@lingui/core/macro';
|
|
|
|
import {
|
|
appendCommonExceptionCode,
|
|
CustomException,
|
|
UnknownException,
|
|
} from 'src/utils/custom-exception';
|
|
|
|
describe('appendCommonExceptionCode', () => {
|
|
it('should merge CommonExceptionCode with specific exception code', () => {
|
|
const specificExceptionCode = {
|
|
SPECIFIC_ERROR: 'SPECIFIC_ERROR',
|
|
};
|
|
|
|
const result = appendCommonExceptionCode(specificExceptionCode);
|
|
|
|
expect(result).toEqual({
|
|
INTERNAL_SERVER_ERROR: 'INTERNAL_SERVER_ERROR',
|
|
SPECIFIC_ERROR: 'SPECIFIC_ERROR',
|
|
});
|
|
});
|
|
|
|
it('should return CommonExceptionCode when empty object is provided', () => {
|
|
const result = appendCommonExceptionCode({});
|
|
|
|
expect(result).toEqual({
|
|
INTERNAL_SERVER_ERROR: 'INTERNAL_SERVER_ERROR',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('CustomException', () => {
|
|
class TestException extends CustomException<string> {
|
|
constructor(
|
|
message: string,
|
|
code: string,
|
|
{ userFriendlyMessage }: { userFriendlyMessage: MessageDescriptor },
|
|
) {
|
|
super(message, code, { userFriendlyMessage });
|
|
}
|
|
}
|
|
|
|
it('should set message and code correctly', () => {
|
|
const message = 'Test error message';
|
|
const code = 'TEST_ERROR';
|
|
const userFriendlyMessage = msg`Test user friendly message`;
|
|
const exception = new TestException(message, code, { userFriendlyMessage });
|
|
|
|
expect(exception.message).toBe(message);
|
|
expect(exception.code).toBe(code);
|
|
expect(exception.userFriendlyMessage).toBe(userFriendlyMessage);
|
|
});
|
|
|
|
it('should set userFriendlyMessage when provided', () => {
|
|
const message = 'Test error message';
|
|
const code = 'TEST_ERROR';
|
|
const userFriendlyMessage = msg`User friendly error message`;
|
|
const exception = new TestException(message, code, {
|
|
userFriendlyMessage,
|
|
});
|
|
|
|
expect(exception.message).toBe(message);
|
|
expect(exception.code).toBe(code);
|
|
expect(exception.userFriendlyMessage).toBe(userFriendlyMessage);
|
|
});
|
|
|
|
it('should extend Error', () => {
|
|
const exception = new TestException('Test error', 'TEST_ERROR', {
|
|
userFriendlyMessage: msg`Test error`,
|
|
});
|
|
|
|
expect(exception).toBeInstanceOf(Error);
|
|
});
|
|
});
|
|
|
|
describe('UnknownException', () => {
|
|
it('should extend CustomException', () => {
|
|
const exception = new UnknownException('Test error', 'TEST_ERROR', {
|
|
userFriendlyMessage: msg`Test error`,
|
|
});
|
|
|
|
expect(exception).toBeInstanceOf(CustomException);
|
|
});
|
|
|
|
it('should set message and code correctly', () => {
|
|
const message = 'Test error message';
|
|
const code = 'TEST_ERROR';
|
|
const exception = new UnknownException(message, code, {
|
|
userFriendlyMessage: msg`Test error`,
|
|
});
|
|
|
|
expect(exception.message).toBe(message);
|
|
expect(exception.code).toBe(code);
|
|
});
|
|
|
|
it('should set userFriendlyMessage when provided', () => {
|
|
const message = 'Test error message';
|
|
const code = 'TEST_ERROR';
|
|
const userFriendlyMessage = msg`User friendly error message`;
|
|
const exception = new UnknownException(message, code, {
|
|
userFriendlyMessage,
|
|
});
|
|
|
|
expect(exception.message).toBe(message);
|
|
expect(exception.code).toBe(code);
|
|
expect(exception.userFriendlyMessage).toBe(userFriendlyMessage);
|
|
});
|
|
});
|