@@ -42,36 +42,107 @@ def is_api_request(request: web.Request, api_version: str) -> bool:
4242 return bool (request .path .startswith (base_path ))
4343
4444
45- def error_middleware_factory ( # noqa: C901
46- api_version : str ,
47- ) -> Middleware :
48- _is_prod : bool = is_production_environ ()
49-
50- def _process_and_raise_unexpected_error (request : web .BaseRequest , err : Exception ):
51- error_code = create_error_code (err )
52- error_context : dict [str , Any ] = {
53- "request.remote" : f"{ request .remote } " ,
54- "request.method" : f"{ request .method } " ,
55- "request.path" : f"{ request .path } " ,
56- }
57-
58- user_error_msg = _FMSG_INTERNAL_ERROR_USER_FRIENDLY
59- http_error = create_http_error (
60- err ,
45+ def _process_and_raise_unexpected_error (
46+ request : web .BaseRequest , err : Exception , * , skip_internal_error_details : bool
47+ ):
48+ """Process unexpected exceptions and raise them as HTTP errors with proper formatting."""
49+ error_code = create_error_code (err )
50+ error_context : dict [str , Any ] = {
51+ "request.remote" : f"{ request .remote } " ,
52+ "request.method" : f"{ request .method } " ,
53+ "request.path" : f"{ request .path } " ,
54+ }
55+
56+ user_error_msg = _FMSG_INTERNAL_ERROR_USER_FRIENDLY
57+ http_error = create_http_error (
58+ err ,
59+ user_error_msg ,
60+ web .HTTPInternalServerError ,
61+ skip_internal_error_details = skip_internal_error_details ,
62+ error_code = error_code ,
63+ )
64+ _logger .exception (
65+ ** create_troubleshotting_log_kwargs (
6166 user_error_msg ,
62- web . HTTPInternalServerError ,
63- skip_internal_error_details = _is_prod ,
67+ error = err ,
68+ error_context = error_context ,
6469 error_code = error_code ,
6570 )
66- _logger .exception (
67- ** create_troubleshotting_log_kwargs (
68- user_error_msg ,
69- error = err ,
70- error_context = error_context ,
71- error_code = error_code ,
72- )
71+ )
72+ raise http_error
73+
74+
75+ def _handle_http_error (err : web .HTTPError ) -> None :
76+ """Handle standard HTTP errors by ensuring they're properly formatted."""
77+ err .content_type = MIMETYPE_APPLICATION_JSON
78+ if err .reason :
79+ err .set_status (err .status , safe_status_message (message = err .reason ))
80+
81+ if not err .text or not is_enveloped_from_text (err .text ):
82+ error_message = err .text or err .reason or "Unexpected error"
83+ error_model = ErrorGet (
84+ errors = [
85+ ErrorItemType .from_error (err ),
86+ ],
87+ status = err .status ,
88+ logs = [
89+ LogMessageType (message = error_message , level = "ERROR" ),
90+ ],
91+ message = error_message ,
7392 )
74- raise http_error
93+ err .text = EnvelopeFactory (error = error_model ).as_text ()
94+
95+
96+ def _handle_http_successful (
97+ err : web .HTTPSuccessful , request : web .Request , * , skip_internal_error_details : bool
98+ ) -> None :
99+ """Handle successful HTTP responses, ensuring they're properly enveloped."""
100+ err .content_type = MIMETYPE_APPLICATION_JSON
101+ if err .reason :
102+ err .set_status (err .status , safe_status_message (message = err .reason ))
103+
104+ if err .text :
105+ try :
106+ payload = json_loads (err .text )
107+ if not is_enveloped_from_map (payload ):
108+ payload = wrap_as_envelope (data = payload )
109+ err .text = json_dumps (payload )
110+ except Exception as other_error : # pylint: disable=broad-except
111+ _process_and_raise_unexpected_error (
112+ request ,
113+ other_error ,
114+ skip_internal_error_details = skip_internal_error_details ,
115+ )
116+
117+
118+ def _handle_not_implemented (
119+ err : NotImplementedError , * , skip_internal_error_details : bool
120+ ) -> None :
121+ """Handle NotImplementedError by converting to appropriate HTTP error."""
122+ http_error = create_http_error (
123+ err ,
124+ f"{ err } " ,
125+ web .HTTPNotImplemented ,
126+ skip_internal_error_details = skip_internal_error_details ,
127+ )
128+ raise http_error from err
129+
130+
131+ def _handle_timeout (err : TimeoutError , * , skip_internal_error_details : bool ) -> None :
132+ """Handle TimeoutError by converting to appropriate HTTP error."""
133+ http_error = create_http_error (
134+ err ,
135+ f"{ err } " ,
136+ web .HTTPGatewayTimeout ,
137+ skip_internal_error_details = skip_internal_error_details ,
138+ )
139+ raise http_error from err
140+
141+
142+ def error_middleware_factory ( # noqa: C901
143+ api_version : str ,
144+ ) -> Middleware :
145+ _is_prod : bool = is_production_environ ()
75146
76147 @web .middleware
77148 async def _middleware_handler (request : web .Request , handler : Handler ): # noqa: C901
@@ -86,71 +157,30 @@ async def _middleware_handler(request: web.Request, handler: Handler): # noqa:
86157 return await handler (request )
87158
88159 except web .HTTPError as err :
89-
90- err .content_type = MIMETYPE_APPLICATION_JSON
91- if err .reason :
92- err .set_status (err .status , safe_status_message (message = err .reason ))
93-
94- if not err .text or not is_enveloped_from_text (err .text ):
95- error_message = err .text or err .reason or "Unexpected error"
96- error_model = ErrorGet (
97- errors = [
98- ErrorItemType .from_error (err ),
99- ],
100- status = err .status ,
101- logs = [
102- LogMessageType (message = error_message , level = "ERROR" ),
103- ],
104- message = error_message ,
105- )
106- err .text = EnvelopeFactory (error = error_model ).as_text ()
107-
160+ _handle_http_error (err )
108161 raise
109162
110163 except web .HTTPSuccessful as err :
111- err .content_type = MIMETYPE_APPLICATION_JSON
112- if err .reason :
113- err .set_status (err .status , safe_status_message (message = err .reason ))
114-
115- if err .text :
116- try :
117- payload = json_loads (err .text )
118- if not is_enveloped_from_map (payload ):
119- payload = wrap_as_envelope (data = payload )
120- err .text = json_dumps (payload )
121- except Exception as other_error : # pylint: disable=broad-except
122- _process_and_raise_unexpected_error (request , other_error )
164+ _handle_http_successful (err , request , skip_internal_error_details = _is_prod )
123165 raise
124166
125167 except web .HTTPRedirection as err :
126168 _logger .debug ("Redirected to %s" , err )
127169 raise
128170
129171 except NotImplementedError as err :
130- http_error = create_http_error (
131- err ,
132- f"{ err } " ,
133- web .HTTPNotImplemented ,
134- skip_internal_error_details = _is_prod ,
135- )
136- raise http_error from err
172+ _handle_not_implemented (err , skip_internal_error_details = _is_prod )
137173
138174 except TimeoutError as err :
139- http_error = create_http_error (
140- err ,
141- f"{ err } " ,
142- web .HTTPGatewayTimeout ,
143- skip_internal_error_details = _is_prod ,
144- )
145- raise http_error from err
175+ _handle_timeout (err , skip_internal_error_details = _is_prod )
146176
147177 except Exception as err : # pylint: disable=broad-except
148- _process_and_raise_unexpected_error (request , err )
178+ _process_and_raise_unexpected_error (
179+ request , err , skip_internal_error_details = _is_prod
180+ )
149181
150182 # adds identifier (mostly for debugging)
151- setattr ( # noqa: B010
152- _middleware_handler , "__middleware_name__" , f"{ __name__ } .error_{ api_version } "
153- )
183+ _middleware_handler .__middleware_name__ = f"{ __name__ } .error_{ api_version } "
154184
155185 return _middleware_handler
156186
0 commit comments