refactor(backend): ♻️ refactor backend services (#669)

* refactor(backend): ♻️ refactor backend services

* refactor(backend): ♻️ move user model folder into own directory for future expansion

* fix overriding results

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-09-04 12:28:49 -08:00 committed by GitHub
parent b550dae593
commit 3d87ffc3a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 188 additions and 181 deletions

View File

@ -96,8 +96,8 @@ class BaseAccessModel(Generic[T, D]):
if any_case:
search_attr = getattr(self.sql_model, key)
result = session.query(self.sql_model).filter(func.lower(search_attr) == key.lower()).one_or_none()
result = session.query(self.sql_model).filter_by(**{key: value}).one_or_none()
else:
result = session.query(self.sql_model).filter_by(**{key: value}).one_or_none()
if not result:
return

View File

@ -3,9 +3,10 @@ from logging import getLogger
from sqlalchemy.orm.session import Session
from mealie.db.data_access_layer.group_access_model import GroupDataAccessModel
from mealie.db.models.cookbook import CookBook
from mealie.db.models.event import Event, EventNotification
from mealie.db.models.group import Group
from mealie.db.models.group.cookbook import CookBook
from mealie.db.models.group.shopping_list import ShoppingList
from mealie.db.models.group.webhooks import GroupWebhooksModel
from mealie.db.models.mealplan import MealPlan
from mealie.db.models.recipe.category import Category
@ -13,7 +14,6 @@ from mealie.db.models.recipe.comment import RecipeComment
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
from mealie.db.models.recipe.recipe import RecipeModel, Tag
from mealie.db.models.settings import SiteSettings
from mealie.db.models.shopping_list import ShoppingList
from mealie.db.models.sign_up import SignUp
from mealie.db.models.users import LongLiveToken, User
from mealie.schema.admin import SiteSettings as SiteSettingsSchema
@ -39,7 +39,9 @@ from .user_access_model import UserDataAccessModel
logger = getLogger()
DEFAULT_PK = "id"
pk_id = "id"
pk_slug = "slug"
pk_token = "token"
class CategoryDataAccessModel(BaseAccessModel):
@ -53,38 +55,37 @@ class TagsDataAccessModel(BaseAccessModel):
class DatabaseAccessLayer:
"""
`DatabaseAccessLayer` class is the data access layer for all database actions within
Mealie. Database uses composition from classes derived from BaseAccessModel. These
can be substantiated from the BaseAccessModel class or through inheritance when
additional methods are required.
"""
def __init__(self) -> None:
"""
`DatabaseAccessLayer` class is the data access layer for all database actions within
Mealie. Database uses composition from classes derived from BaseAccessModel. These
can be substantiated from the BaseAccessModel class or through inheritance when
additional methods are required.
"""
# Recipes
self.recipes = RecipeDataAccessModel("slug", RecipeModel, Recipe)
self.ingredient_foods = BaseAccessModel(DEFAULT_PK, IngredientFoodModel, IngredientFood)
self.ingredient_units = BaseAccessModel(DEFAULT_PK, IngredientUnitModel, IngredientUnit)
self.comments = BaseAccessModel(DEFAULT_PK, RecipeComment, CommentOut)
self.recipes = RecipeDataAccessModel(pk_slug, RecipeModel, Recipe)
self.ingredient_foods = BaseAccessModel(pk_id, IngredientFoodModel, IngredientFood)
self.ingredient_units = BaseAccessModel(pk_id, IngredientUnitModel, IngredientUnit)
self.comments = BaseAccessModel(pk_id, RecipeComment, CommentOut)
# Tags and Categories
self.categories = CategoryDataAccessModel("slug", Category, RecipeCategoryResponse)
self.tags = TagsDataAccessModel("slug", Tag, RecipeTagResponse)
self.categories = CategoryDataAccessModel(pk_slug, Category, RecipeCategoryResponse)
self.tags = TagsDataAccessModel(pk_slug, Tag, RecipeTagResponse)
# Site
self.settings = BaseAccessModel(DEFAULT_PK, SiteSettings, SiteSettingsSchema)
self.sign_ups = BaseAccessModel("token", SignUp, SignUpOut)
self.event_notifications = BaseAccessModel(DEFAULT_PK, EventNotification, EventNotificationIn)
self.events = BaseAccessModel(DEFAULT_PK, Event, EventSchema)
self.settings = BaseAccessModel(pk_id, SiteSettings, SiteSettingsSchema)
self.sign_ups = BaseAccessModel(pk_token, SignUp, SignUpOut)
self.event_notifications = BaseAccessModel(pk_id, EventNotification, EventNotificationIn)
self.events = BaseAccessModel(pk_id, Event, EventSchema)
# Users
self.users = UserDataAccessModel(DEFAULT_PK, User, PrivateUser)
self.api_tokens = BaseAccessModel(DEFAULT_PK, LongLiveToken, LongLiveTokenInDB)
self.users = UserDataAccessModel(pk_id, User, PrivateUser)
self.api_tokens = BaseAccessModel(pk_id, LongLiveToken, LongLiveTokenInDB)
# Group Data
self.groups = GroupDataAccessModel(DEFAULT_PK, Group, GroupInDB)
self.meals = BaseAccessModel(DEFAULT_PK, MealPlan, MealPlanOut)
self.webhooks = BaseAccessModel(DEFAULT_PK, GroupWebhooksModel, ReadWebhook)
self.shopping_lists = BaseAccessModel(DEFAULT_PK, ShoppingList, ShoppingListOut)
self.cookbooks = BaseAccessModel(DEFAULT_PK, CookBook, ReadCookBook)
self.groups = GroupDataAccessModel(pk_id, Group, GroupInDB)
self.meals = BaseAccessModel(pk_id, MealPlan, MealPlanOut)
self.webhooks = BaseAccessModel(pk_id, GroupWebhooksModel, ReadWebhook)
self.shopping_lists = BaseAccessModel(pk_id, ShoppingList, ShoppingListOut)
self.cookbooks = BaseAccessModel(pk_id, CookBook, ReadCookBook)

View File

@ -3,6 +3,5 @@ from .group import *
from .mealplan import *
from .recipe.recipe import *
from .settings import *
from .shopping_list import *
from .sign_up import *
from .users import *

View File

@ -1,4 +1,3 @@
import uuid
from datetime import datetime
from sqlalchemy import Column, DateTime, Integer
@ -7,22 +6,11 @@ from sqlalchemy.orm import declarative_base
from sqlalchemy.orm.session import Session
def get_uuid_as_hex() -> str:
"""
Generate a UUID as a hex string.
:return: UUID as a hex string.
"""
return uuid.uuid4().hex
@as_declarative()
class Base:
id = Column(Integer, primary_key=True)
created_at = Column(DateTime, default=datetime.now())
# @declared_attr
# def __tablename__(cls):
# return cls.__name__.lower()
update_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now())
class BaseMixins:

