mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-06-23 15:31:37 -04:00
refactor(backend): ♻️ change error messages to follow standard pattern to match locals on frontend (#668)
Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
abc0d0d59f
commit
b550dae593
159
dev/scripts/gen_error_messages.py
Normal file
159
dev/scripts/gen_error_messages.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import json
|
||||||
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
|
CWD = Path(__file__).parent
|
||||||
|
|
||||||
|
PROJECT_BASE = CWD.parent.parent
|
||||||
|
|
||||||
|
server_side_msgs = PROJECT_BASE / "mealie" / "utils" / "error_messages.py"
|
||||||
|
en_us_msgs = PROJECT_BASE / "frontend" / "lang" / "errors" / "en-US.json"
|
||||||
|
client_side_msgs = PROJECT_BASE / "frontend" / "utils" / "error-messages.ts"
|
||||||
|
|
||||||
|
GENERATE_MESSAGES = [
|
||||||
|
# User Related
|
||||||
|
"user",
|
||||||
|
"webhook",
|
||||||
|
"token",
|
||||||
|
# Group Related
|
||||||
|
"group",
|
||||||
|
"cookbook",
|
||||||
|
"mealplan",
|
||||||
|
# Recipe Related
|
||||||
|
"scraper",
|
||||||
|
"recipe",
|
||||||
|
"ingredient",
|
||||||
|
"food",
|
||||||
|
"unit",
|
||||||
|
# Admin Related
|
||||||
|
"backup",
|
||||||
|
"migration",
|
||||||
|
"event",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorMessage:
|
||||||
|
def __init__(self, prefix, verb) -> None:
|
||||||
|
self.message = f"{prefix.title()} {verb.title()} Failed"
|
||||||
|
self.snake = slugify(self.message, separator="_")
|
||||||
|
self.kabab = slugify(self.message, separator="-")
|
||||||
|
|
||||||
|
def factory(prefix) -> list["ErrorMessage"]:
|
||||||
|
verbs = ["Create", "Update", "Delete"]
|
||||||
|
return [ErrorMessage(prefix, verb) for verb in verbs]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CodeGenLines:
|
||||||
|
start: int
|
||||||
|
end: int
|
||||||
|
|
||||||
|
indentation: str
|
||||||
|
text: list[str]
|
||||||
|
|
||||||
|
_next_line = None
|
||||||
|
|
||||||
|
def purge_lines(self) -> None:
|
||||||
|
start = self.start + 1
|
||||||
|
end = self.end
|
||||||
|
del self.text[start:end]
|
||||||
|
|
||||||
|
def push_line(self, string: str) -> None:
|
||||||
|
self._next_line = self._next_line or self.start + 1
|
||||||
|
self.text.insert(self._next_line, self.indentation + string)
|
||||||
|
self._next_line += 1
|
||||||
|
|
||||||
|
|
||||||
|
def find_start(file_text: list[str], gen_id: str):
|
||||||
|
for x, line in enumerate(file_text):
|
||||||
|
if "CODE_GEN_ID:" in line and gen_id in line:
|
||||||
|
return x, line
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_end(file_text: list[str], gen_id: str):
|
||||||
|
for x, line in enumerate(file_text):
|
||||||
|
if f"END {gen_id}" in line:
|
||||||
|
return x, line
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_indentation_of_string(line: str):
|
||||||
|
return re.sub(r"#.*", "", line).removesuffix("\n")
|
||||||
|
|
||||||
|
|
||||||
|
def get_messages(message_prefix: str) -> str:
|
||||||
|
prefix = message_prefix.lower()
|
||||||
|
|
||||||
|
return [
|
||||||
|
f'{prefix}_create_failure = "{prefix}-create-failure"\n',
|
||||||
|
f'{prefix}_update_failure = "{prefix}-update-failure"\n',
|
||||||
|
f'{prefix}_delete_failure = "{prefix}-delete-failure"\n',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def code_gen_factory(file_path: Path) -> CodeGenLines:
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
text = file.readlines()
|
||||||
|
start_num, line = find_start(text, "ERROR_MESSAGE_ENUMS")
|
||||||
|
indentation = get_indentation_of_string(line)
|
||||||
|
end_num, line = find_end(text, "ERROR_MESSAGE_ENUMS")
|
||||||
|
|
||||||
|
return CodeGenLines(
|
||||||
|
start=start_num,
|
||||||
|
end=end_num,
|
||||||
|
indentation=indentation,
|
||||||
|
text=text,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def write_to_locals(messages: list[ErrorMessage]) -> None:
|
||||||
|
with open(en_us_msgs, "r") as f:
|
||||||
|
existing_msg = json.loads(f.read())
|
||||||
|
|
||||||
|
for msg in messages:
|
||||||
|
if msg.kabab in existing_msg:
|
||||||
|
continue
|
||||||
|
|
||||||
|
existing_msg[msg.kabab] = msg.message
|
||||||
|
print(f"Added Key {msg.kabab} to 'en-US.json'")
|
||||||
|
|
||||||
|
with open(en_us_msgs, "w") as f:
|
||||||
|
f.write(json.dumps(existing_msg, indent=4))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Starting...")
|
||||||
|
GENERATE_MESSAGES.sort()
|
||||||
|
|
||||||
|
code_gen = code_gen_factory(server_side_msgs)
|
||||||
|
code_gen.purge_lines()
|
||||||
|
|
||||||
|
messages = []
|
||||||
|
for msg_type in GENERATE_MESSAGES:
|
||||||
|
messages += get_messages(msg_type)
|
||||||
|
messages.append("\n")
|
||||||
|
|
||||||
|
for msg in messages:
|
||||||
|
code_gen.push_line(msg)
|
||||||
|
|
||||||
|
with open(server_side_msgs, "w") as file:
|
||||||
|
file.writelines(code_gen.text)
|
||||||
|
|
||||||
|
# Locals
|
||||||
|
|
||||||
|
local_msgs = []
|
||||||
|
for msg_type in GENERATE_MESSAGES:
|
||||||
|
local_msgs += ErrorMessage.factory(msg_type)
|
||||||
|
|
||||||
|
write_to_locals(local_msgs)
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
File diff suppressed because one or more lines are too long
44
frontend/lang/errors/en-US.json
Normal file
44
frontend/lang/errors/en-US.json
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"backup-create-failed": "Backup Create Failed",
|
||||||
|
"backup-update-failed": "Backup Update Failed",
|
||||||
|
"backup-delete-failed": "Backup Delete Failed",
|
||||||
|
"cookbook-create-failed": "Cookbook Create Failed",
|
||||||
|
"cookbook-update-failed": "Cookbook Update Failed",
|
||||||
|
"cookbook-delete-failed": "Cookbook Delete Failed",
|
||||||
|
"event-create-failed": "Event Create Failed",
|
||||||
|
"event-update-failed": "Event Update Failed",
|
||||||
|
"event-delete-failed": "Event Delete Failed",
|
||||||
|
"food-create-failed": "Food Create Failed",
|
||||||
|
"food-update-failed": "Food Update Failed",
|
||||||
|
"food-delete-failed": "Food Delete Failed",
|
||||||
|
"group-create-failed": "Group Create Failed",
|
||||||
|
"group-update-failed": "Group Update Failed",
|
||||||
|
"group-delete-failed": "Group Delete Failed",
|
||||||
|
"ingredient-create-failed": "Ingredient Create Failed",
|
||||||
|
"ingredient-update-failed": "Ingredient Update Failed",
|
||||||
|
"ingredient-delete-failed": "Ingredient Delete Failed",
|
||||||
|
"mealplan-create-failed": "Mealplan Create Failed",
|
||||||
|
"mealplan-update-failed": "Mealplan Update Failed",
|
||||||
|
"mealplan-delete-failed": "Mealplan Delete Failed",
|
||||||
|
"migration-create-failed": "Migration Create Failed",
|
||||||
|
"migration-update-failed": "Migration Update Failed",
|
||||||
|
"migration-delete-failed": "Migration Delete Failed",
|
||||||
|
"recipe-create-failed": "Recipe Create Failed",
|
||||||
|
"recipe-update-failed": "Recipe Update Failed",
|
||||||
|
"recipe-delete-failed": "Recipe Delete Failed",
|
||||||
|
"scraper-create-failed": "Scraper Create Failed",
|
||||||
|
"scraper-update-failed": "Scraper Update Failed",
|
||||||
|
"scraper-delete-failed": "Scraper Delete Failed",
|
||||||
|
"token-create-failed": "Token Create Failed",
|
||||||
|
"token-update-failed": "Token Update Failed",
|
||||||
|
"token-delete-failed": "Token Delete Failed",
|
||||||
|
"unit-create-failed": "Unit Create Failed",
|
||||||
|
"unit-update-failed": "Unit Update Failed",
|
||||||
|
"unit-delete-failed": "Unit Delete Failed",
|
||||||
|
"user-create-failed": "User Create Failed",
|
||||||
|
"user-update-failed": "User Update Failed",
|
||||||
|
"user-delete-failed": "User Delete Failed",
|
||||||
|
"webhook-create-failed": "Webhook Create Failed",
|
||||||
|
"webhook-update-failed": "Webhook Update Failed",
|
||||||
|
"webhook-delete-failed": "Webhook Delete Failed"
|
||||||
|
}
|
@ -3,12 +3,12 @@ from sqlalchemy.orm.session import Session
|
|||||||
|
|
||||||
from mealie.schema.user.user import PrivateUser
|
from mealie.schema.user.user import PrivateUser
|
||||||
|
|
||||||
from .dependencies import generate_session, get_current_user, is_logged_in
|
from .dependencies import generate_session, get_admin_user, get_current_user, is_logged_in
|
||||||
|
|
||||||
|
|
||||||
class ReadDeps:
|
class PublicDeps:
|
||||||
"""
|
"""
|
||||||
ReadDeps contains the common dependencies for all read operations through the API.
|
PublicDeps contains the common dependencies for all read operations through the API.
|
||||||
Note: The user object is used to definer what assets the user has access to.
|
Note: The user object is used to definer what assets the user has access to.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -28,9 +28,9 @@ class ReadDeps:
|
|||||||
self.user: bool = user
|
self.user: bool = user
|
||||||
|
|
||||||
|
|
||||||
class WriteDeps:
|
class UserDeps:
|
||||||
"""
|
"""
|
||||||
WriteDeps contains the common dependencies for all read operations through the API.
|
UserDeps contains the common dependencies for all read operations through the API.
|
||||||
Note: The user must be logged in or the route will return a 401 error.
|
Note: The user must be logged in or the route will return a 401 error.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -48,3 +48,15 @@ class WriteDeps:
|
|||||||
self.session: Session = session
|
self.session: Session = session
|
||||||
self.bg_task: BackgroundTasks = background_tasks
|
self.bg_task: BackgroundTasks = background_tasks
|
||||||
self.user: PrivateUser = user
|
self.user: PrivateUser = user
|
||||||
|
|
||||||
|
|
||||||
|
class AdminDeps:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
background_tasks: BackgroundTasks,
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
user=Depends(get_admin_user),
|
||||||
|
):
|
||||||
|
self.session: Session = session
|
||||||
|
self.bg_task: BackgroundTasks = background_tasks
|
||||||
|
self.user: PrivateUser = user
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Callable, Generic, TypeVar
|
from typing import Callable, Generic, Type, TypeVar
|
||||||
|
|
||||||
from fastapi import BackgroundTasks, Depends, HTTPException, status
|
from fastapi import BackgroundTasks, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
from mealie.core.config import get_app_dirs, get_settings
|
from mealie.core.config import get_app_dirs, get_settings
|
||||||
from mealie.core.dependencies.grouped import ReadDeps, WriteDeps
|
from mealie.core.dependencies.grouped import PublicDeps, UserDeps
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.db.database import get_database
|
from mealie.db.database import get_database
|
||||||
from mealie.db.db_setup import SessionLocal
|
from mealie.db.db_setup import SessionLocal
|
||||||
@ -17,25 +17,25 @@ T = TypeVar("T")
|
|||||||
D = TypeVar("D")
|
D = TypeVar("D")
|
||||||
|
|
||||||
|
|
||||||
|
CLS_DEP = TypeVar("CLS_DEP") # Generic Used for the class method dependencies
|
||||||
|
|
||||||
|
|
||||||
class BaseHttpService(Generic[T, D], ABC):
|
class BaseHttpService(Generic[T, D], ABC):
|
||||||
"""The BaseHttpService class is a generic class that can be used to create
|
"""
|
||||||
|
The BaseHttpService class is a generic class that can be used to create
|
||||||
http services that are injected via `Depends` into a route function. To use,
|
http services that are injected via `Depends` into a route function. To use,
|
||||||
you must define the Generic type arguments:
|
you must define the Generic type arguments:
|
||||||
|
|
||||||
`T`: The type passed into the *_existing functions (e.g. id) which is then passed into assert_existing
|
`T`: The type passed into the *_existing functions (e.g. id) which is then passed into assert_existing
|
||||||
`D`: Item returned from database layer
|
`D`: Item returned from database layer
|
||||||
|
|
||||||
Child Requirements:
|
|
||||||
Define the following functions:
|
|
||||||
`assert_existing(self, data: T) -> None:`
|
|
||||||
|
|
||||||
Define the following variables:
|
|
||||||
`event_func`: A function that is called when an event is created.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
item: D = None
|
item: D = None
|
||||||
|
|
||||||
# Function that Generate Corrsesponding Routes through RouterFactor
|
# Function that Generate Corrsesponding Routes through RouterFactory:
|
||||||
|
# if the method is defined or != `None` than the corresponding route is defined through the RouterFactory.
|
||||||
|
# If the method is not defined, then the route will be excluded from creation. This service based articheture
|
||||||
|
# is being adopted as apart of the v1 migration
|
||||||
get_all: Callable = None
|
get_all: Callable = None
|
||||||
create_one: Callable = None
|
create_one: Callable = None
|
||||||
update_one: Callable = None
|
update_one: Callable = None
|
||||||
@ -75,48 +75,35 @@ class BaseHttpService(Generic[T, D], ABC):
|
|||||||
self._group_id_cache = group.id
|
self._group_id_cache = group.id
|
||||||
return self._group_id_cache
|
return self._group_id_cache
|
||||||
|
|
||||||
@classmethod
|
def _existing_factory(dependency: Type[CLS_DEP]) -> classmethod:
|
||||||
def read_existing(cls, item_id: T, deps: ReadDeps = Depends()):
|
def cls_method(cls, item_id: T, deps: CLS_DEP = Depends(dependency)):
|
||||||
"""
|
|
||||||
Used for dependency injection for routes that require an existing recipe. If the recipe doesn't exist
|
|
||||||
or the user doens't not have the required permissions, the proper HTTP Status code will be raised.
|
|
||||||
"""
|
|
||||||
new_class = cls(deps.session, deps.user, deps.bg_task)
|
new_class = cls(deps.session, deps.user, deps.bg_task)
|
||||||
new_class.assert_existing(item_id)
|
new_class.assert_existing(item_id)
|
||||||
return new_class
|
return new_class
|
||||||
|
|
||||||
@classmethod
|
return classmethod(cls_method)
|
||||||
def write_existing(cls, item_id: T, deps: WriteDeps = Depends()):
|
|
||||||
"""
|
|
||||||
Used for dependency injection for routes that require an existing recipe. The only difference between
|
|
||||||
read_existing and write_existing is that the user is required to be logged in on write_existing method.
|
|
||||||
"""
|
|
||||||
new_class = cls(deps.session, deps.user, deps.bg_task)
|
|
||||||
new_class.assert_existing(item_id)
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
@classmethod
|
def _class_method_factory(dependency: Type[CLS_DEP]) -> classmethod:
|
||||||
def public(cls, deps: ReadDeps = Depends()):
|
def cls_method(cls, deps: CLS_DEP = Depends(dependency)):
|
||||||
"""
|
|
||||||
A Base instance to be used as a router dependency
|
|
||||||
"""
|
|
||||||
return cls(deps.session, deps.user, deps.bg_task)
|
return cls(deps.session, deps.user, deps.bg_task)
|
||||||
|
|
||||||
@classmethod
|
return classmethod(cls_method)
|
||||||
def private(cls, deps: WriteDeps = Depends()):
|
|
||||||
"""
|
|
||||||
A Base instance to be used as a router dependency
|
|
||||||
"""
|
|
||||||
return cls(deps.session, deps.user, deps.bg_task)
|
|
||||||
|
|
||||||
@abstractmethod
|
# TODO: Refactor to allow for configurable dependencies base on substantiation
|
||||||
def populate_item(self) -> None:
|
read_existing = _existing_factory(PublicDeps)
|
||||||
...
|
write_existing = _existing_factory(UserDeps)
|
||||||
|
|
||||||
|
public = _class_method_factory(PublicDeps)
|
||||||
|
private = _class_method_factory(UserDeps)
|
||||||
|
|
||||||
def assert_existing(self, id: T) -> None:
|
def assert_existing(self, id: T) -> None:
|
||||||
self.populate_item(id)
|
self.populate_item(id)
|
||||||
self._check_item()
|
self._check_item()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def populate_item(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
def _check_item(self) -> None:
|
def _check_item(self) -> None:
|
||||||
if not self.item:
|
if not self.item:
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
60
mealie/services/base_http_service/http_services.py
Normal file
60
mealie/services/base_http_service/http_services.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from abc import abstractmethod
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from mealie.core.dependencies.grouped import AdminDeps, PublicDeps, UserDeps
|
||||||
|
|
||||||
|
from .base_http_service import BaseHttpService
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
D = TypeVar("D")
|
||||||
|
|
||||||
|
|
||||||
|
class PublicHttpService(BaseHttpService[T, D]):
|
||||||
|
"""
|
||||||
|
PublicHttpService sets the class methods to PublicDeps for read actions
|
||||||
|
and UserDeps for write actions which are inaccessible to not logged in users.
|
||||||
|
"""
|
||||||
|
|
||||||
|
read_existing = BaseHttpService._existing_factory(PublicDeps)
|
||||||
|
write_existing = BaseHttpService._existing_factory(UserDeps)
|
||||||
|
|
||||||
|
public = BaseHttpService._class_method_factory(PublicDeps)
|
||||||
|
private = BaseHttpService._class_method_factory(UserDeps)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def populate_item(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class UserHttpService(BaseHttpService[T, D]):
|
||||||
|
"""
|
||||||
|
UserHttpService sets the class methods to UserDeps which are inaccessible
|
||||||
|
to not logged in users.
|
||||||
|
"""
|
||||||
|
|
||||||
|
read_existing = BaseHttpService._existing_factory(UserDeps)
|
||||||
|
write_existing = BaseHttpService._existing_factory(UserDeps)
|
||||||
|
|
||||||
|
public = BaseHttpService._class_method_factory(UserDeps)
|
||||||
|
private = BaseHttpService._class_method_factory(UserDeps)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def populate_item(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class AdminHttpService(BaseHttpService[T, D]):
|
||||||
|
"""
|
||||||
|
AdminHttpService restricts the class methods to AdminDeps which are restricts
|
||||||
|
all class methods to users who are administrators.
|
||||||
|
"""
|
||||||
|
|
||||||
|
read_existing = BaseHttpService._existing_factory(AdminDeps)
|
||||||
|
write_existing = BaseHttpService._existing_factory(AdminDeps)
|
||||||
|
|
||||||
|
public = BaseHttpService._class_method_factory(AdminDeps)
|
||||||
|
private = BaseHttpService._class_method_factory(AdminDeps)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def populate_item(self) -> None:
|
||||||
|
...
|
@ -1,3 +1,4 @@
|
|||||||
|
import inspect
|
||||||
from typing import Any, Callable, Optional, Sequence, Type, TypeVar
|
from typing import Any, Callable, Optional, Sequence, Type, TypeVar
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
@ -23,15 +24,7 @@ class RouterFactory(APIRouter):
|
|||||||
update_schema: Type[T]
|
update_schema: Type[T]
|
||||||
_base_path: str = "/"
|
_base_path: str = "/"
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, service: Type[S], prefix: Optional[str] = None, tags: Optional[list[str]] = None, *_, **kwargs):
|
||||||
self,
|
|
||||||
service: Type[S],
|
|
||||||
prefix: Optional[str] = None,
|
|
||||||
tags: Optional[list[str]] = None,
|
|
||||||
*args,
|
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
|
|
||||||
self.service: Type[S] = service
|
self.service: Type[S] = service
|
||||||
self.schema: Type[T] = service._schema
|
self.schema: Type[T] = service._schema
|
||||||
|
|
||||||
@ -57,6 +50,7 @@ class RouterFactory(APIRouter):
|
|||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
response_model=Optional[list[self.schema]], # type: ignore
|
response_model=Optional[list[self.schema]], # type: ignore
|
||||||
summary="Get All",
|
summary="Get All",
|
||||||
|
description=inspect.cleandoc(self.service.get_all.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.service.create_one:
|
if self.service.create_one:
|
||||||
@ -66,6 +60,7 @@ class RouterFactory(APIRouter):
|
|||||||
methods=["POST"],
|
methods=["POST"],
|
||||||
response_model=self.schema,
|
response_model=self.schema,
|
||||||
summary="Create One",
|
summary="Create One",
|
||||||
|
description=inspect.cleandoc(self.service.create_one.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.service.update_many:
|
if self.service.update_many:
|
||||||
@ -75,6 +70,7 @@ class RouterFactory(APIRouter):
|
|||||||
methods=["PUT"],
|
methods=["PUT"],
|
||||||
response_model=Optional[list[self.schema]], # type: ignore
|
response_model=Optional[list[self.schema]], # type: ignore
|
||||||
summary="Update Many",
|
summary="Update Many",
|
||||||
|
description=inspect.cleandoc(self.service.update_many.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.service.delete_all:
|
if self.service.delete_all:
|
||||||
@ -84,6 +80,7 @@ class RouterFactory(APIRouter):
|
|||||||
methods=["DELETE"],
|
methods=["DELETE"],
|
||||||
response_model=Optional[list[self.schema]], # type: ignore
|
response_model=Optional[list[self.schema]], # type: ignore
|
||||||
summary="Delete All",
|
summary="Delete All",
|
||||||
|
description=inspect.cleandoc(self.service.delete_all.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.service.populate_item:
|
if self.service.populate_item:
|
||||||
@ -93,6 +90,7 @@ class RouterFactory(APIRouter):
|
|||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
response_model=self.get_one_schema,
|
response_model=self.get_one_schema,
|
||||||
summary="Get One",
|
summary="Get One",
|
||||||
|
description=inspect.cleandoc(self.service.populate_item.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.service.update_one:
|
if self.service.update_one:
|
||||||
@ -102,15 +100,18 @@ class RouterFactory(APIRouter):
|
|||||||
methods=["PUT"],
|
methods=["PUT"],
|
||||||
response_model=self.schema,
|
response_model=self.schema,
|
||||||
summary="Update One",
|
summary="Update One",
|
||||||
|
description=inspect.cleandoc(self.service.update_one.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.service.delete_one:
|
if self.service.delete_one:
|
||||||
|
print(self.service.delete_one.__doc__)
|
||||||
self._add_api_route(
|
self._add_api_route(
|
||||||
"/{item_id}",
|
"/{item_id}",
|
||||||
self._delete_one(),
|
self._delete_one(),
|
||||||
methods=["DELETE"],
|
methods=["DELETE"],
|
||||||
response_model=self.schema,
|
response_model=self.schema,
|
||||||
summary="Delete One",
|
summary="Delete One",
|
||||||
|
description=inspect.cleandoc(self.service.delete_one.__doc__ or ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _add_api_route(self, path: str, endpoint: Callable[..., Any], **kwargs: Any) -> None:
|
def _add_api_route(self, path: str, endpoint: Callable[..., Any], **kwargs: Any) -> None:
|
||||||
|
@ -4,13 +4,13 @@ from fastapi import HTTPException, status
|
|||||||
|
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.schema.cookbook.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook
|
from mealie.schema.cookbook.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook
|
||||||
from mealie.services.base_http_service.base_http_service import BaseHttpService
|
from mealie.services.base_http_service.http_services import UserHttpService
|
||||||
from mealie.services.events import create_group_event
|
from mealie.services.events import create_group_event
|
||||||
|
|
||||||
logger = get_logger(module=__name__)
|
logger = get_logger(module=__name__)
|
||||||
|
|
||||||
|
|
||||||
class CookbookService(BaseHttpService[int, ReadCookBook]):
|
class CookbookService(UserHttpService[int, ReadCookBook]):
|
||||||
event_func = create_group_event
|
event_func = create_group_event
|
||||||
_restrict_by_group = True
|
_restrict_by_group = True
|
||||||
|
|
||||||
@ -19,17 +19,17 @@ class CookbookService(BaseHttpService[int, ReadCookBook]):
|
|||||||
_update_schema = UpdateCookBook
|
_update_schema = UpdateCookBook
|
||||||
_get_one_schema = RecipeCookBook
|
_get_one_schema = RecipeCookBook
|
||||||
|
|
||||||
def populate_item(self, id: int | str):
|
def populate_item(self, item_id: int | str):
|
||||||
try:
|
try:
|
||||||
id = int(id)
|
item_id = int(item_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if isinstance(id, int):
|
if isinstance(item_id, int):
|
||||||
self.item = self.db.cookbooks.get_one(self.session, id, override_schema=RecipeCookBook)
|
self.item = self.db.cookbooks.get_one(self.session, item_id, override_schema=RecipeCookBook)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.item = self.db.cookbooks.get_one(self.session, id, key="slug", override_schema=RecipeCookBook)
|
self.item = self.db.cookbooks.get_one(self.session, item_id, key="slug", override_schema=RecipeCookBook)
|
||||||
|
|
||||||
def get_all(self) -> list[ReadCookBook]:
|
def get_all(self) -> list[ReadCookBook]:
|
||||||
items = self.db.cookbooks.get(self.session, self.group_id, "group_id", limit=999)
|
items = self.db.cookbooks.get(self.session, self.group_id, "group_id", limit=999)
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
|
|
||||||
from mealie.core.dependencies.grouped import WriteDeps
|
from mealie.core.dependencies.grouped import UserDeps
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.schema.recipe.recipe_category import CategoryBase
|
from mealie.schema.recipe.recipe_category import CategoryBase
|
||||||
from mealie.schema.user.user import GroupInDB
|
from mealie.schema.user.user import GroupInDB
|
||||||
@ -18,12 +18,12 @@ class GroupSelfService(BaseHttpService[int, str]):
|
|||||||
item: GroupInDB
|
item: GroupInDB
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def read_existing(cls, deps: WriteDeps = Depends()):
|
def read_existing(cls, deps: UserDeps = Depends()):
|
||||||
"""Override parent method to remove `item_id` from arguments"""
|
"""Override parent method to remove `item_id` from arguments"""
|
||||||
return super().read_existing(item_id=0, deps=deps)
|
return super().read_existing(item_id=0, deps=deps)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write_existing(cls, deps: WriteDeps = Depends()):
|
def write_existing(cls, deps: UserDeps = Depends()):
|
||||||
"""Override parent method to remove `item_id` from arguments"""
|
"""Override parent method to remove `item_id` from arguments"""
|
||||||
return super().write_existing(item_id=0, deps=deps)
|
return super().write_existing(item_id=0, deps=deps)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from typing import Union
|
|||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from mealie.core.dependencies.grouped import ReadDeps, WriteDeps
|
from mealie.core.dependencies.grouped import PublicDeps, UserDeps
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.schema.recipe.recipe import CreateRecipe, Recipe
|
from mealie.schema.recipe.recipe import CreateRecipe, Recipe
|
||||||
from mealie.services.base_http_service.base_http_service import BaseHttpService
|
from mealie.services.base_http_service.base_http_service import BaseHttpService
|
||||||
@ -25,11 +25,11 @@ class RecipeService(BaseHttpService[str, Recipe]):
|
|||||||
event_func = create_recipe_event
|
event_func = create_recipe_event
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write_existing(cls, slug: str, deps: WriteDeps = Depends()):
|
def write_existing(cls, slug: str, deps: UserDeps = Depends()):
|
||||||
return super().write_existing(slug, deps)
|
return super().write_existing(slug, deps)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def read_existing(cls, slug: str, deps: ReadDeps = Depends()):
|
def read_existing(cls, slug: str, deps: PublicDeps = Depends()):
|
||||||
return super().write_existing(slug, deps)
|
return super().write_existing(slug, deps)
|
||||||
|
|
||||||
def assert_existing(self, slug: str):
|
def assert_existing(self, slug: str):
|
||||||
|
81
mealie/utils/error_messages.py
Normal file
81
mealie/utils/error_messages.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ErrorMessages:
|
||||||
|
"""
|
||||||
|
This enum class holds the text values that represent the errors returned when
|
||||||
|
something goes wrong on the server side.
|
||||||
|
|
||||||
|
Example: {"details": "general-failure"}
|
||||||
|
|
||||||
|
The items contained within the '#' are automatically generated by a script in the scripts directory.
|
||||||
|
DO NOT EDIT THE CONTENTS BETWEEN THOSE. If you need to add a custom error message, do so in the lines
|
||||||
|
above.
|
||||||
|
|
||||||
|
Why Generate This!?!?! If we generate static errors on the backend we can ensure that a constant
|
||||||
|
set or error messages will be returned to the frontend. As such we can use the "details" key to
|
||||||
|
look up localized messages in the frontend. as such DO NOT change the generated or manual codes
|
||||||
|
without making the necessary changes on the client side code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
general_failure = "general-failure"
|
||||||
|
|
||||||
|
# CODE_GEN_ID: ERROR_MESSAGE_ENUMS
|
||||||
|
backup_create_failure = "backup-create-failure"
|
||||||
|
backup_update_failure = "backup-update-failure"
|
||||||
|
backup_delete_failure = "backup-delete-failure"
|
||||||
|
|
||||||
|
cookbook_create_failure = "cookbook-create-failure"
|
||||||
|
cookbook_update_failure = "cookbook-update-failure"
|
||||||
|
cookbook_delete_failure = "cookbook-delete-failure"
|
||||||
|
|
||||||
|
event_create_failure = "event-create-failure"
|
||||||
|
event_update_failure = "event-update-failure"
|
||||||
|
event_delete_failure = "event-delete-failure"
|
||||||
|
|
||||||
|
food_create_failure = "food-create-failure"
|
||||||
|
food_update_failure = "food-update-failure"
|
||||||
|
food_delete_failure = "food-delete-failure"
|
||||||
|
|
||||||
|
group_create_failure = "group-create-failure"
|
||||||
|
group_update_failure = "group-update-failure"
|
||||||
|
group_delete_failure = "group-delete-failure"
|
||||||
|
|
||||||
|
ingredient_create_failure = "ingredient-create-failure"
|
||||||
|
ingredient_update_failure = "ingredient-update-failure"
|
||||||
|
ingredient_delete_failure = "ingredient-delete-failure"
|
||||||
|
|
||||||
|
mealplan_create_failure = "mealplan-create-failure"
|
||||||
|
mealplan_update_failure = "mealplan-update-failure"
|
||||||
|
mealplan_delete_failure = "mealplan-delete-failure"
|
||||||
|
|
||||||
|
migration_create_failure = "migration-create-failure"
|
||||||
|
migration_update_failure = "migration-update-failure"
|
||||||
|
migration_delete_failure = "migration-delete-failure"
|
||||||
|
|
||||||
|
recipe_create_failure = "recipe-create-failure"
|
||||||
|
recipe_update_failure = "recipe-update-failure"
|
||||||
|
recipe_delete_failure = "recipe-delete-failure"
|
||||||
|
|
||||||
|
scraper_create_failure = "scraper-create-failure"
|
||||||
|
scraper_update_failure = "scraper-update-failure"
|
||||||
|
scraper_delete_failure = "scraper-delete-failure"
|
||||||
|
|
||||||
|
token_create_failure = "token-create-failure"
|
||||||
|
token_update_failure = "token-update-failure"
|
||||||
|
token_delete_failure = "token-delete-failure"
|
||||||
|
|
||||||
|
unit_create_failure = "unit-create-failure"
|
||||||
|
unit_update_failure = "unit-update-failure"
|
||||||
|
unit_delete_failure = "unit-delete-failure"
|
||||||
|
|
||||||
|
user_create_failure = "user-create-failure"
|
||||||
|
user_update_failure = "user-update-failure"
|
||||||
|
user_delete_failure = "user-delete-failure"
|
||||||
|
|
||||||
|
webhook_create_failure = "webhook-create-failure"
|
||||||
|
webhook_update_failure = "webhook-update-failure"
|
||||||
|
webhook_delete_failure = "webhook-delete-failure"
|
||||||
|
|
||||||
|
# END ERROR_MESSAGE_ENUMS
|
Loading…
x
Reference in New Issue
Block a user