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
« 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
4import sqlalchemy.exc 1t
5from fastapi import HTTPException, status 1t
6from pydantic import UUID4, BaseModel 1t
8from mealie.repos.repository_generic import RepositoryGeneric 1t
9from mealie.schema.response import ErrorResponse 1t
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:
17 ```
18 class MyClass:
19 def __init__(self, repo, logger):
20 self.mixins = HttpRepo(repo, logger)
21 ```
23 """
25 repo: RepositoryGeneric 1t
26 exception_msgs: Callable[[type[Exception]], str] | None 1t
27 default_message: str = "An unexpected error occurred." 1t
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
40 if default_message: 1CKDIERLTSfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa
41 self.default_message = default_message 1fpqulvbhmwignzdcAekBxa
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
48 def handle_exception(self, ex: Exception) -> None: 1t
49 # Cleanup
50 self.logger.exception(ex) 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa
51 self.repo.session.rollback() 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa
53 # Respond
54 msg = self.get_exception_message(ex) 1CDIEfpqulvbhmwiygnzodcAjerFGHksBxa
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 )
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
74 return item 1CKDLfpqulvbhmwMiyNgnzodcAjerJOFGPHksBQxa
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
79 if not item: 1Rbhgdcjerksa
80 raise HTTPException( 1Rbcjka
81 status.HTTP_404_NOT_FOUND,
82 detail=ErrorResponse.respond(message="Not found."),
83 )
85 return item 1hgdjersa
87 def update_one(self, data: U, item_id: int | str | UUID4) -> R: 1t
88 item = self.repo.get_one(item_id) 1TSfplbhmgoca
90 if not item: 1Sfplbhmgoca
91 raise HTTPException( 1Sa
92 status.HTTP_404_NOT_FOUND,
93 detail=ErrorResponse.respond(message="Not found."),
94 )
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
101 return item 1fplbhmgca
103 def patch_one(self, data: U, item_id: int | str | UUID4) -> R: 1t
104 item = self.repo.get_one(item_id)
106 if not item:
107 raise HTTPException(
108 status.HTTP_404_NOT_FOUND,
109 detail=ErrorResponse.respond(message="Not found."),
110 )
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)
117 return item
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
127 return item 1fqbindcjeJka