Files
AareDAQ/src/aare/common/error_codes.py
T

116 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from enum import StrEnum
class AuthErrorCode(StrEnum):
"""
Stable, machine-readable error codes used across the API.
Rules:
- never rename an existing value (treat as public API)
- only add new values
"""
# Generic / defaults
AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR"
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED"
FORBIDDEN = "FORBIDDEN"
HTTP_ERROR = "HTTP_ERROR"
INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
# Auth/JWT
INVALID_TOKEN = "INVALID_TOKEN"
SESSION_ALREADY_ACTIVE = "SESSION_ALREADY_ACTIVE"
# Authorization
NOT_STAFF = "NOT_STAFF"
NOT_IN_ACTIVE_PGROUP = "NOT_IN_ACTIVE_PGROUP"
NOT_BATON_HOLDER = "NOT_BATON_HOLDER"
class DAQErrorCode(StrEnum):
BEAMLINE_BUSY = "BEAMLINE_BUSY"
_ERROR_CODE_HELP: dict[str, str] = {
# Auth/JWT
AuthErrorCode.AUTHENTICATION_ERROR: (
"Generic authentication problem. Usually means the request lacked valid credentials "
"(expired/invalid token, missing Authorization header, etc.)."
),
AuthErrorCode.AUTHENTICATION_FAILED: (
"Authentication failed during login/token creation. Typically incorrect credentials "
"or an inability to validate the user."
),
AuthErrorCode.INVALID_TOKEN: (
"The provided token could not be decoded/validated (bad signature, expired, malformed). "
"Re-authenticate to obtain a new token."
),
AuthErrorCode.SESSION_ALREADY_ACTIVE: (
"A different session currently owns control. Use “force current session” (if allowed) "
"or wait for the active session to expire/end."
),
AuthErrorCode.NOT_BATON_HOLDER: (
"A different session currently owns the baton. Request the baton or wait for active session to expire/end"),
# Authorization
AuthErrorCode.FORBIDDEN: (
"Generic permissions failure. The user is authenticated but not allowed to perform this action."
),
AuthErrorCode.NOT_STAFF: (
"This action requires staff privileges. Log in with a staff account or ask staff to perform it."
),
AuthErrorCode.NOT_IN_ACTIVE_PGROUP: (
"You are not a member of the currently active p-group. Change p-group or use an account "
"that belongs to the active group."
),
# Generic
AuthErrorCode.HTTP_ERROR: (
"Generic HTTP error wrapper. The server returned an HTTPException that wasnt mapped to a more specific code."
),
AuthErrorCode.INTERNAL_SERVER_ERROR: (
"Unhandled server error. Check server logs for a stack trace and context."
),
DAQErrorCode.BEAMLINE_BUSY: (
"Beamline state is set to Busy by prior action. If this state persists an additional error may have occurred, "
"preventing the state from being released, this should timeout within 10 minutes."
"If this occurs please seek assistance from your local contact."
)
}
def error_code_help(code: str) -> str | None:
"""
Return a human help message for a code string, if known.
Accepts either enum value strings or raw strings.
"""
if not code:
return None
return _ERROR_CODE_HELP.get(str(code))
def export_error_code_help() -> dict[str, str]:
"""
Export help text as {"CODE": "help text", ...}
"""
return {str(k): str(v) for k, v in _ERROR_CODE_HELP.items()}
def export_error_codes_grouped() -> dict[str, dict[str, str]]:
"""
Export codes grouped by enum class name:
{
"AuthErrorCode": {"INVALID_TOKEN": "INVALID_TOKEN", ...},
"DAQErrorCode": {"BEAMLINE_BUSY": "BEAMLINE_BUSY", ...}
}
"""
enums: tuple[type[StrEnum], ...] = (AuthErrorCode, DAQErrorCode)
return {e.__name__: {c.name: str(c.value) for c in e} for e in enums}
def export_error_codes() -> dict[str, str]:
"""
Backwards-compatible, flat export used by older clients/tests/docs:
{"INVALID_TOKEN": "INVALID_TOKEN", ...}
NOTE: This intentionally exports only AuthErrorCode to avoid breaking
existing consumers that assume a flat map and/or specific keys.
"""
return {c.name: str(c.value) for c in AuthErrorCode}