mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-27 20:55:12 -05:00
Feature/group based notifications (#918)
* fix group page * setup group notification for backend * update type generators * script to auto-generate schema exports * setup frontend CRUD interface * remove old notifications UI * drop old events api * add test functionality * update naming for fields * add event dispatcher functionality * bump to python 3.10 * bump python version * purge old event code * use-async apprise * set mealie logo as image * unify styles for buttons rows * add links to banners
This commit is contained in:
@@ -76,6 +76,17 @@ class CrudMixins:
|
||||
|
||||
return item
|
||||
|
||||
def get_one(self, item_id):
|
||||
item = self.repo.get(item_id)
|
||||
|
||||
if not item:
|
||||
raise HTTPException(
|
||||
status.HTTP_404_NOT_FOUND,
|
||||
detail=ErrorResponse.respond(message="Not found."),
|
||||
)
|
||||
|
||||
return item
|
||||
|
||||
def update_one(self, data, item_id):
|
||||
item = self.repo.get(item_id)
|
||||
|
||||
@@ -98,11 +109,11 @@ class CrudMixins:
|
||||
self.handle_exception(ex)
|
||||
|
||||
def delete_one(self, item_id):
|
||||
item = self.repo.get(item_id)
|
||||
self.logger.info(f"Deleting item with id {item}")
|
||||
self.logger.info(f"Deleting item with id {item_id}")
|
||||
|
||||
try:
|
||||
item = self.repo.delete(item)
|
||||
item = self.repo.delete(item_id)
|
||||
self.logger.info(item)
|
||||
except Exception as ex:
|
||||
self.handle_exception(ex)
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import events, notifications
|
||||
from . import events
|
||||
|
||||
about_router = APIRouter(prefix="/api/about")
|
||||
|
||||
about_router.include_router(events.router, tags=["Events: CRUD"])
|
||||
about_router.include_router(notifications.router, tags=["Events: Notifications"])
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
from http.client import HTTPException
|
||||
|
||||
from fastapi import Depends, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.schema.events import EventNotificationIn, EventNotificationOut, TestEvent
|
||||
from mealie.services.events import test_notification
|
||||
|
||||
router = AdminAPIRouter()
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
@router.post("/notifications")
|
||||
async def create_event_notification(
|
||||
event_data: EventNotificationIn,
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
"""Create event_notification in the Database"""
|
||||
db = get_repositories(session)
|
||||
|
||||
return db.event_notifications.create(event_data)
|
||||
|
||||
|
||||
@router.post("/notifications/test")
|
||||
async def test_notification_route(
|
||||
test_data: TestEvent,
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
"""Create event_notification in the Database"""
|
||||
db = get_repositories(session)
|
||||
|
||||
if test_data.id:
|
||||
event_obj: EventNotificationIn = db.event_notifications.get(test_data.id)
|
||||
test_data.test_url = event_obj.notification_url
|
||||
|
||||
try:
|
||||
test_notification(test_data.test_url)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
@router.get("/notifications", response_model=list[EventNotificationOut])
|
||||
async def get_all_event_notification(session: Session = Depends(generate_session)):
|
||||
"""Get all event_notification from the Database"""
|
||||
db = get_repositories(session)
|
||||
return db.event_notifications.get_all(override_schema=EventNotificationOut)
|
||||
|
||||
|
||||
@router.put("/notifications/{id}")
|
||||
async def update_event_notification(id: int, session: Session = Depends(generate_session)):
|
||||
"""Update event_notification in the Database"""
|
||||
# not yet implemented
|
||||
raise HTTPException(status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||
|
||||
|
||||
@router.delete("/notifications/{id}")
|
||||
async def delete_event_notification(id: int, session: Session = Depends(generate_session)):
|
||||
"""Delete event_notification from the Database"""
|
||||
# Delete Item
|
||||
db = get_repositories(session)
|
||||
return db.event_notifications.delete(id)
|
||||
@@ -8,7 +8,7 @@ from mealie.services.group_services import CookbookService, WebhookService
|
||||
from mealie.services.group_services.meal_service import MealService
|
||||
from mealie.services.group_services.reports_service import GroupReportService
|
||||
|
||||
from . import categories, invitations, labels, migrations, preferences, self_service, shopping_lists
|
||||
from . import categories, invitations, labels, migrations, notifications, preferences, self_service, shopping_lists
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -56,3 +56,4 @@ def get_all_reports(
|
||||
router.include_router(report_router)
|
||||
router.include_router(shopping_lists.router)
|
||||
router.include_router(labels.router)
|
||||
router.include_router(notifications.router)
|
||||
|
||||
85
mealie/routes/groups/notifications.py
Normal file
85
mealie/routes/groups/notifications.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from functools import cached_property
|
||||
from sqlite3 import IntegrityError
|
||||
from typing import Type
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.dependencies import SharedDependencies
|
||||
from mealie.routes._base.mixins import CrudMixins
|
||||
from mealie.schema.group.group_events import (
|
||||
GroupEventNotifierCreate,
|
||||
GroupEventNotifierOut,
|
||||
GroupEventNotifierPrivate,
|
||||
GroupEventNotifierSave,
|
||||
GroupEventNotifierUpdate,
|
||||
)
|
||||
from mealie.schema.mapper import cast
|
||||
from mealie.schema.query import GetAll
|
||||
from mealie.services.event_bus_service.event_bus_service import EventBusService
|
||||
|
||||
router = APIRouter(prefix="/groups/events/notifications", tags=["Group: Event Notifications"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupEventsNotifierController:
|
||||
deps: SharedDependencies = Depends(SharedDependencies.user)
|
||||
event_bus: EventBusService = Depends(EventBusService)
|
||||
|
||||
@cached_property
|
||||
def repo(self):
|
||||
if not self.deps.acting_user:
|
||||
raise Exception("No user is logged in.")
|
||||
|
||||
return self.deps.repos.group_event_notifier.by_group(self.deps.acting_user.group_id)
|
||||
|
||||
def registered_exceptions(self, ex: Type[Exception]) -> str:
|
||||
registered = {
|
||||
Exception: "An unexpected error occurred.",
|
||||
IntegrityError: "An unexpected error occurred.",
|
||||
}
|
||||
|
||||
return registered.get(ex, "An unexpected error occurred.")
|
||||
|
||||
# =======================================================================
|
||||
# CRUD Operations
|
||||
|
||||
@property
|
||||
def mixins(self) -> CrudMixins:
|
||||
return CrudMixins(self.repo, self.deps.logger, self.registered_exceptions, "An unexpected error occurred.")
|
||||
|
||||
@router.get("", response_model=list[GroupEventNotifierOut])
|
||||
def get_all(self, q: GetAll = Depends(GetAll)):
|
||||
return self.repo.get_all(start=q.start, limit=q.limit)
|
||||
|
||||
@router.post("", response_model=GroupEventNotifierOut, status_code=201)
|
||||
def create_one(self, data: GroupEventNotifierCreate):
|
||||
save_data = cast(data, GroupEventNotifierSave, group_id=self.deps.acting_user.group_id)
|
||||
return self.mixins.create_one(save_data)
|
||||
|
||||
@router.get("/{item_id}", response_model=GroupEventNotifierOut)
|
||||
def get_one(self, item_id: UUID4):
|
||||
return self.mixins.get_one(item_id)
|
||||
|
||||
@router.put("/{item_id}", response_model=GroupEventNotifierOut)
|
||||
def update_one(self, item_id: UUID4, data: GroupEventNotifierUpdate):
|
||||
if data.apprise_url is None:
|
||||
current_data: GroupEventNotifierPrivate = self.repo.get_one(
|
||||
item_id, override_schema=GroupEventNotifierPrivate
|
||||
)
|
||||
data.apprise_url = current_data.apprise_url
|
||||
|
||||
return self.mixins.update_one(data, item_id)
|
||||
|
||||
@router.delete("/{item_id}", status_code=204)
|
||||
def delete_one(self, item_id: UUID4):
|
||||
self.mixins.delete_one(item_id) # type: ignore
|
||||
|
||||
# =======================================================================
|
||||
# Test Event Notifications
|
||||
|
||||
@router.post("/{item_id}/test", status_code=204)
|
||||
def test_notification(self, item_id: UUID4):
|
||||
item: GroupEventNotifierPrivate = self.repo.get_one(item_id, override_schema=GroupEventNotifierPrivate)
|
||||
self.event_bus.test_publisher(item.apprise_url)
|
||||
@@ -17,6 +17,8 @@ from mealie.schema.group.group_shopping_list import (
|
||||
)
|
||||
from mealie.schema.mapper import cast
|
||||
from mealie.schema.query import GetAll
|
||||
from mealie.services.event_bus_service.event_bus_service import EventBusService
|
||||
from mealie.services.event_bus_service.message_types import EventTypes
|
||||
from mealie.services.group_services.shopping_lists import ShoppingListService
|
||||
|
||||
router = APIRouter(prefix="/groups/shopping/lists", tags=["Group: Shopping Lists"])
|
||||
@@ -26,6 +28,7 @@ router = APIRouter(prefix="/groups/shopping/lists", tags=["Group: Shopping Lists
|
||||
class ShoppingListRoutes:
|
||||
deps: SharedDependencies = Depends(SharedDependencies.user)
|
||||
service: ShoppingListService = Depends(ShoppingListService.private)
|
||||
event_bus: EventBusService = Depends(EventBusService)
|
||||
|
||||
@cached_property
|
||||
def repo(self):
|
||||
@@ -56,7 +59,16 @@ class ShoppingListRoutes:
|
||||
@router.post("", response_model=ShoppingListOut)
|
||||
def create_one(self, data: ShoppingListCreate):
|
||||
save_data = cast(data, ShoppingListSave, group_id=self.deps.acting_user.group_id)
|
||||
return self.mixins.create_one(save_data)
|
||||
val = self.mixins.create_one(save_data)
|
||||
|
||||
if val:
|
||||
self.event_bus.dispatch(
|
||||
self.deps.acting_user.group_id,
|
||||
EventTypes.shopping_list_created,
|
||||
msg="A new shopping list has been created.",
|
||||
)
|
||||
|
||||
return val
|
||||
|
||||
@router.get("/{item_id}", response_model=ShoppingListOut)
|
||||
def get_one(self, item_id: UUID4):
|
||||
|
||||
Reference in New Issue
Block a user