diff --git a/packages/abstractions/kiota_abstractions/api_error.py b/packages/abstractions/kiota_abstractions/api_error.py index e3b630f4..95177bb9 100644 --- a/packages/abstractions/kiota_abstractions/api_error.py +++ b/packages/abstractions/kiota_abstractions/api_error.py @@ -10,17 +10,14 @@ class APIError(Exception): response_status_code: Optional[int] = None response_headers: Optional[dict[str, str]] = None + @property + def primary_message(self) -> Optional[str]: + """The message shown for this error. Generated error models override it with the + message carried by the API's error payload.""" + return self.message + def __str__(self) -> str: - error = getattr(self, "error", None) - if error: - return f""" - APIError - Code: {self.response_status_code} - message: {self.message} - error: {error} - """ - return f""" - APIError - Code: {self.response_status_code} - message: {self.message} - """ + first_line = self.primary_message or type(self).__name__ + if self.response_status_code is not None: + return f"{first_line} (status {self.response_status_code})" + return first_line diff --git a/packages/abstractions/tests/test_api_error.py b/packages/abstractions/tests/test_api_error.py new file mode 100644 index 00000000..be925b01 --- /dev/null +++ b/packages/abstractions/tests/test_api_error.py @@ -0,0 +1,56 @@ +from dataclasses import dataclass +from typing import Optional + +from kiota_abstractions.api_error import APIError + + +@dataclass +class MainError: + code: Optional[str] = None + message: Optional[str] = None + + +@dataclass +class GeneratedError(APIError): + """The shape kiota generates for an API's error model: an ``error`` payload and a + ``primary_message`` override, and no ``message`` set by the deserializer.""" + + error: Optional[MainError] = None + + @property + def primary_message(self) -> Optional[str]: + if self.error is not None: + return self.error.message or "" + return "" + + +def test_str_is_the_message(): + assert str(APIError(message="boom")) == "boom" + + +def test_str_appends_the_status_code(): + assert str(APIError(message="boom", response_status_code=429)) == "boom (status 429)" + + +def test_str_without_any_message_names_the_class(): + assert str(APIError(response_status_code=502)) == "APIError (status 502)" + + +def test_primary_message_defaults_to_the_message(): + assert APIError(message="boom").primary_message == "boom" + assert APIError().primary_message is None + + +def test_str_of_a_generated_error_uses_its_primary_message(): + message = "Application is over its MailboxConcurrency limit." + error = GeneratedError( + response_status_code=429, + error=MainError(code="ApplicationThrottled", message=message), + ) + + assert str(error) == f"{message} (status 429)" + + +def test_str_never_starts_with_a_blank_line(): + for error in (APIError(), APIError(message="boom"), GeneratedError(error=MainError())): + assert str(error).splitlines()[0].strip()