Some more refactoring. Push_events don't work at the moment

This commit is contained in:
Jonas Linter
2025-11-27 15:33:15 +01:00
parent f89236228d
commit e2d0ef8e53
4 changed files with 1330193 additions and 314 deletions

View File

@@ -30,20 +30,16 @@ class WebhookProcessorProtocol(Protocol):
async def process(
self,
payload: dict[str, Any],
webhook_request: WebhookRequest,
hotel: Hotel,
db_session: AsyncSession,
request: Request,
config: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Process webhook payload.
Args:
payload: Parsed webhook payload
webhook_request: WebhookRequest database record
hotel: Hotel associated with this webhook
webhook_request: WebhookRequest database record (contains payload_json and hotel_id)
db_session: Database session
request: FastAPI Request object
config: Application configuration (optional)
Returns:
Response dict with status, message, customer_id, reservation_id
@@ -85,12 +81,38 @@ class WebhookProcessorRegistry:
return self._processors.get(webhook_type)
async def process_wix_form_submission(request: Request, data: dict[str, Any], db):
"""Shared business logic for handling Wix form submissions (test and production)."""
async def process_wix_form_submission(
request: Request | None,
data: dict[str, Any],
db,
config: dict[str, Any] | None = None,
hotel_id: str | None = None,
event_dispatcher=None,
):
"""Shared business logic for handling Wix form submissions (test and production).
Args:
request: FastAPI Request object (can be None during startup reprocessing)
data: Webhook payload data
db: Database session
config: Application config (optional, extracted from request if not provided)
hotel_id: Hotel ID (optional, will use from data or config default if not provided)
event_dispatcher: Event dispatcher for push notifications (optional)
"""
timestamp = datetime.now().isoformat()
_LOGGER.info("Received Wix form data at %s", timestamp)
# Extract config and event_dispatcher from request if not provided
if config is None and request is not None:
config = request.app.state.config
if event_dispatcher is None and request is not None:
event_dispatcher = getattr(request.app.state, "event_dispatcher", None)
# Provide fallback config if still None
if config is None:
config = {}
data = data.get("data") # Handle nested "data" key if present
# save customer and reservation to DB
@@ -191,18 +213,17 @@ async def process_wix_form_submission(request: Request, data: dict[str, Any], db
db_customer = await customer_service.get_or_create_customer(customer_data)
# Determine hotel_code and hotel_name
# Priority: 1) Form field, 2) Configuration default, 3) Hardcoded fallback
hotel_code = data.get("field:hotelid", None)
# Priority: 1) Passed hotel_id, 2) Form field, 3) Config default, 4) Fallback
hotel_code = hotel_id or data.get("field:hotelid", None)
if hotel_code is None:
_LOGGER.warning("No hotel_code provided in form data, using default")
hotel_code = request.app.state.config.get("default_hotel_code", "123")
_LOGGER.warning("No hotel_code provided, using default from config")
hotel_code = config.get("default_hotel_code", "123")
hotel_name = (
data.get("field:hotelname")
or data.get("hotelname")
or request.app.state.config.get("default_hotel_name")
or config.get("default_hotel_name")
or "Frangart Inn" # fallback
)
@@ -222,7 +243,7 @@ async def process_wix_form_submission(request: Request, data: dict[str, Any], db
# Get advertising account IDs conditionally based on fbclid/gclid presence
meta_account_id, google_account_id = get_advertising_account_ids(
request.app.state.config, hotel_code, fbclid, gclid
config, hotel_code, fbclid, gclid
)
reservation = ReservationData(
@@ -265,12 +286,11 @@ async def process_wix_form_submission(request: Request, data: dict[str, Any], db
async def push_event():
# Fire event for listeners (push, etc.) - hotel-specific dispatch
dispatcher = getattr(request.app.state, "event_dispatcher", None)
if dispatcher:
if event_dispatcher:
# Get hotel_code from reservation to target the right listeners
hotel_code = getattr(db_reservation, "hotel_code", None)
if hotel_code and hotel_code.strip():
await dispatcher.dispatch_for_hotel(
await event_dispatcher.dispatch_for_hotel(
"form_processed", hotel_code, db_customer, db_reservation
)
_LOGGER.info("Dispatched form_processed event for hotel %s", hotel_code)
@@ -304,41 +324,52 @@ class WixFormProcessor:
async def process(
self,
payload: dict[str, Any],
webhook_request: WebhookRequest,
hotel: Hotel,
db_session: AsyncSession,
request: Request,
config: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Process Wix form webhook payload.
Args:
payload: Parsed webhook payload
webhook_request: WebhookRequest database record
hotel: Hotel associated with this webhook
db_session: Database session
request: FastAPI Request object
config: Application configuration (optional)
Returns:
Response dict with status and details
"""
# Import here to avoid circular dependency
# Call processing function with data from webhook_request
result = await process_wix_form_submission(
request=None, # No request context needed
data=webhook_request.payload_json,
db=db_session,
config=config,
hotel_id=webhook_request.hotel_id,
event_dispatcher=None, # No push events during reprocessing
)
# Call existing processing function
result = await process_wix_form_submission(request, payload, db_session)
# The existing function doesn't return customer/reservation IDs directly,
# but they would be in the database session. We'll need to extract them
# from the result or query after the fact. For now, return the result as-is.
return result
async def process_generic_webhook_submission(
request: Request, data: dict[str, Any], db
request: Request | None,
data: dict[str, Any],
db,
config: dict[str, Any] | None = None,
hotel_id: str | None = None,
event_dispatcher=None,
):
"""Process generic webhook submissions with nested structure.
Args:
request: FastAPI Request object (can be None during startup reprocessing)
data: Webhook payload data
db: Database session
config: Application config (optional, extracted from request if not provided)
hotel_id: Hotel ID (optional, will use from data or config default)
event_dispatcher: Event dispatcher for push notifications (optional)
Expected structure:
{
"hotel_data": {"hotelname": "...", "hotelcode": "..."},
@@ -371,6 +402,16 @@ async def process_generic_webhook_submission(
timestamp = datetime.now().isoformat()
_LOGGER.info("Processing generic webhook submission at %s", timestamp)
# Extract config and event_dispatcher from request if not provided
if config is None and request is not None:
config = request.app.state.config
if event_dispatcher is None and request is not None:
event_dispatcher = getattr(request.app.state, "event_dispatcher", None)
# Provide fallback config if still None
if config is None:
config = {}
# Extract nested data
hotel_data = data.get("hotel_data", {})
form_data = data.get("form_data", {})
@@ -391,17 +432,16 @@ async def process_generic_webhook_submission(
selected_offers_str = ", ".join(selected_offers) if selected_offers else None
# Extract hotel information
hotel_code = hotel_data.get("hotelcode")
# Priority: 1) Passed hotel_id, 2) Webhook data, 3) Config default, 4) Fallback
hotel_code = hotel_id or hotel_data.get("hotelcode")
hotel_name = hotel_data.get("hotelname")
if not hotel_code:
_LOGGER.warning("No hotel_code provided in webhook data, using default")
hotel_code = request.app.state.config.get("default_hotel_code", "123")
_LOGGER.warning("No hotel_code provided, using default from config")
hotel_code = config.get("default_hotel_code", "123")
if not hotel_name:
hotel_name = (
request.app.state.config.get("default_hotel_name") or "Frangart Inn"
)
hotel_name = config.get("default_hotel_name") or "Frangart Inn"
# Extract customer information
first_name = form_data.get("name")
@@ -517,7 +557,7 @@ async def process_generic_webhook_submission(
# Get advertising account IDs conditionally based on fbclid/gclid presence
meta_account_id, google_account_id = get_advertising_account_ids(
request.app.state.config, hotel_code, fbclid, gclid
config, hotel_code, fbclid, gclid
)
# Create reservation
@@ -560,12 +600,11 @@ async def process_generic_webhook_submission(
async def push_event():
# Fire event for listeners (push, etc.) - hotel-specific dispatch
dispatcher = getattr(request.app.state, "event_dispatcher", None)
if dispatcher:
if event_dispatcher:
# Get hotel_code from reservation to target the right listeners
hotel_code = getattr(db_reservation, "hotel_code", None)
if hotel_code and hotel_code.strip():
await dispatcher.dispatch_for_hotel(
await event_dispatcher.dispatch_for_hotel(
"form_processed", hotel_code, db_customer, db_reservation
)
_LOGGER.info("Dispatched form_processed event for hotel %s", hotel_code)
@@ -604,27 +643,30 @@ class GenericWebhookProcessor:
async def process(
self,
payload: dict[str, Any],
webhook_request: WebhookRequest,
hotel: Hotel,
db_session: AsyncSession,
request: Request,
config: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Process generic webhook payload.
Args:
payload: Parsed webhook payload
webhook_request: WebhookRequest database record
hotel: Hotel associated with this webhook
db_session: Database session
request: FastAPI Request object
config: Application configuration (optional)
Returns:
Response dict with status and details
"""
# Call existing processing function
result = await process_generic_webhook_submission(request, payload, db_session)
# Call processing function with data from webhook_request
result = await process_generic_webhook_submission(
request=None, # No request context needed
data=webhook_request.payload_json,
db=db_session,
config=config,
hotel_id=webhook_request.hotel_id,
event_dispatcher=None, # No push events during reprocessing
)
return result