Coverage for opt/mealie/lib/python3.12/site-packages/mealie/routes/_base/mixins.py: 87%

66 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-11-25 17:29 +0000

1from collections.abc import Callable 1t

2from logging import Logger 1t

3 

4import sqlalchemy.exc 1t

5from fastapi import HTTPException, status 1t

6from pydantic import UUID4, BaseModel 1t

7 

8from mealie.repos.repository_generic import RepositoryGeneric 1t

9from mealie.schema.response import ErrorResponse 1t

10 

11 

12class HttpRepo[C: BaseModel, R: BaseModel, U: BaseModel]: 1t

13 """ 

14 The HttpRepo[C, R, U] class is a mixin class that provides a common set of methods for CRUD operations. 

15 This class is intended to be used in a composition pattern where a class has a mixin property. For example: 

16 

17 ``` 

18 class MyClass: 

19 def __init__(self, repo, logger): 

20 self.mixins = HttpRepo(repo, logger) 

21 ``` 

22 

23 """ 

24 

25 repo: RepositoryGeneric 1t

26 exception_msgs: Callable[[type[Exception]], str] | None 1t

27 default_message: str = "An unexpected error occurred." 1t

28 

29 def __init__( 1t

30 self, 

31 repo: RepositoryGeneric, 

32 logger: Logger, 

33 exception_msgs: Callable[[type[Exception]], str] | None = None, 

34 default_message: str | None = None, 

35 ) -> None: 

36 self.repo = repo 1CKDIERLTSfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

37 self.logger = logger 1CKDIERLTSfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

38 self.exception_msgs = exception_msgs 1CKDIERLTSfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

39 

40 if default_message: 1CKDIERLTSfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

41 self.default_message = default_message 1fpqulvbhmwignzdcAekBxa

42 

43 def get_exception_message(self, ext: Exception) -> str: 1t

44 if self.exception_msgs: 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa

45 return self.exception_msgs(type(ext)) 1CIEfpqulvbhmwiygnzodcAjerFGHksBxa

46 return self.default_message 1Dfulvbhmwiynoderxa

47 

48 def handle_exception(self, ex: Exception) -> None: 1t

49 # Cleanup 

50 self.logger.exception(ex) 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa

51 self.repo.session.rollback() 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa

52 

53 # Respond 

54 msg = self.get_exception_message(ex) 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa

55 

56 if isinstance(ex, sqlalchemy.exc.NoResultFound): 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa

57 raise HTTPException( 1Ebdea

58 status.HTTP_404_NOT_FOUND, 

59 detail=ErrorResponse.respond(message=msg, exception=str(ex)), 

60 ) 

61 else: 

62 raise HTTPException( 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa

63 status.HTTP_400_BAD_REQUEST, 

64 detail=ErrorResponse.respond(message=msg, exception=str(ex)), 

65 ) 

66 

67 def create_one(self, data: C) -> R | None: 1t

68 item: R | None = None 1CKDLfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

69 try: 1CKDLfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

70 item = self.repo.create(data) 1CKDLfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

71 except Exception as ex: 1CDfpqulvbhmwiygnzodcAjerFGHksBxa

72 self.handle_exception(ex) 1CDfpqulvbhmwiygnzodcAjerFGHksBxa

73 

74 return item 1CKDLfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa

75 

76 def get_one(self, item_id: int | str | UUID4, key: str | None = None) -> R: 1t

77 item = self.repo.get_one(item_id, key) 1Rbhgdcjerksa

78 

79 if not item: 1Rbhgdcjerksa

80 raise HTTPException( 1Rbcjka

81 status.HTTP_404_NOT_FOUND, 

82 detail=ErrorResponse.respond(message="Not found."), 

83 ) 

84 

85 return item 1hgdjersa

86 

87 def update_one(self, data: U, item_id: int | str | UUID4) -> R: 1t

88 item = self.repo.get_one(item_id) 1TSfplbhmgoca

89 

90 if not item: 1Sfplbhmgoca

91 raise HTTPException( 1Sa

92 status.HTTP_404_NOT_FOUND, 

93 detail=ErrorResponse.respond(message="Not found."), 

94 ) 

95 

96 try: 1fplbhmgoca

97 item = self.repo.update(item_id, data) # type: ignore 1fplbhmgoca

98 except Exception as ex: 1goa

99 self.handle_exception(ex) 1goa

100 

101 return item 1fplbhmgca

102 

103 def patch_one(self, data: U, item_id: int | str | UUID4) -> R: 1t

104 item = self.repo.get_one(item_id) 

105 

106 if not item: 

107 raise HTTPException( 

108 status.HTTP_404_NOT_FOUND, 

109 detail=ErrorResponse.respond(message="Not found."), 

110 ) 

111 

112 try: 

113 item = self.repo.patch(item_id, data.model_dump(exclude_unset=True, exclude_defaults=True)) 

114 except Exception as ex: 

115 self.handle_exception(ex) 

116 

117 return item 

118 

119 def delete_one(self, item_id: int | str | UUID4) -> R | None: 1t

120 item: R | None = None 1IEfqbindcjeJka

121 try: 1IEfqbindcjeJka

122 item = self.repo.delete(item_id) 1IEfqbindcjeJka

123 self.logger.info(f"Deleting item with id {item_id}") 1fqbindcjeJka

124 except Exception as ex: 1IEbidea

125 self.handle_exception(ex) 1IEbidea

126 

127 return item 1fqbindcjeJka