View File

@ -2,6 +2,8 @@ from sqlalchemy import Boolean, Column, DateTime, Integer, String
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from ._model_utils import auto_init
class EventNotification(SqlAlchemyBase, BaseMixins):
__tablename__ = "event_notifications"
@ -19,19 +21,9 @@ class EventNotification(SqlAlchemyBase, BaseMixins):
group = Column(Boolean, default=False)
user = Column(Boolean, default=False)
def __init__(
self, name, notification_url, type, general, recipe, backup, scheduled, migration, group, user, **_
) -> None:
self.name = name
self.notification_url = notification_url
self.type = type
self.general = general
self.recipe = recipe
self.backup = backup
self.scheduled = scheduled
self.migration = migration
self.group = group
self.user = user
@auto_init()
def __init__(self, **_) -> None:
pass
class Event(SqlAlchemyBase, BaseMixins):
@ -42,8 +34,6 @@ class Event(SqlAlchemyBase, BaseMixins):
time_stamp = Column(DateTime)
category = Column(String)
def __init__(self, title, text, time_stamp, category, **_) -> None:
self.title = title
self.text = text
self.time_stamp = time_stamp
self.category = category
@auto_init()
def __init__(self, **_) -> None:
pass

View File

@ -1 +1,3 @@
from .group import *
from .shopping_list import *
from .webhooks import *

View File

@ -1,8 +1,8 @@
from sqlalchemy import Column, ForeignKey, Integer, String, orm
from ._model_base import BaseMixins, SqlAlchemyBase
from ._model_utils import auto_init
from .recipe.category import Category, cookbooks_to_categories
from .._model_base import BaseMixins, SqlAlchemyBase
from .._model_utils import auto_init
from ..recipe.category import Category, cookbooks_to_categories
class CookBook(SqlAlchemyBase, BaseMixins):

