Coverage for seedboxsync/front/apis/core/error.py: 100%

20 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-26 17:14 +0000

1# 

2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr> 

3# 

4# For the full copyright and license information, please view the LICENSE 

5# file that was distributed with this source code. 

6# 

7"""SeedboxSync api error module.""" 

8 

9from datetime import datetime 

10from typing import Any 

11import uuid 

12from flask import Response, jsonify 

13from werkzeug.exceptions import BadRequest, HTTPException, NotFound, Unauthorized 

14from seedboxsync.front.apis import api 

15from seedboxsync.front.babel import gettext as _ 

16 

17 

18@api.errorhandler(BadRequest) # type: ignore[untyped-decorator] 

19@api.errorhandler(NotFound) # type: ignore[untyped-decorator] 

20@api.errorhandler(Unauthorized) # type: ignore[untyped-decorator] 

21def api_errorhandler(error: BadRequest | NotFound | Unauthorized) -> tuple[dict[str, Any], int]: 

22 """ 

23 Handle validation and not-found API errors. 

24 

25 Args: 

26 error (BadRequest | NotFound | Unauthorized): HTTP exception to serialize. 

27 

28 Returns: 

29 tuple[dict[str, Any], int]: Empty response body and HTTP status code. 

30 """ 

31 status_code = error.code or 500 

32 

33 # Get Flask-RESTX error data or build it from the HTTP exception 

34 data = getattr( 

35 error, 

36 "data", 

37 { 

38 "message": error.name, 

39 "errors": error.description, 

40 }, 

41 ) 

42 

43 error.data = { # type: ignore[union-attr] 

44 "type": "about:blank", 

45 "success": False, 

46 "status": status_code, 

47 "title": data.get("message", ""), 

48 **({"message": data["errors"]} if "errors" in data else {}), 

49 "timestamp": datetime.now().astimezone().isoformat(), 

50 "traceId": str(uuid.uuid4()), 

51 } 

52 

53 return {}, status_code 

54 

55 

56def error(exc: Exception) -> tuple[Response, int | None]: 

57 """ 

58 Serialize an exception as a JSON API response. 

59 

60 Args: 

61 exc (Exception): Exception raised while processing the request. 

62 

63 Returns: 

64 tuple[Response, int | None]: JSON response and HTTP status code. 

65 """ 

66 status_code = exc.code if isinstance(exc, HTTPException) else 500 

67 title = exc.name if isinstance(exc, HTTPException) else _("Internal Server Error") 

68 message = exc.description if isinstance(exc, HTTPException) else str(exc) 

69 

70 return ( 

71 jsonify( 

72 { 

73 "type": "about:blank", 

74 "success": False, 

75 "status": status_code, 

76 "title": title, 

77 "message": message, 

78 "timestamp": datetime.now().astimezone().isoformat(), 

79 "traceId": str(uuid.uuid4()), 

80 } 

81 ), 

82 status_code, 

83 )