Passed
Push — master ( 1de29a...581576 )
by Michael
03:27
created

JsonRpcResponse.to_dict()   A

Complexity

Conditions 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 15
rs 9.9
c 0
b 0
f 0
cc 4
nop 1
1
import typing
2
from dataclasses import dataclass, field
3
4
from .. import constants, errors, utils
5
6
7
__all__ = (
8
    'JsonRpcResponse',
9
)
10
11
12
@dataclass
13
class JsonRpcResponse:
14
    jsonrpc: str = constants.VERSION_2_0
15
    msg_id: typing.Any = constants.NOTHING
16
    result: typing.Any = constants.NOTHING
17
    error: typing.Optional[errors.JsonRpcError] = None
18
    context: dict = field(default_factory=dict)
19
20
    @property
21
    def is_notification(self) -> bool:
22
        return self.msg_id is constants.NOTHING
23
24
    @classmethod
25
    def from_dict(cls, data: dict, *, error_map: typing.Optional[dict] = None, **kwargs) -> 'JsonRpcResponse':
26
        cls._validate_json_response(data)
27
28
        response = cls(
29
            msg_id=data.get('id', constants.NOTHING),
30
            jsonrpc=data.get('jsonrpc', constants.VERSION_2_0),
31
            result=data.get('result'),
32
            **kwargs,
33
        )
34
35
        if 'error' in data:
36
            cls._add_error(response, data['error'], error_map=error_map)
37
38
        return response
39
40
    def to_dict(self) -> typing.Optional[dict]:
41
        if self.msg_id is constants.NOTHING:
42
            return None
43
44
        data = {'id': self.msg_id, 'jsonrpc': self.jsonrpc}
45
46
        if self.error is constants.NOTHING:
47
            data['result'] = self.result
48
        else:
49
            data['error'] = {'code': self.error.code, 'message': self.error.message}
50
51
            if self.error.data is not None:
52
                data['error']['data'] = self.error.data
53
54
        return data
55
56
    @staticmethod
57
    def _validate_json_response(data: typing.Any) -> None:
58
        if not isinstance(data, dict):
59
            raise errors.InvalidRequest
60
61
        utils.validate_jsonrpc(data.get('jsonrpc'))
62
63
        if 'result' not in data and 'error' not in data:
64
            raise errors.InvalidRequest('"result" or "error" not found in data.', data={'raw_response': data})
65
66
    @staticmethod
67
    def _add_error(response: 'JsonRpcResponse', error: typing.Any, *, error_map: typing.Optional[dict] = None) -> None:
68
        if not isinstance(error, dict):
69
            raise errors.InvalidRequest
70
71
        if not ({'code', 'message'}) <= error.keys():
72
            raise errors.InvalidRequest
73
74
        if error_map:
75
            exception_class = error_map.get(error['code'], errors.JsonRpcError)
76
        else:
77
            exception_class = errors.JsonRpcError
78
79
        response.error = exception_class(
80
            message=error['message'],
81
            data=error.get('data'),
82
            code=error['code'],
83
        )
84