View File

@ -3,12 +3,12 @@ import sqlalchemy.orm as orm
from sqlalchemy.orm.session import Session
from mealie.core.config import settings
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from mealie.db.models.cookbook import CookBook
from mealie.db.models.group.webhooks import GroupWebhooksModel
from mealie.db.models.recipe.category import Category, group2categories
from .._model_base import BaseMixins, SqlAlchemyBase
from .._model_utils import auto_init
from ..group.webhooks import GroupWebhooksModel
from ..recipe.category import Category, group2categories
from .cookbook import CookBook
class Group(SqlAlchemyBase, BaseMixins):

View File

@ -3,8 +3,8 @@ from requests import Session
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.ext.orderinglist import ordering_list
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from mealie.db.models.group import Group
from .._model_base import BaseMixins, SqlAlchemyBase
from .group import Group
class ShoppingListItem(SqlAlchemyBase, BaseMixins):

View File

@ -5,7 +5,8 @@ from sqlalchemy.ext.orderinglist import ordering_list
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from mealie.db.models.group import Group
from mealie.db.models.recipe.recipe import RecipeModel
from mealie.db.models.shopping_list import ShoppingList
from .group.shopping_list import ShoppingList
class Meal(SqlAlchemyBase):

View File

@ -2,6 +2,8 @@ from sqlalchemy import Boolean, Column, Integer, String
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from ._model_utils import auto_init
class SignUp(SqlAlchemyBase, BaseMixins):
__tablename__ = "sign_ups"
@ -10,13 +12,6 @@ class SignUp(SqlAlchemyBase, BaseMixins):
name = Column(String, index=True)
admin = Column(Boolean, default=False)
def __init__(
self,
session,
token,
name,
admin,
) -> None:
self.token = token
self.name = name
self.admin = admin
@auto_init()
def __init__(self, **_) -> None:
pass

View File

@ -0,0 +1 @@
from .users import *

View File

@ -27,9 +27,11 @@ class User(SqlAlchemyBase, BaseMixins):
username = Column(String, index=True, unique=True)
email = Column(String, unique=True, index=True)
password = Column(String)
admin = Column(Boolean, default=False)
group_id = Column(Integer, ForeignKey("groups.id"))
group = orm.relationship("Group", back_populates="users")
admin = Column(Boolean, default=False)
tokens: list[LongLiveToken] = orm.relationship(
LongLiveToken, back_populates="user", cascade="all, delete, delete-orphan", single_parent=True
)

View File

@ -3,7 +3,7 @@ from typing import Optional
from fastapi_camelcase import CamelModel
from pydantic.utils import GetterDict
from mealie.db.models.shopping_list import ShoppingList
from mealie.db.models.group.shopping_list import ShoppingList
class ListItem(CamelModel):

View File

@ -1,3 +1,2 @@
from .base_http_service import *
from .base_service import *
from .http_services import *
from .router_factory import *

View File

