1
1
# (C) 2024 GoodData Corporation
2
2
import base64
3
3
import traceback
4
- from typing import Callable , Optional , Union
4
+ from typing import Callable , Optional , Union , cast
5
5
6
6
import orjson
7
7
import pyarrow .flight
8
8
9
9
from gooddata_flight_server .errors .error_code import ErrorCode
10
10
11
+ _ERROR_INFO_MAX_MSG = 256
12
+ _ERROR_INFO_MAX_DETAIL = 512
13
+
14
+
15
+ def _truncate_str_value (val : Optional [str ], max_len : int ) -> Optional [str ]:
16
+ if val is None :
17
+ return None
18
+
19
+ if len (val ) <= max_len :
20
+ return val
21
+
22
+ # no big deal that the actual max length is slightly exceeded
23
+ # all this truncating happens because ErrorInfo is eventually
24
+ # passed via gRPC headers which have 16k hard limit
25
+ return val [:max_len ] + " [truncated]"
26
+
11
27
12
28
class ErrorInfo :
13
29
"""
@@ -22,15 +38,15 @@ def __init__(
22
38
body : Optional [bytes ] = None ,
23
39
code : int = 0 ,
24
40
) -> None :
25
- self ._msg = msg
26
- self ._detail : Optional [str ] = detail
41
+ self ._msg = cast ( str , _truncate_str_value ( msg , _ERROR_INFO_MAX_MSG ))
42
+ self ._detail : Optional [str ] = _truncate_str_value ( detail , _ERROR_INFO_MAX_DETAIL )
27
43
self ._body : Optional [bytes ] = body
28
44
self ._code : int = code
29
45
30
46
@property
31
47
def msg (self ) -> str :
32
48
"""
33
- :return: human readable error message
49
+ :return: human- readable error message
34
50
"""
35
51
return self ._msg
36
52
@@ -60,26 +76,36 @@ def with_msg(self, msg: str) -> "ErrorInfo":
60
76
"""
61
77
Updates error message.
62
78
63
- :param msg: new message
79
+ :param msg: new message, up to 256 characters; will be truncated if the limit is exceeded
64
80
:return: self, for call chaining sakes
65
81
"""
66
- self ._msg = msg
82
+ self ._msg = cast (str , _truncate_str_value (msg , _ERROR_INFO_MAX_MSG ))
83
+
67
84
return self
68
85
69
86
def with_detail (self , detail : Optional [str ] = None ) -> "ErrorInfo" :
70
87
"""
71
88
Updates or resets the error detail.
72
89
73
- :param detail: detail to set; if None, the detail stored in the meta will be removed; default is None
90
+ :param detail: detail to set; if None, the detail stored in the meta will be removed; default is None;
91
+ detail can be up to 512 characters; will be truncated if the limit is exceeded
74
92
:return: self, for call chaining sakes
75
93
"""
76
- self ._detail = detail
94
+ self ._detail = _truncate_str_value (detail , _ERROR_INFO_MAX_DETAIL )
95
+
77
96
return self
78
97
79
98
def with_body (self , body : Optional [Union [bytes , str ]]) -> "ErrorInfo" :
80
99
"""
81
100
Updates or resets the error body.
82
101
102
+ IMPORTANT: the ErrorInfo (and thus the contents of `body`) are passed out via FlightError.extra_info
103
+ property. The Flight RPC implementations pass the `extra_info` via gRPC headers. In turn, the gRPC headers
104
+ do have size limit. Keep this in mind when designing the value of `body`.
105
+
106
+ If you set body that is too large, you will run into problems like this:
107
+ https://github.com/grpc/grpc/issues/37852.
108
+
83
109
:param body: body to set; if None, the body stored in the meta will be removed; default is None
84
110
:return: self, for call chaining sakes
85
111
"""
0 commit comments