easybill_client/generated/async/easybill_generated_async/exceptions.py
claudi caacb339dd Add unit tests for authentication and webhook parsing
- Implement tests for basic and bearer authentication headers in `test_auth.py`.
- Create tests for the `EasybillWebhookParser` in `test_webhooks.py`, covering JSON and form-encoded payloads, as well as a generic parse and acknowledgement method.
2026-04-17 10:20:12 +02:00

218 lines
7.9 KiB
Python

"""
easybill REST API
The first version of the easybill REST API. [CHANGELOG](https://api.easybill.de/rest/v1/CHANGELOG.md) ## Authentication You can choose between two available methods: `Basic Auth` or `Bearer Token`. In each HTTP request, one of the following HTTP headers is required: ``` # Basic Auth Authorization: Basic base64_encode('<email>:<api_key>') # Bearer Token Authorization: Bearer <api_key> ``` ## Limitations ### Request Limit * PLUS: 10 requests per minute * BUSINESS: 60 requests per minute If the limit is exceeded, you will receive the HTTP error: `429 Too Many Requests` ### Result Limit All result lists are limited to 100 by default. This limit can be increased by the query parameter `limit` to a maximum of 1000. ## Query filter Many list resources can be filtered. In `/documents` you can filter e.g. by number with `/documents?number=111028654`. If you want to filter multiple numbers, you can either enter them separated by commas `/documents?number=111028654,222006895` or as an array `/documents?number[]=111028654&number[]=222006895`. **Warning**: The maximum size of an HTTP request line in bytes is 4094. If this limit is exceeded, you will receive the HTTP error: `414 Request-URI Too Large` ### Escape commas in query You can escape commans in query `name=Patrick\\, Peter` if you submit the header `X-Easybill-Escape: true` in your request. ## Property login_id This is the login of your admin or employee account. ## Date and Date-Time format Please use the timezone `Europe/Berlin`. * **date** = *Y-m-d* = `2016-12-31` * **date-time** = *Y-m-d H:i:s* = `2016-12-31 03:13:37` Date or datetime can be `null` because the attributes have been added later and the entry is older.
The version of the OpenAPI document: 1.96.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
from typing import Any, Optional
from typing_extensions import Self
class OpenApiException(Exception):
"""The base exception class for all OpenAPIExceptions"""
class ApiTypeError(OpenApiException, TypeError):
def __init__(self, msg, path_to_item=None, valid_classes=None,
key_type=None) -> None:
""" Raises an exception for TypeErrors
Args:
msg (str): the exception message
Keyword Args:
path_to_item (list): a list of keys an indices to get to the
current_item
None if unset
valid_classes (tuple): the primitive classes that current item
should be an instance of
None if unset
key_type (bool): False if our value is a value in a dict
True if it is a key in a dict
False if our item is an item in a list
None if unset
"""
self.path_to_item = path_to_item
self.valid_classes = valid_classes
self.key_type = key_type
full_msg = msg
if path_to_item:
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
super(ApiTypeError, self).__init__(full_msg)
class ApiValueError(OpenApiException, ValueError):
def __init__(self, msg, path_to_item=None) -> None:
"""
Args:
msg (str): the exception message
Keyword Args:
path_to_item (list) the path to the exception in the
received_data dict. None if unset
"""
self.path_to_item = path_to_item
full_msg = msg
if path_to_item:
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
super(ApiValueError, self).__init__(full_msg)
class ApiAttributeError(OpenApiException, AttributeError):
def __init__(self, msg, path_to_item=None) -> None:
"""
Raised when an attribute reference or assignment fails.
Args:
msg (str): the exception message
Keyword Args:
path_to_item (None/list) the path to the exception in the
received_data dict
"""
self.path_to_item = path_to_item
full_msg = msg
if path_to_item:
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
super(ApiAttributeError, self).__init__(full_msg)
class ApiKeyError(OpenApiException, KeyError):
def __init__(self, msg, path_to_item=None) -> None:
"""
Args:
msg (str): the exception message
Keyword Args:
path_to_item (None/list) the path to the exception in the
received_data dict
"""
self.path_to_item = path_to_item
full_msg = msg
if path_to_item:
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
super(ApiKeyError, self).__init__(full_msg)
class ApiException(OpenApiException):
def __init__(
self,
status=None,
reason=None,
http_resp=None,
*,
body: Optional[str] = None,
data: Optional[Any] = None,
) -> None:
self.status = status
self.reason = reason
self.body = body
self.data = data
self.headers = None
if http_resp:
if self.status is None:
self.status = http_resp.status
if self.reason is None:
self.reason = http_resp.reason
if self.body is None:
try:
self.body = http_resp.data.decode('utf-8')
except Exception:
pass
self.headers = http_resp.headers
@classmethod
def from_response(
cls,
*,
http_resp,
body: Optional[str],
data: Optional[Any],
) -> Self:
if http_resp.status == 400:
raise BadRequestException(http_resp=http_resp, body=body, data=data)
if http_resp.status == 401:
raise UnauthorizedException(http_resp=http_resp, body=body, data=data)
if http_resp.status == 403:
raise ForbiddenException(http_resp=http_resp, body=body, data=data)
if http_resp.status == 404:
raise NotFoundException(http_resp=http_resp, body=body, data=data)
# Added new conditions for 409 and 422
if http_resp.status == 409:
raise ConflictException(http_resp=http_resp, body=body, data=data)
if http_resp.status == 422:
raise UnprocessableEntityException(http_resp=http_resp, body=body, data=data)
if 500 <= http_resp.status <= 599:
raise ServiceException(http_resp=http_resp, body=body, data=data)
raise ApiException(http_resp=http_resp, body=body, data=data)
def __str__(self):
"""Custom error messages for exception"""
error_message = "({0})\n"\
"Reason: {1}\n".format(self.status, self.reason)
if self.headers:
error_message += "HTTP response headers: {0}\n".format(
self.headers)
if self.body:
error_message += "HTTP response body: {0}\n".format(self.body)
if self.data:
error_message += "HTTP response data: {0}\n".format(self.data)
return error_message
class BadRequestException(ApiException):
pass
class NotFoundException(ApiException):
pass
class UnauthorizedException(ApiException):
pass
class ForbiddenException(ApiException):
pass
class ServiceException(ApiException):
pass
class ConflictException(ApiException):
"""Exception for HTTP 409 Conflict."""
pass
class UnprocessableEntityException(ApiException):
"""Exception for HTTP 422 Unprocessable Entity."""
pass
def render_path(path_to_item):
"""Returns a string representation of a path"""
result = ""
for pth in path_to_item:
if isinstance(pth, int):
result += "[{0}]".format(pth)
else:
result += "['{0}']".format(pth)
return result