@ -1,12 +1,12 @@
from abc import ABC, abstractmethod
from typing import Callable, Generic, Type, TypeVar
from typing import Any, Callable, Generic, Type, TypeVar
from fastapi import BackgroundTasks, Depends, HTTPException, status
from sqlalchemy.orm.session import Session
from mealie.core.config import get_app_dirs, get_settings
from mealie.core.dependencies.grouped import PublicDeps, UserDeps
from mealie.core.root_logger import get_logger
from mealie.db.data_access_layer.db_access import DatabaseAccessLayer
from mealie.db.database import get_database
from mealie.db.db_setup import SessionLocal
from mealie.schema.user.user import PrivateUser
@ -44,10 +44,10 @@ class BaseHttpService(Generic[T, D], ABC):
delete_one: Callable = None
delete_all: Callable = None
db_access: DatabaseAccessLayer = None
# Type Definitions
_schema = None
_create_schema = None
_update_schema = None
# Function called to create a server side event
event_func: Callable = None
@ -67,14 +67,6 @@ class BaseHttpService(Generic[T, D], ABC):
self.app_dirs = get_app_dirs()
self.settings = get_settings()
@property
def group_id(self):
# TODO: Populate Group in Private User Call WARNING: May require significant refactoring
if not self._group_id_cache:
group = self.db.groups.get(self.session, self.user.group, "name")
self._group_id_cache = group.id
return self._group_id_cache
def _existing_factory(dependency: Type[CLS_DEP]) -> classmethod:
def cls_method(cls, item_id: T, deps: CLS_DEP = Depends(dependency)):
new_class = cls(deps.session, deps.user, deps.bg_task)
@ -89,21 +81,42 @@ class BaseHttpService(Generic[T, D], ABC):
return classmethod(cls_method)
# TODO: Refactor to allow for configurable dependencies base on substantiation
read_existing = _existing_factory(PublicDeps)
write_existing = _existing_factory(UserDeps)
@classmethod
@abstractmethod
def public(cls, deps: Any):
pass
public = _class_method_factory(PublicDeps)
private = _class_method_factory(UserDeps)
@classmethod
@abstractmethod
def private(cls, deps: Any):
pass
@classmethod
@abstractmethod
def read_existing(cls, deps: Any):
pass
@classmethod
@abstractmethod
def write_existing(cls, deps: Any):
pass
@abstractmethod
def populate_item(self) -> None:
pass
@property
def group_id(self):
# TODO: Populate Group in Private User Call WARNING: May require significant refactoring
if not self._group_id_cache:
group = self.db.groups.get(self.session, self.user.group, "name")
self._group_id_cache = group.id
return self._group_id_cache
def assert_existing(self, id: T) -> None:
self.populate_item(id)
self._check_item()
@abstractmethod
def populate_item(self) -> None:
...
def _check_item(self) -> None:
if not self.item:
raise HTTPException(status.HTTP_404_NOT_FOUND)
@ -114,8 +127,38 @@ class BaseHttpService(Generic[T, D], ABC):
if not group_id or group_id != self.group_id:
raise HTTPException(status.HTTP_403_FORBIDDEN)
if hasattr(self, "check_item"):
self.check_item()
def _create_event(self, title: str, message: str) -> None:
if not self.__class__.event_func:
raise NotImplementedError("`event_func` must be set by child class")
self.background_tasks.add_task(self.__class__.event_func, title, message, self.session)
# Generic CRUD Functions
def _create_one(self, data: Any, exception_msg="generic-create-error") -> D:
try:
self.item = self.db_access.create(self.session, data)
except Exception as ex:
logger.exception(ex)
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail={"message": exception_msg, "exception": str(ex)})
return self.item
def _update_one(self, data: Any, id: int = None) -> D:
if not self.item:
return
target_id = id or self.item.id
self.item = self.db_access.update(self.session, target_id, data)
return self.item
def _delete_one(self, id: int = None) -> D:
if not self.item:
return
target_id = id or self.item.id
self.item = self.db_access.delete(self.session, target_id)
return self.item

View File

@ -1,17 +0,0 @@
from mealie.core.config import get_app_dirs, get_settings
from mealie.core.root_logger import get_logger
from mealie.db.database import get_database
from mealie.db.db_setup import generate_session
logger = get_logger()
class BaseService:
def __init__(self) -> None:
# Static Globals Dependency Injection
self.db = get_database()
self.app_dirs = get_app_dirs()
self.settings = get_settings()
def session_context(self):
return generate_session()

View File

