Skip to content

Commit 5daa386

Browse files
committed
PLINT-1088 Including internal_type in errors' data member
1 parent 761598d commit 5daa386

14 files changed

Lines changed: 179 additions & 95 deletions

src/galaxy/api/jsonrpc.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import asyncio
22
from collections import namedtuple
3-
from collections.abc import Iterable
3+
from collections.abc import Iterable, Mapping
44
import logging
55
import inspect
66
import json
@@ -16,7 +16,12 @@ class JsonRpcError(Exception):
1616
def __init__(self, code, message, data=None):
1717
self.code = code
1818
self.message = str(message)
19-
self.data = data
19+
self.data = {}
20+
if data is not None:
21+
if not isinstance(data, Mapping):
22+
raise TypeError(f"Data parameter should be a mapping, got this instead: {data}")
23+
self.data = data
24+
self.data.update({"internal_type": type(self).__name__})
2025
super().__init__()
2126

2227
def __eq__(self, other):
@@ -25,43 +30,41 @@ def __eq__(self, other):
2530
def json(self):
2631
obj = {
2732
"code": self.code,
28-
"message": self.message
33+
"message": self.message,
34+
"data": self.data
2935
}
3036

31-
if self.data is not None:
32-
obj["data"] = self.data
33-
3437
return obj
3538

3639

3740
class ParseError(JsonRpcError):
38-
def __init__(self, message="Parse error"):
39-
super().__init__(-32700, message)
41+
def __init__(self, message="Parse error", data=None):
42+
super().__init__(-32700, message, data)
4043

4144

4245
class InvalidRequest(JsonRpcError):
43-
def __init__(self, message="Invalid Request"):
44-
super().__init__(-32600, message)
46+
def __init__(self, message="Invalid Request", data=None):
47+
super().__init__(-32600, message, data)
4548

4649

4750
class MethodNotFound(JsonRpcError):
48-
def __init__(self, message="Method not found"):
49-
super().__init__(-32601, message)
51+
def __init__(self, message="Method not found", data=None):
52+
super().__init__(-32601, message, data)
5053

5154

5255
class InvalidParams(JsonRpcError):
53-
def __init__(self, message="Invalid params"):
54-
super().__init__(-32602, message)
56+
def __init__(self, message="Invalid params", data=None):
57+
super().__init__(-32602, message, data)
5558

5659

5760
class Timeout(JsonRpcError):
58-
def __init__(self, message="Method timed out"):
59-
super().__init__(-32000, message)
61+
def __init__(self, message="Method timed out", data=None):
62+
super().__init__(-32000, message, data)
6063

6164

6265
class Aborted(JsonRpcError):
63-
def __init__(self, message="Method aborted"):
64-
super().__init__(-32001, message)
66+
def __init__(self, message="Method aborted", data=None):
67+
super().__init__(-32001, message, data)
6568

6669

6770
class ApplicationError(JsonRpcError):

tests/test_achievements.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ async def test_get_unlocked_achievements_success(plugin, read, write):
7979

8080

8181
@pytest.mark.asyncio
82-
@pytest.mark.parametrize("exception,code,message", [
83-
(BackendError, 4, "Backend error"),
84-
(KeyError, 0, "Unknown error")
82+
@pytest.mark.parametrize("exception,code,message,internal_type", [
83+
(BackendError, 4, "Backend error", "BackendError"),
84+
(KeyError, 0, "Unknown error", "UnknownError")
8585
])
86-
async def test_get_unlocked_achievements_error(exception, code, message, plugin, read, write):
86+
async def test_get_unlocked_achievements_error(exception, code, message, internal_type, plugin, read, write):
8787
plugin.prepare_achievements_context.return_value = async_return_value(None)
8888
request = {
8989
"jsonrpc": "2.0",
@@ -113,7 +113,8 @@ async def test_get_unlocked_achievements_error(exception, code, message, plugin,
113113
"game_id": "14",
114114
"error": {
115115
"code": code,
116-
"message": message
116+
"message": message,
117+
"data": {"internal_type" : internal_type}
117118
}
118119
}
119120
},
@@ -145,7 +146,8 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri
145146
"id": "3",
146147
"error": {
147148
"code": 4,
148-
"message": "Backend error"
149+
"message": "Backend error",
150+
"data": {"internal_type": "BackendError"}
149151
}
150152
}
151153
]
@@ -192,7 +194,8 @@ async def test_import_in_progress(plugin, read, write):
192194
"id": "4",
193195
"error": {
194196
"code": 600,
195-
"message": "Import already in progress"
197+
"message": "Import already in progress",
198+
"data": {"internal_type": "ImportInProgress"}
196199
}
197200
} in messages
198201

tests/test_authenticate.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,19 @@ async def test_success(plugin, read, write):
4343

4444

