34362de7b7
## What Splits route trigger logic-function failures into two cases instead of one catch-all: - **User error** — the function's own code threw an uncaught error. Returns `500` and is **not** sent to Sentry. - **Platform error** — an infrastructure/execution failure on our side. Returns `500` and **is** sent to Sentry. A disabled logic function now returns `403`. ## Why User-code failures were flooding Sentry: a single workspace's function hitting a transient upstream error generated tens of thousands of events. #21656 stopped the flood by muting the entire route-trigger execution error bucket — but muting everything also silenced genuine platform failures we *do* want to be alerted on. Splitting the bucket keeps the user-code noise out of Sentry (the original goal) while making sure real platform errors still surface. Users who want to return a specific status/body when their function fails can still catch the error and return a `Response` — that path is unchanged. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21715?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
77 lines
3.6 KiB
TypeScript
77 lines
3.6 KiB
TypeScript
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';
|
|
|
|
export enum LogicFunctionExceptionCode {
|
|
LOGIC_FUNCTION_NOT_FOUND = 'LOGIC_FUNCTION_NOT_FOUND',
|
|
LOGIC_FUNCTION_ALREADY_EXIST = 'LOGIC_FUNCTION_ALREADY_EXIST',
|
|
LOGIC_FUNCTION_NOT_READY = 'LOGIC_FUNCTION_NOT_READY',
|
|
LOGIC_FUNCTION_BUILDING = 'LOGIC_FUNCTION_BUILDING',
|
|
LOGIC_FUNCTION_CODE_UNCHANGED = 'LOGIC_FUNCTION_CODE_UNCHANGED',
|
|
LOGIC_FUNCTION_EXECUTION_LIMIT_REACHED = 'LOGIC_FUNCTION_EXECUTION_LIMIT_REACHED',
|
|
LOGIC_FUNCTION_CREATE_FAILED = 'LOGIC_FUNCTION_CREATE_FAILED',
|
|
LOGIC_FUNCTION_COMPILATION_FAILED = 'LOGIC_FUNCTION_COMPILATION_FAILED',
|
|
LOGIC_FUNCTION_EXECUTION_TIMEOUT = 'LOGIC_FUNCTION_EXECUTION_TIMEOUT',
|
|
LOGIC_FUNCTION_PLATFORM_EXECUTION_ERROR = 'LOGIC_FUNCTION_PLATFORM_EXECUTION_ERROR',
|
|
LOGIC_FUNCTION_LAYER_BUILD_FAILED = 'LOGIC_FUNCTION_LAYER_BUILD_FAILED',
|
|
LOGIC_FUNCTION_DISABLED = 'LOGIC_FUNCTION_DISABLED',
|
|
LOGIC_FUNCTION_INVALID_SEED_PROJECT = 'LOGIC_FUNCTION_INVALID_SEED_PROJECT',
|
|
INVALID_LOGIC_FUNCTION_INPUT = 'INVALID_LOGIC_FUNCTION_INPUT',
|
|
LOGIC_FUNCTION_PREBUILT_BUNDLE_NOT_INSTALLED = 'LOGIC_FUNCTION_PREBUILT_BUNDLE_NOT_INSTALLED',
|
|
}
|
|
|
|
const getLogicFunctionExceptionUserFriendlyMessage = (
|
|
code: LogicFunctionExceptionCode,
|
|
) => {
|
|
switch (code) {
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_FOUND:
|
|
return msg`Function not found.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_ALREADY_EXIST:
|
|
return msg`A function with this name already exists.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_READY:
|
|
return msg`Function is not ready.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_BUILDING:
|
|
return msg`Function is currently building.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_CODE_UNCHANGED:
|
|
return msg`Function code is unchanged.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_EXECUTION_LIMIT_REACHED:
|
|
return msg`Function execution limit reached.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_CREATE_FAILED:
|
|
return msg`Failed to create function.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_COMPILATION_FAILED:
|
|
return msg`Function code failed to compile.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_EXECUTION_TIMEOUT:
|
|
return msg`Function execution timed out.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_PLATFORM_EXECUTION_ERROR:
|
|
return msg`Function execution failed.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_LAYER_BUILD_FAILED:
|
|
return msg`Failed to build function dependencies.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_DISABLED:
|
|
return msg`Logic function execution is disabled.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_INVALID_SEED_PROJECT:
|
|
return msg`Invalid seed project configuration.`;
|
|
case LogicFunctionExceptionCode.INVALID_LOGIC_FUNCTION_INPUT:
|
|
return msg`Invalid logic function input.`;
|
|
case LogicFunctionExceptionCode.LOGIC_FUNCTION_PREBUILT_BUNDLE_NOT_INSTALLED:
|
|
return msg`Prebuilt bundle is not installed on the function. Rebuild and try again.`;
|
|
default:
|
|
assertUnreachable(code);
|
|
}
|
|
};
|
|
|
|
export class LogicFunctionException extends CustomException<LogicFunctionExceptionCode> {
|
|
constructor(
|
|
message: string,
|
|
code: LogicFunctionExceptionCode,
|
|
{ userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {},
|
|
) {
|
|
super(message, code, {
|
|
userFriendlyMessage:
|
|
userFriendlyMessage ??
|
|
getLogicFunctionExceptionUserFriendlyMessage(code),
|
|
});
|
|
}
|
|
}
|