@ -1,5 +1,5 @@
import inspect
from typing import Any, Callable, Optional, Sequence, Type, TypeVar
from typing import Any, Callable, Optional, Sequence, Type, TypeVar, get_type_hints
from fastapi import APIRouter
from fastapi.params import Depends
@ -8,7 +8,7 @@ from pydantic import BaseModel
from .base_http_service import BaseHttpService
""""
"""
This code is largely based off of the FastAPI Crud Router
https://github.com/awtkns/fastapi-crudrouter/blob/master/fastapi_crudrouter/core/_base.py
"""
@ -18,25 +18,40 @@ S = TypeVar("S", bound=BaseHttpService)
DEPENDENCIES = Optional[Sequence[Depends]]
def get_return(func: Callable, default) -> Type:
return get_type_hints(func).get("return", default)
def get_func_args(func: Callable) -> Sequence[str]:
for _, value in get_type_hints(func).items():
if value:
return value
else:
return None
class RouterFactory(APIRouter):
schema: Type[T]
create_schema: Type[T]
update_schema: Type[T]
_base_path: str = "/"
def __init__(self, service: Type[S], prefix: Optional[str] = None, tags: Optional[list[str]] = None, *_, **kwargs):
"""
RouterFactory takes a concrete service class derived from the BaseHttpService class and returns common
CRUD Routes for the service. The following features are implmeneted in the RouterFactory:
1. API endpoint Descriptions are read from the docstrings of the methods in the passed in service class
2. Return types are inferred from the concrete service schema, or specified from the return type annotations.
This provides flexibility to return different types based on each route depending on client needs.
3. Arguemnt types are inferred for Post and Put routes where the first type annotated argument is the data that
is beging posted or updated. Note that this is only done for the first argument of the method.
4. The Get and Delete routes assume that you've defined the `write_existing` and `read_existing` methods in the
service class. The dependencies defined in the `write_existing` and `read_existing` methods are passed directly
to the FastAPI router and as such should include the `item_id` or equilivent argument.
"""
self.service: Type[S] = service
self.schema: Type[T] = service._schema
# HACK: Special Case for Coobooks, not sure this is a good way to handle the abstraction :/
if hasattr(self.service, "_get_one_schema"):
self.get_one_schema = self.service._get_one_schema
else:
self.get_one_schema = self.schema
self.update_schema: Type[T] = service._update_schema
self.create_schema: Type[T] = service._create_schema
prefix = str(prefix or self.schema.__name__).lower()
prefix = self._base_path + prefix.strip("/")
tags = tags or [prefix.strip("/").capitalize()]
@ -88,7 +103,7 @@ class RouterFactory(APIRouter):
"/{item_id}",
self._get_one(),
methods=["GET"],
response_model=self.get_one_schema,
response_model=get_type_hints(self.service.populate_item).get("return", self.schema),
summary="Get One",
description=inspect.cleandoc(self.service.populate_item.__doc__ or ""),
)
@ -104,7 +119,6 @@ class RouterFactory(APIRouter):
)
if self.service.delete_one:
print(self.service.delete_one.__doc__)
self._add_api_route(
"/{item_id}",
self._delete_one(),
@ -160,19 +174,25 @@ class RouterFactory(APIRouter):
return route
def _create(self, *args: Any, **kwargs: Any) -> Callable[..., Any]:
def route(data: self.create_schema, service: S = Depends(self.service.private)) -> T: # type: ignore
create_schema = get_func_args(self.service.create_one) or self.schema
def route(data: create_schema, service: S = Depends(self.service.private)) -> T: # type: ignore
return service.create_one(data)
return route
def _update(self, *args: Any, **kwargs: Any) -> Callable[..., Any]:
def route(data: self.update_schema, service: S = Depends(self.service.write_existing)) -> T: # type: ignore
update_schema = get_func_args(self.service.update_one) or self.schema
def route(data: update_schema, service: S = Depends(self.service.write_existing)) -> T: # type: ignore
return service.update_one(data)
return route
def _update_many(self, *args: Any, **kwargs: Any) -> Callable[..., Any]:
def route(data: list[self.update_schema], service: S = Depends(self.service.write_existing)) -> T: # type: ignore
update_many_schema = get_func_args(self.service.update_many) or list[self.schema]
def route(data: update_many_schema, service: S = Depends(self.service.private)) -> T: # type: ignore
return service.update_many(data)
return route

View File

@ -1,11 +1,11 @@
from __future__ import annotations
from fastapi import HTTPException, status
from mealie.core.root_logger import get_logger
from mealie.db.database import get_database
from mealie.schema.cookbook.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook
from mealie.services.base_http_service.http_services import UserHttpService
from mealie.services.events import create_group_event
from mealie.utils.error_messages import ErrorMessages
logger = get_logger(module=__name__)
@ -15,11 +15,10 @@ class CookbookService(UserHttpService[int, ReadCookBook]):
_restrict_by_group = True
_schema = ReadCookBook
_create_schema = CreateCookBook
_update_schema = UpdateCookBook
_get_one_schema = RecipeCookBook
def populate_item(self, item_id: int | str):
db_access = get_database().cookbooks
def populate_item(self, item_id: int) -> RecipeCookBook:
try:
item_id = int(item_id)
except Exception:
@ -37,25 +36,13 @@ class CookbookService(UserHttpService[int, ReadCookBook]):
return items
def create_one(self, data: CreateCookBook) -> ReadCookBook:
try:
self.item = self.db.cookbooks.create(self.session, SaveCookBook(group_id=self.group_id, **data.dict()))
except Exception as ex:
raise HTTPException(
status.HTTP_400_BAD_REQUEST, detail={"message": "PAGE_CREATION_ERROR", "exception": str(ex)}
)
data = SaveCookBook(group_id=self.group_id, **data.dict())
return self._create_one(data, ErrorMessages.cookbook_create_failure)
return self.item
def update_one(self, data: UpdateCookBook, id: int = None) -> ReadCookBook:
return self._update_one(data, id)
def update_one(self, data: CreateCookBook, id: int = None) -> ReadCookBook:
if not self.item:
return
target_id = id or self.item.id
self.item = self.db.cookbooks.update(self.session, target_id, data)
return self.item
def update_many(self, data: list[ReadCookBook]) -> list[ReadCookBook]:
def update_many(self, data: list[UpdateCookBook]) -> list[ReadCookBook]:
updated = []
for cookbook in data:
@ -65,10 +52,4 @@ class CookbookService(UserHttpService[int, ReadCookBook]):
return updated
def delete_one(self, id: int = None) -> ReadCookBook:
if not self.item:
return
target_id = id or self.item.id
self.item = self.db.cookbooks.delete(self.session, target_id)
return self.item
return self._delete_one(id)

View File

@ -6,13 +6,13 @@ from mealie.core.dependencies.grouped import UserDeps
from mealie.core.root_logger import get_logger
from mealie.schema.recipe.recipe_category import CategoryBase
from mealie.schema.user.user import GroupInDB
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
logger = get_logger(module=__name__)
class GroupSelfService(BaseHttpService[int, str]):
class GroupSelfService(UserHttpService[int, str]):
_restrict_by_group = True
event_func = create_group_event
item: GroupInDB
@ -36,8 +36,9 @@ class GroupSelfService(BaseHttpService[int, str]):
if self.item.id != self.group_id:
raise HTTPException(status.HTTP_403_FORBIDDEN)
def populate_item(self, _: str = None):
def populate_item(self, _: str = None) -> GroupInDB:
self.item = self.db.groups.get(self.session, self.group_id)
return self.item
def update_categories(self, new_categories: list[CategoryBase]):
if not self.item:

View File

@ -5,13 +5,13 @@ from fastapi import HTTPException, status
from mealie.core.root_logger import get_logger
from mealie.schema.group import ReadWebhook
from mealie.schema.group.webhook import CreateWebhook, SaveWebhook
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
logger = get_logger(module=__name__)
class WebhookService(BaseHttpService[int, ReadWebhook]):
class WebhookService(UserHttpService[int, ReadWebhook]):
event_func = create_group_event
_restrict_by_group = True
@ -19,8 +19,9 @@ class WebhookService(BaseHttpService[int, ReadWebhook]):
_create_schema = CreateWebhook
_update_schema = CreateWebhook
def populate_item(self, id: int | str):
def populate_item(self, id: int) -> ReadWebhook:
self.item = self.db.webhooks.get_one(self.session, id)
return self.item
def get_all(self) -> list[ReadWebhook]:
return self.db.webhooks.get(self.session, self.group_id, match_key="group_id", limit=9999)

View File

@ -8,13 +8,13 @@ from sqlalchemy.exc import IntegrityError
from mealie.core.dependencies.grouped import PublicDeps, UserDeps
from mealie.core.root_logger import get_logger
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.http_services import PublicHttpService
from mealie.services.events import create_recipe_event
logger = get_logger(module=__name__)
class RecipeService(BaseHttpService[str, Recipe]):
class RecipeService(PublicHttpService[str, Recipe]):
"""
Class Methods:
`read_existing`: Reads an existing recipe from the database.

View File

@ -3,13 +3,13 @@ from fastapi import HTTPException, status
from mealie.core.root_logger import get_logger
from mealie.core.security import hash_password, verify_password
from mealie.schema.user.user import ChangePassword, PrivateUser
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_user_event
logger = get_logger(module=__name__)
class UserService(BaseHttpService[int, str]):
class UserService(UserHttpService[int, str]):
event_func = create_user_event
acting_user: PrivateUser = None