Dispatcharr/apps/connect/utils.py
SergeantPanda b2496dc4e7 refactor(trigger_event): streamline event dispatching and improve plugin action handling
- Replaced the plugin listing mechanism with a more efficient handler iteration for event actions.
- Enhanced logging to provide clearer insights into the dispatching process and plugin action execution.
- Added error handling to ensure that failures in one plugin action do not block subsequent actions.
- Updated tests to cover new behavior and ensure proper handling of enabled and disabled plugins.
2026-06-16 11:25:17 -05:00

137 lines
4.7 KiB
Python

# connect/utils.py
import logging
from django.template import Template, Context
from .models import EventSubscription, DeliveryLog, SUPPORTED_EVENTS
from .handlers.webhook import WebhookHandler
from .handlers.script import ScriptHandler
from apps.plugins.loader import PluginManager
logger = logging.getLogger(__name__)
HANDLERS = {
"webhook": WebhookHandler,
"script": ScriptHandler,
}
def trigger_event(event_name, payload):
if event_name not in SUPPORTED_EVENTS:
logger.debug(f"Unsupported event '{event_name}' - skipping")
return
logger.debug(
f"Triggering connect event: {event_name} payload_keys={list((payload or {}).keys())}"
)
subscriptions = EventSubscription.objects.filter(
event=event_name, enabled=True
).select_related("integration")
count = subscriptions.count()
logger.info(f"Found {count} connect subscription(s) for event '{event_name}'")
# First, fetch all subscriptions and trigger
for sub in subscriptions:
integration = sub.integration
if not integration.enabled:
logger.debug(
f"Skipping disabled integration id={integration.id} name={integration.name}"
)
continue
# apply optional payload template (only for webhook integrations)
# If the rendered template is valid JSON, use that object as the payload.
# Otherwise, pass the rendered string as-is.
final_payload = payload
if integration.type == 'webhook' and sub.payload_template:
try:
template = Template(sub.payload_template)
final_payload = template.render(Context(payload)).strip()
except Exception as e:
logger.error(
f"Payload template render failed for subscription id={sub.id}: {e}"
)
final_payload = payload
handler_cls = HANDLERS.get(integration.type)
if not handler_cls:
DeliveryLog.objects.create(
subscription=sub,
status="failed",
request_payload=final_payload,
error_message=f"No handler for integration type '{integration.type}'",
)
logger.error(
f"No handler for integration type '{integration.type}' (integration id={integration.id})"
)
continue
handler = handler_cls(integration, sub, final_payload)
logger.debug(
f"Executing handler type={integration.type} integration_id={integration.id} subscription_id={sub.id}"
)
try:
result = handler.execute()
DeliveryLog.objects.create(
subscription=sub,
status="success" if result.get("success") else "failed",
request_payload=final_payload,
response_payload=result,
)
logger.info(
f"Connect delivery succeeded for subscription id={sub.id} integration '{integration.name}'"
)
except Exception as e:
DeliveryLog.objects.create(
subscription=sub,
status="failed",
request_payload=final_payload,
error_message=str(e),
)
logger.error(
f"Connect delivery failed for subscription id={sub.id} integration '{integration.name}': {e}"
)
pm = PluginManager.get()
pm.discover_plugins(sync_db=False, use_cache=True)
handlers = list(pm.iter_actions_for_event(event_name))
if not handlers:
return
from apps.plugins.models import PluginConfig
handler_keys = {key for key, _ in handlers}
enabled_keys = set(
PluginConfig.objects.filter(enabled=True, key__in=handler_keys).values_list(
"key", flat=True
)
)
logger.debug(
"Dispatching event '%s' to %d plugin action(s) (%d enabled)",
event_name,
len(handlers),
len(enabled_keys),
)
params = {"event": event_name, "payload": payload}
for key, action_id in handlers:
if key not in enabled_keys:
logger.debug(
"Skipping disabled plugin id=%s for event '%s'", key, event_name
)
continue
logger.debug(
"Triggering plugin action for event '%s' on plugin id=%s action=%s",
event_name,
key,
action_id,
)
try:
pm.run_action(key, action_id, params)
except Exception:
logger.exception(
"Plugin action failed for event '%s' on plugin id=%s action=%s",
event_name,
key,
action_id,
)