From d09861549b43e3507a3c4247f2932f7b23514f32 Mon Sep 17 00:00:00 2001 From: Arne Bahlo Date: Tue, 24 Sep 2024 17:59:02 +0200 Subject: [PATCH 1/2] feat: Add AxiomError for more detailed error messages --- src/axiom_py/__init__.py | 4 ++-- src/axiom_py/client.py | 40 +++++++++++++++++++++++++++++----------- tests/test_client.py | 6 +++--- tests/test_datasets.py | 15 ++++++--------- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/axiom_py/__init__.py b/src/axiom_py/__init__.py index 275da0c..07358a0 100644 --- a/src/axiom_py/__init__.py +++ b/src/axiom_py/__init__.py @@ -3,7 +3,7 @@ """ from .client import ( - Error, + AxiomError, IngestFailure, IngestStatus, IngestOptions, @@ -27,7 +27,7 @@ ) _all_ = [ - Error, + AxiomError, IngestFailure, IngestStatus, IngestOptions, diff --git a/src/axiom_py/client.py b/src/axiom_py/client.py index 830ef24..bffeed1 100644 --- a/src/axiom_py/client.py +++ b/src/axiom_py/client.py @@ -30,13 +30,6 @@ AXIOM_URL = "https://api.axiom.co" -@dataclass -class Error: - status: Optional[int] = field(default=None) - message: Optional[str] = field(default=None) - error: Optional[str] = field(default=None) - - @dataclass class IngestFailure: """The ingestion failure of a single event""" @@ -118,10 +111,35 @@ class AplOptions: includeCursor: bool = field(default=False) -def raise_response_error(r): - if r.status_code >= 400: - # TODO: Decode JSON https://github.com/axiomhq/axiom-go/blob/610cfbd235d3df17f96a4bb156c50385cfbd9edd/axiom/error.go#L35-L50 - r.raise_for_status() +class AxiomError(Exception): + """This exception is raised on request errors.""" + + status: int + message: str + + @dataclass + class Response: + message: str + error: Optional[str] + + def __init__(self, status: int, res: Response): + message = res.error if res.error is not None else res.message + super().__init__(f"API error {status}: {message}") + + self.status = status + self.message = message + + +def raise_response_error(res): + if res.status_code >= 400: + try: + error_res = from_dict(AxiomError.Response, res.json()) + except Exception: + # Response is not in the Axiom JSON format, create generic error + # message + error_res = AxiomError.Response(message=res.reason, error=None) + + raise AxiomError(res.status_code, error_res) class Client: # pylint: disable=R0903 diff --git a/tests/test_client.py b/tests/test_client.py index 80a5450..071cb35 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -11,8 +11,8 @@ from logging import getLogger from datetime import datetime, timedelta from .helpers import get_random_name -from requests.exceptions import HTTPError from axiom_py import ( + AxiomError, Client, AplOptions, AplResultFormat, @@ -264,7 +264,7 @@ def tearDownClass(cls): "dataset (%s) was not deleted as part of the test, deleting it now." % cls.dataset_name ) - except HTTPError as err: + except AxiomError as e: # nothing to do here, since the dataset doesn't exist - cls.logger.warning(err) + cls.logger.warning(e) cls.logger.info("finish cleaning up after TestClient") diff --git a/tests/test_datasets.py b/tests/test_datasets.py index e6be360..59040fd 100644 --- a/tests/test_datasets.py +++ b/tests/test_datasets.py @@ -5,12 +5,9 @@ import unittest from typing import List, Dict from logging import getLogger -from requests.exceptions import HTTPError from datetime import timedelta from .helpers import get_random_name -from axiom_py import ( - Client, -) +from axiom_py import Client, AxiomError class TestDatasets(unittest.TestCase): @@ -78,11 +75,11 @@ def test_step999_delete(self): dataset, f"expected test dataset (%{self.dataset_name}) to be deleted", ) - except HTTPError as err: + except AxiomError as e: # the get method returns 404 error if dataset doesn't exist, so # that means that our tests passed, otherwise, it should fail. - if err.response.status_code != 404: - self.fail(err) + if e.status != 404: + self.fail(e) @classmethod def tearDownClass(cls): @@ -97,7 +94,7 @@ def tearDownClass(cls): "dataset (%s) was not deleted as part of the test, deleting it now." % cls.dataset_name ) - except HTTPError as err: + except AxiomError as e: # nothing to do here, since the dataset doesn't exist - cls.logger.warning(err) + cls.logger.warning(e) cls.logger.info("finish cleaning up after TestDatasets") From 9e50a91e3af7de6583b7f751660981cba556a508 Mon Sep 17 00:00:00 2001 From: Arne Bahlo Date: Tue, 24 Sep 2024 18:08:37 +0200 Subject: [PATCH 2/2] chore: Bump version to 0.8.0 --- pyproject.toml | 2 +- src/axiom_py/version.py | 2 +- uv.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bd54efc..45d6dcc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "axiom-py" -version = "0.7.0" +version = "0.8.0" description = "Official bindings for the Axiom API" readme = "README.md" requires-python = ">=3.8" diff --git a/src/axiom_py/version.py b/src/axiom_py/version.py index 7322ccc..b4b730c 100644 --- a/src/axiom_py/version.py +++ b/src/axiom_py/version.py @@ -1,3 +1,3 @@ """The current version""" -__version__ = "0.7.0" +__version__ = "0.8.0" diff --git a/uv.lock b/uv.lock index 0cb6084..12e7f5a 100644 --- a/uv.lock +++ b/uv.lock @@ -3,7 +3,7 @@ requires-python = ">=3.8" [[package]] name = "axiom-py" -version = "0.7.0" +version = "0.8.0" source = { editable = "." } dependencies = [ { name = "dacite" },