refactor: webhook events (#1661)

* refactored EventBusService to work outside FastAPI

* extended event models

* refactored webhooks to run through event bus

* added basic webhook test route

* changed get_all to page_all

* fixed incorrectly implemented Vue variables

* fixed broken webhook test

* changed factory from staticmethod to classmethod

* made query boundary definitions easier to read
This commit is contained in:
Michael Genson
2022-09-27 21:55:20 -05:00
committed by GitHub
parent 025f1bc603
commit 796e55b7d5
11 changed files with 175 additions and 54 deletions

View File

@@ -1,54 +1,63 @@
from datetime import datetime, timezone
from typing import Optional
import requests
from fastapi.encoders import jsonable_encoder
from pydantic import UUID4
from sqlalchemy.orm import Session
from mealie.db.db_setup import create_session
from mealie.db.models.group.webhooks import GroupWebhooksModel
from mealie.repos.all_repositories import get_repositories
from mealie.schema.response.pagination import PaginationQuery
from mealie.services.event_bus_service.event_bus_service import EventBusService
from mealie.services.event_bus_service.event_types import (
INTERNAL_INTEGRATION_ID,
EventDocumentType,
EventOperation,
EventTypes,
EventWebhookData,
)
last_ran = datetime.now(timezone.utc)
def get_scheduled_webhooks(session: Session, bottom: datetime, top: datetime) -> list[GroupWebhooksModel]:
def post_group_webhooks(start_dt: Optional[datetime] = None, group_id: Optional[UUID4] = None) -> None:
"""Post webhook events to specified group, or all groups"""
global last_ran
# if not specified, start the query at the last time the service ran
start_dt = start_dt or last_ran
# end the query at the current time
last_ran = end_dt = datetime.now(timezone.utc)
if group_id is None:
# publish the webhook event to each group's event bus
session = create_session()
repos = get_repositories(session)
groups_data = repos.groups.page_all(PaginationQuery(page=1, per_page=-1))
group_ids = [group.id for group in groups_data.items]
else:
group_ids = [group_id]
"""
get_scheduled_webhooks queries the database for all webhooks scheduled between the bottom and
top time ranges. It returns a list of GroupWebhooksModel objects.
At this time only mealplan webhooks are supported. To add support for more types,
add a dispatch event for that type here (e.g. EventDocumentType.recipe_bulk_report) and
handle the webhook data in the webhook event bus listener
"""
return (
session.query(GroupWebhooksModel)
.where(
GroupWebhooksModel.enabled == True, # noqa: E712 - required for SQLAlchemy comparison
GroupWebhooksModel.scheduled_time > bottom.astimezone(timezone.utc).time(),
GroupWebhooksModel.scheduled_time <= top.astimezone(timezone.utc).time(),
)
.all()
event_type = EventTypes.webhook_task
event_document_data = EventWebhookData(
document_type=EventDocumentType.mealplan,
operation=EventOperation.info,
webhook_start_dt=start_dt,
webhook_end_dt=end_dt,
)
def post_group_webhooks() -> None:
global last_ran
session = create_session()
results = get_scheduled_webhooks(session, last_ran, datetime.now())
last_ran = datetime.now(timezone.utc)
repos = get_repositories(session)
memo = {}
def get_meals(group_id: UUID4):
if group_id not in memo:
memo[group_id] = repos.meals.get_all(group_id=group_id)
return memo[group_id]
for result in results:
meals = get_meals(result.group_id)
if not meals:
continue
requests.post(result.url, json=jsonable_encoder(meals))
for group_id in group_ids:
event_bus = EventBusService(group_id=group_id)
event_bus.dispatch(
integration_id=INTERNAL_INTEGRATION_ID,
group_id=group_id,
event_type=event_type,
document_data=event_document_data,
)