4545
@pytest.mark.asyncio
46-
@pytest.mark.parametrize("error,code,message", [
47-
pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"),
48-
pytest.param(BackendNotAvailable, 2, "Backend not available", id="backend_not_available"),
49-
pytest.param(BackendTimeout, 3, "Backend timed out", id="backend_timeout"),
50-
pytest.param(BackendError, 4, "Backend error", id="backend_error"),
51-
pytest.param(InvalidCredentials, 100, "Invalid credentials", id="invalid_credentials"),
52-
pytest.param(NetworkError, 101, "Network error", id="network_error"),
53-
pytest.param(ProtocolError, 103, "Protocol error", id="protocol_error"),
54-
pytest.param(TemporaryBlocked, 104, "Temporary blocked", id="temporary_blocked"),
55-
pytest.param(Banned, 105, "Banned", id="banned"),
56-
pytest.param(AccessDenied, 106, "Access denied", id="access_denied"),
46+
@pytest.mark.parametrize("error,code,message, internal_type", [
47+
pytest.param(UnknownError, 0, "Unknown error", "UnknownError"),
48+
pytest.param(BackendNotAvailable, 2, "Backend not available", "BackendNotAvailable"),
49+
pytest.param(BackendTimeout, 3, "Backend timed out", "BackendTimeout"),
50+
pytest.param(BackendError, 4, "Backend error", "BackendError"),
51+
pytest.param(InvalidCredentials, 100, "Invalid credentials", "InvalidCredentials"),
52+
pytest.param(NetworkError, 101, "Network error", "NetworkError"),
53+
pytest.param(ProtocolError, 103, "Protocol error", "ProtocolError"),
54+
pytest.param(TemporaryBlocked, 104, "Temporary blocked", "TemporaryBlocked"),
55+
pytest.param(Banned, 105, "Banned", "Banned"),
56+
pytest.param(AccessDenied, 106, "Access denied", "AccessDenied"),
5757
])
58-
async def test_failure(plugin, read, write, error, code, message):
58+
async def test_failure(plugin, read, write, error, code, message, internal_type):
5959
request = {
6060
"jsonrpc": "2.0",
6161
"id": "3",
@@ -73,7 +73,8 @@ async def test_failure(plugin, read, write, error, code, message):
7373
"id": "3",
7474
"error": {
7575
"code": code,
76-
"message": message
76+
"message": message,
77+
"data" : {"internal_type" : internal_type}
7778
}
7879
}
7980
]

tests/test_errors.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,50 @@
33
import galaxy.api.jsonrpc as jsonrpc
44

55

6+
@pytest.mark.parametrize("data", [
7+
{"key1": "value", "key2": "value2"},
8+
{},
9+
{"key1": ["list", "of", "things"], "key2": None},
10+
{"key1": ("tuple", Exception)},
11+
])
12+
def test_valid_error_data(data):
13+
test_message = "Test error message"
14+
test_code = 1
15+
err_obj = jsonrpc.JsonRpcError(code=test_code, message=test_message, data=data)
16+
data.update({"internal_type": "JsonRpcError"})
17+
expected_json = {"code": 1, "data": data, "message": "Test error message"}
18+
assert err_obj.json() == expected_json
19+
20+
21+
def test_error_default_data():
22+
test_message = "Test error message"
23+
test_code = 1
24+
err_obj = jsonrpc.JsonRpcError(code=test_code, message=test_message)
25+
expected_json = {"code": test_code, "data": {"internal_type": "JsonRpcError"}, "message": test_message}
26+
assert err_obj.json() == expected_json
27+
28+
29+
@pytest.mark.parametrize("data", [
30+
123,
31+
["not", "a", "mapping"],
32+
"nor is this"
33+
])
34+
def test_invalid_error_data(data):
35+
test_message = "Test error message"
36+
test_code = 1
37+
with pytest.raises(TypeError):
38+
jsonrpc.JsonRpcError(code=test_code, message=test_message, data=data)
39+
40+
41+
def test_error_override_internal_type():
42+
test_message = "Test error message"
43+
test_code = 1
44+
test_data = {"internal_type": "SomeUserProvidedType", "details": "some more data"}
45+
err_obj = jsonrpc.JsonRpcError(code=test_code, message=test_message, data=test_data)
46+
expected_json = {"code": test_code, "data": {"details": "some more data", "internal_type": "JsonRpcError"}, "message": test_message}
47+
assert err_obj.json() == expected_json
48+
49+
650
@pytest.mark.parametrize("error, expected_error_msg", [
751
(errors.AuthenticationRequired, "Authentication required"),
852
(errors.BackendNotAvailable, "Backend not available"),

tests/test_friends.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ async def test_get_friends_failure(plugin, read, write):
6161
"error": {
6262
"code": 0,
6363
"message": "Unknown error",
64+
"data": {"internal_type": "UnknownError"}
6465
}
6566
}
6667
]

tests/test_game_library_settings.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ async def test_get_library_settings_success(plugin, read, write):
7979

8080

8181
@pytest.mark.asyncio
82-
@pytest.mark.parametrize("exception,code,message", [
83-
(BackendError, 4, "Backend error"),
84-
(KeyError, 0, "Unknown error")
82+
@pytest.mark.parametrize("exception,code,message,internal_type", [
83+
(BackendError, 4, "Backend error", "BackendError"),
84+
(KeyError, 0, "Unknown error", "UnknownError")
8585
])
86-
async def test_get_game_library_settings_error(exception, code, message, plugin, read, write):
86+
async def test_get_game_library_settings_error(exception, code, message, internal_type, plugin, read, write):
8787
plugin.prepare_game_library_settings_context.return_value = async_return_value(None)
8888
request = {
8989
"jsonrpc": "2.0",
@@ -112,7 +112,8 @@ async def test_get_game_library_settings_error(exception, code, message, plugin,
112112
"game_id": "6",
113113
"error": {
114114
"code": code,
115-
"message": message
115+
"message": message,
116+
"data": {"internal_type": internal_type}
116117
}
117118
}
118119
},
@@ -144,7 +145,8 @@ async def test_prepare_get_game_library_settings_context_error(plugin, read, wri
144145
"id": "3",
145146
"error": {
146147
"code": 4,
147-
"message": "Backend error"
148+
"message": "Backend error",
149+
"data": {"internal_type": "BackendError"}
148150
}
149151
}
150152
]
@@ -190,7 +192,8 @@ async def test_import_in_progress(plugin, read, write):
190192
"id": "4",
191193
"error": {
192194
"code": 600,
193-
"message": "Import already in progress"
195+
"message": "Import already in progress",
196+
"data": {"internal_type": "ImportInProgress"}
194197
}
195198
} in messages
196199

tests/test_game_times.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ async def test_get_game_time_success(plugin, read, write):
7979

8080

8181
@pytest.mark.asyncio
82-
@pytest.mark.parametrize("exception,code,message", [
83-
(BackendError, 4, "Backend error"),
84-
(KeyError, 0, "Unknown error")
82+
@pytest.mark.parametrize("exception,code,message, internal_type", [
83+
(BackendError, 4, "Backend error", "BackendError"),
84+
(KeyError, 0, "Unknown error", "UnknownError")
8585
])
86-
async def test_get_game_time_error(exception, code, message, plugin, read, write):
86+
async def test_get_game_time_error(exception, code, message, internal_type, plugin, read, write):
8787
plugin.prepare_game_times_context.return_value = async_return_value(None)
8888
request = {
8989
"jsonrpc": "2.0",
@@ -112,7 +112,8 @@ async def test_get_game_time_error(exception, code, message, plugin, read, write
112112
"game_id": "6",
113113
"error": {
114114
"code": code,
115-
"message": message
115+
"message": message,
116+
"data" : {"internal_type" : internal_type}
116117
}
117118
}
118119
},
@@ -144,7 +145,8 @@ async def test_prepare_get_game_time_context_error(plugin, read, write):
144145
"id": "3",
145146
"error": {
146147
"code": 4,
147-
"message": "Backend error"
148+
"message": "Backend error",
149+
"data": {"internal_type": "BackendError"}
148150
}
149151
}
150152
]
@@ -190,7 +192,8 @@ async def test_import_in_progress(plugin, read, write):
190192
"id": "4",
191193
"error": {
192194
"code": 600,
193-
"message": "Import already in progress"
195+
"message": "Import already in progress",
196+
"data": {"internal_type": "ImportInProgress"}
194197
}
195198
} in messages
196199

tests/test_local_games.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ async def test_success(plugin, read, write):
5151

5252
@pytest.mark.asyncio
5353
@pytest.mark.parametrize(
54-
"error,code,message",
54+
"error,code,message, internal_type",
5555
[
56-
pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"),
57-
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing")
56+
pytest.param(UnknownError, 0, "Unknown error", "UnknownError", id="unknown_error"),
57+
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", "FailedParsingManifest", id="failed_parsing")
5858
],
5959
)
60-
async def test_failure(plugin, read, write, error, code, message):
60+
async def test_failure(plugin, read, write, error, code, message, internal_type):
6161
request = {
6262
"jsonrpc": "2.0",
6363
"id": "3",
@@ -74,7 +74,8 @@ async def test_failure(plugin, read, write, error, code, message):
7474
"id": "3",
7575
"error": {
7676
"code": code,
77-
"message": message
77+
"message": message,
78+
"data" : {"internal_type" : internal_type}
7879
}
7980
}
8081
]

0 commit comments

Comments
 (0)