Skip to content

Commit

Permalink
Serialize exceptions as string
Browse files Browse the repository at this point in the history
  • Loading branch information
petterhj committed Mar 19, 2024
1 parent 1718621 commit 94c8648
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 27 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## ?.?.? - 2024-03-19

* Status data exceptions are now accurately converted to strings when exported
as JSON.

## 3.0.0 - 2024-03-18

* Refactored status data helpers to adopt standard dataclasses in place of
Expand Down
35 changes: 18 additions & 17 deletions okdata/aws/status/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class TraceEventStatus(str, Enum):

class StatusJSONEncoder(JSONEncoder):
def default(self, obj, *args, **kwargs):
if isinstance(obj, Exception):
return str(obj)
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
Expand Down Expand Up @@ -77,21 +79,20 @@ class StatusData(BaseModel):
exception: Optional[str] = None
errors: Optional[List] = None

def __post_init__(self):
# Ensure that `meta` is of type `StatusMeta` if provided as a dictionary.
if isinstance(self.meta, dict):
self.meta = StatusMeta(**self.meta)

# Ensure that exception data is a string
self.exception = str(self.exception) if self.exception else None

def __setattr__(self, name, value):
# Validate and ensure format of errors
for error in self.errors or []:
if not isinstance(error, dict):
raise TypeError(f"{error} is not a dict.")
if "message" not in error:
raise ValueError("Missing key 'message'.")
if not isinstance(error["message"], dict):
raise TypeError("error['message'] is not a dict.")
if "nb" not in error["message"]:
raise ValueError("Missing key 'nb' in error['message'].")
if name == "errors" and value is not None:
if not isinstance(value, list):
raise TypeError("`errors` must be provided as a list.")

for error in value:
if not isinstance(error, dict):
raise TypeError(f"{error} is not a dict.")
if "message" not in error:
raise ValueError("Missing key 'message'.")
if not isinstance(error["message"], dict):
raise TypeError("error['message'] is not a dict.")
if "nb" not in error["message"]:
raise ValueError("Missing key 'nb' in error['message'].")

super().__setattr__(name, value)
23 changes: 14 additions & 9 deletions okdata/aws/status/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

from requests.exceptions import HTTPError

from .model import StatusData, TraceStatus, TraceEventStatus
from .model import (
StatusData,
StatusMeta,
TraceEventStatus,
TraceStatus,
)
from .sdk import Status

_status_logger = None
Expand Down Expand Up @@ -57,13 +62,13 @@ def _status_from_lambda_context(event, context):
"trace_id": event.get("execution_name"),
"user": authorizer.get("principalId"),
"component": os.getenv("SERVICE_NAME"),
"meta": {
"function_name": getattr(context, "function_name", None),
"function_version": getattr(context, "function_version", None),
"function_stage": request_context.get("stage"),
"function_api_id": request_context.get("apiId"),
"git_rev": os.getenv("GIT_REV"),
"git_branch": os.getenv("GIT_BRANCH"),
},
"meta": StatusMeta(
function_name=getattr(context, "function_name", None),
function_version=getattr(context, "function_version", None),
function_stage=request_context.get("stage"),
function_api_id=request_context.get("apiId"),
git_rev=os.getenv("GIT_REV"),
git_branch=os.getenv("GIT_BRANCH"),
),
}
)
5 changes: 5 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@


class TestStatusData:
def test_invalid_errors(self):
params = {"errors": "foo"}
with pytest.raises(TypeError):
StatusData(**params)

def test_errors_entry_valid(self):
params = {"errors": [OK_ERROR, OK_ERROR]}
result = StatusData(**params)
Expand Down
30 changes: 29 additions & 1 deletion tests/test_status.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import re
from copy import deepcopy
from unittest.mock import patch
Expand All @@ -6,7 +7,12 @@
from freezegun import freeze_time
from okdata.sdk.config import Config

from okdata.aws.status.model import StatusData, TraceStatus, TraceEventStatus
from okdata.aws.status.model import (
StatusData,
StatusMeta,
TraceStatus,
TraceEventStatus,
)
from okdata.aws.status.sdk import Status
from okdata.aws.status.wrapper import _status_from_lambda_context

Expand Down Expand Up @@ -129,3 +135,25 @@ def test_add_status_data_payload(self, mock_openid, mock_status_api):
s = Status(mock_status_data)
s.add(domain_id="my-domain-id")
assert s.status_data.domain_id == "my-domain-id"

@freeze_time(utc_now)
def test_status_data_as_json(self, mock_openid, mock_status_api):
s = Status(mock_status_data)
s.add(
domain_id="my-domain-id",
exception=Exception("This did not work as expected"),
user=None,
meta=StatusMeta(function_name="foo-bar"),
)
assert json.loads(s.status_data.json(exclude_none=True)) == {
"trace_id": "my-trace-id",
"domain": "dataset",
"domain_id": "my-domain-id",
"start_time": utc_now,
"end_time": utc_now,
"trace_status": "CONTINUE",
"trace_event_status": "OK",
"component": "system32",
"meta": {"function_name": "foo-bar"},
"exception": "This did not work as expected",
}

0 comments on commit 94c8648

Please sign in to comment.