Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: simplifying error messages from core #273

Merged
merged 4 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- id: matrix
uses: splunk/addonfactory-test-matrix-action@v1.12
uses: splunk/addonfactory-test-matrix-action@v1.13

fossa-scan:
continue-on-error: true
Expand Down
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion splunktaucclib/rest_handler/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import json
import traceback
import urllib.parse
from defusedxml import ElementTree
from functools import wraps

from solnlib.splunk_rest_client import SplunkRestClient
Expand All @@ -41,6 +42,23 @@ def _check_name_for_create(name):
raise RestError(400, 'Name starting with "_" is not allowed for entity')


def _parse_error_msg(exc: binding.HTTPError) -> str:
permission_msg = "do not have permission to perform this operation"
try:
msgs = json.loads(exc.body)["messages"]
text = msgs[0]["text"]
except json.JSONDecodeError:
try:
text = ElementTree.fromstring(exc.body).findtext("./messages/msg")
except ElementTree.ParseError:
return exc.body.decode()
except (KeyError, IndexError):
return exc.body.decode()
if exc.status == 403 and permission_msg in text:
return "This operation is forbidden."
return text


def _pre_request(existing):
"""
Encode payload before request.
Expand Down Expand Up @@ -126,7 +144,7 @@ def wrapper(self, *args, **kwargs):
except RestError:
raise
except binding.HTTPError as exc:
raise RestError(exc.status, str(exc))
raise RestError(exc.status, _parse_error_msg(exc))
except Exception:
raise RestError(500, traceback.format_exc())

Expand Down
7 changes: 2 additions & 5 deletions tests/integration/test_rest_handler_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ def test_inputs_api_call():

def test_400_api_call():
expected_msg = """<msg type="ERROR">Unexpected error "&lt;class 'splunktaucclib.rest_handler.error.RestError'&gt;"
from python handler: "REST Error [400]: Bad Request -- HTTP 400 Bad Request --
b'{"messages":[{"type":"ERROR","text":"Object id=demo://test_input cannot be deleted in config=inputs."}]}'".
from python handler: "REST Error [400]: Bad Request -- Object id=demo://test_input cannot be deleted in config=inputs.".
See splunkd.log/python.log for more details.</msg>"""

response = requests.delete(
Expand All @@ -72,9 +71,7 @@ def test_400_api_call():

def test_403_api_call():
expected_msg = """<msg type="ERROR">Unexpected error "&lt;class 'splunktaucclib.rest_handler.error.RestError'&gt;"
from python handler: "REST Error [403]: Forbidden -- HTTP 403 Forbidden --
b'{"messages":[{"type":"ERROR","text":"You (user=user) do not have permission to perform this operation
(requires capability: admin_all_objects)."}]}'". See splunkd.log/python.log for more details.</msg>"""
from python handler: "REST Error [403]: Forbidden -- This operation is forbidden.". See splunkd.log/python.log for more details.</msg>"""

response = requests.post(
f"https://{host}:{management_port}/servicesNS/-/demo/demo_demo",
Expand Down
68 changes: 67 additions & 1 deletion tests/unit/test_rest_handler_error.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import pytest

from splunktaucclib.rest_handler import error
from splunktaucclib.rest_handler import error, handler
from splunklib import binding
from splunklib.data import record


def make_response_record(body, status=200):
class _MocBufReader:
def __init__(self, buf):
if isinstance(buf, str):
self._buf = buf.encode("utf-8")
else:
self._buf = buf

def read(self, size=None):
return self._buf

return record(
{
"body": binding.ResponseReader(_MocBufReader(body)),
"status": status,
"reason": "",
"headers": None,
}
)


@pytest.mark.parametrize(
Expand All @@ -26,3 +49,46 @@ def test_rest_error(status_code, message, expected_message):
with pytest.raises(Exception) as exc_info:
raise error.RestError(status_code, "message")
assert str(exc_info.value) == expected_message


def test_parse_err_msg_xml_forbidden():
original_err_msg = """<?xml version="1.0" encoding="UTF-8"?>\n<response>\n <messages>\n \
<msg type="ERROR">You (user=user) do not have permission to perform this operation (requires capability: \
list_storage_passwords OR edit_storage_passwords OR admin_all_objects).</msg>\n </messages>\n</response>\n"""
expected_err_msg = "This operation is forbidden."
err = binding.HTTPError(make_response_record(original_err_msg, status=403))
result = handler._parse_error_msg(err)
assert result == expected_err_msg


def test_parse_err_msg_xml_forbidden_invalid():
original_err_msg = "Error message - wrong format"
err = binding.HTTPError(make_response_record(original_err_msg, status=403))
result = handler._parse_error_msg(err)
assert result == original_err_msg


def test_parse_err_msg_json_forbidden():
original_err_msg = """{"messages":[{"type":"ERROR","text":"You (user=user) do not have permission to \
perform this operation (requires capability: admin_all_objects)."}]}"""
expected_err_msg = "This operation is forbidden."
err = binding.HTTPError(make_response_record(original_err_msg, status=403))
result = handler._parse_error_msg(err)
assert result == expected_err_msg


def test_parse_err_msg_json_forbidden_invalid():
original_err_msg = """{"messages":{"type":"ERROR","text":"You (user=user) do not have permission to \
perform this operation (requires capability: admin_all_objects)."}}"""
err = binding.HTTPError(make_response_record(original_err_msg, status=400))
result = handler._parse_error_msg(err)
assert result == original_err_msg


def test_parse_err_msg_json_bad_request():
original_err_msg = """{"messages":[{"type":"ERROR","text":"\
Object id=demo://test_input cannot be deleted in config=inputs."}]}"""
expected_err_msg = "Object id=demo://test_input cannot be deleted in config=inputs."
err = binding.HTTPError(make_response_record(original_err_msg, status=400))
result = handler._parse_error_msg(err)
assert result == expected_err_msg
Loading