mirror of
https://github.com/langgenius/dify.git
synced 2026-04-27 14:08:18 +08:00
feat(trigger): implement complete OAuth authorization flow for trigger providers
- Add OAuth authorization URL generation API endpoint - Implement OAuth callback handler for credential storage - Support both system-level and tenant-level OAuth clients - Add trigger provider credential encryption utilities - Refactor trigger entities into separate modules - Update trigger provider service with OAuth client management - Add credential cache for trigger providers 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -5,11 +5,14 @@ Trigger Manager for loading and managing trigger providers and triggers
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from core.trigger.entities import (
|
||||
from core.plugin.entities.plugin import TriggerProviderID
|
||||
from core.plugin.impl.trigger import PluginTriggerManager
|
||||
from core.trigger.entities.entities import (
|
||||
ProviderConfig,
|
||||
Subscription,
|
||||
TriggerEntity,
|
||||
Unsubscription,
|
||||
)
|
||||
from core.trigger.plugin_trigger import PluginTriggerController
|
||||
from core.trigger.provider import PluginTriggerProviderController
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -28,7 +31,7 @@ class TriggerManager:
|
||||
:param tenant_id: Tenant ID
|
||||
:return: List of trigger provider controllers
|
||||
"""
|
||||
manager = PluginTriggerController()
|
||||
manager = PluginTriggerManager()
|
||||
provider_entities = manager.fetch_trigger_providers(tenant_id)
|
||||
|
||||
controllers = []
|
||||
@ -48,22 +51,21 @@ class TriggerManager:
|
||||
return controllers
|
||||
|
||||
@classmethod
|
||||
def get_plugin_trigger_provider(
|
||||
cls, tenant_id: str, plugin_id: str, provider_name: str
|
||||
) -> Optional[PluginTriggerProviderController]:
|
||||
def get_trigger_provider(
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID
|
||||
) -> PluginTriggerProviderController:
|
||||
"""
|
||||
Get a specific plugin trigger provider
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:return: Trigger provider controller or None
|
||||
"""
|
||||
manager = PluginTriggerManager()
|
||||
provider = manager.fetch_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
provider = manager.fetch_trigger_provider(tenant_id, provider_id)
|
||||
|
||||
if not provider:
|
||||
return None
|
||||
raise ValueError(f"Trigger provider {provider_id} not found")
|
||||
|
||||
try:
|
||||
return PluginTriggerProviderController(
|
||||
@ -74,287 +76,139 @@ class TriggerManager:
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("Failed to load trigger provider")
|
||||
return None
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
def list_all_trigger_providers(cls, tenant_id: str) -> list[PluginTriggerProviderController]:
|
||||
"""
|
||||
List all trigger providers (plugin and builtin)
|
||||
List all trigger providers (plugin)
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:return: List of all trigger provider controllers
|
||||
"""
|
||||
providers = []
|
||||
|
||||
# Get plugin providers
|
||||
plugin_providers = cls.list_plugin_trigger_providers(tenant_id)
|
||||
providers.extend(plugin_providers)
|
||||
|
||||
# TODO: Add builtin providers when implemented
|
||||
# builtin_providers = cls.list_builtin_trigger_providers(tenant_id)
|
||||
# providers.extend(builtin_providers)
|
||||
|
||||
return providers
|
||||
return cls.list_plugin_trigger_providers(tenant_id)
|
||||
|
||||
@classmethod
|
||||
def list_triggers_by_provider(cls, tenant_id: str, plugin_id: str, provider_name: str) -> list[TriggerEntity]:
|
||||
def list_triggers_by_provider(cls, tenant_id: str, provider_id: TriggerProviderID) -> list[TriggerEntity]:
|
||||
"""
|
||||
List all triggers for a specific provider
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:return: List of trigger entities
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
return []
|
||||
|
||||
provider = cls.get_trigger_provider(tenant_id, provider_id)
|
||||
return provider.get_triggers()
|
||||
|
||||
@classmethod
|
||||
def get_trigger(
|
||||
cls, tenant_id: str, plugin_id: str, provider_name: str, trigger_name: str
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID, trigger_name: str
|
||||
) -> Optional[TriggerEntity]:
|
||||
"""
|
||||
Get a specific trigger
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:param trigger_name: Trigger name
|
||||
:return: Trigger entity or None
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
return None
|
||||
|
||||
return provider.get_trigger(trigger_name)
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).get_trigger(trigger_name)
|
||||
|
||||
@classmethod
|
||||
def validate_trigger_credentials(
|
||||
cls, tenant_id: str, plugin_id: str, provider_name: str, credentials: dict
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID, credentials: dict
|
||||
) -> tuple[bool, str]:
|
||||
"""
|
||||
Validate trigger provider credentials
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:param credentials: Credentials to validate
|
||||
:return: Tuple of (is_valid, error_message)
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
return False, "Provider not found"
|
||||
|
||||
try:
|
||||
provider.validate_credentials(credentials)
|
||||
cls.get_trigger_provider(tenant_id, provider_id).validate_credentials(credentials)
|
||||
return True, ""
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
@classmethod
|
||||
def execute_trigger(
|
||||
cls, tenant_id: str, plugin_id: str, provider_name: str, trigger_name: str, parameters: dict, credentials: dict
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID, trigger_name: str, parameters: dict, credentials: dict
|
||||
) -> dict:
|
||||
"""
|
||||
Execute a trigger
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:param trigger_name: Trigger name
|
||||
:param parameters: Trigger parameters
|
||||
:param credentials: Provider credentials
|
||||
:return: Trigger execution result
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
raise ValueError(f"Provider {plugin_id}/{provider_name} not found")
|
||||
|
||||
trigger = provider.get_trigger(trigger_name)
|
||||
trigger = cls.get_trigger_provider(tenant_id, provider_id).get_trigger(trigger_name)
|
||||
if not trigger:
|
||||
raise ValueError(f"Trigger {trigger_name} not found in provider {provider_name}")
|
||||
|
||||
return provider.execute_trigger(trigger_name, parameters, credentials)
|
||||
raise ValueError(f"Trigger {trigger_name} not found in provider {provider_id}")
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).execute_trigger(trigger_name, parameters, credentials)
|
||||
|
||||
@classmethod
|
||||
def subscribe_trigger(
|
||||
cls,
|
||||
tenant_id: str,
|
||||
plugin_id: str,
|
||||
provider_name: str,
|
||||
provider_id: TriggerProviderID,
|
||||
trigger_name: str,
|
||||
subscription_params: dict,
|
||||
credentials: dict,
|
||||
) -> dict:
|
||||
) -> Subscription:
|
||||
"""
|
||||
Subscribe to a trigger (e.g., register webhook)
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:param trigger_name: Trigger name
|
||||
:param subscription_params: Subscription parameters
|
||||
:param credentials: Provider credentials
|
||||
:return: Subscription result
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
raise ValueError(f"Provider {plugin_id}/{provider_name} not found")
|
||||
|
||||
return provider.subscribe_trigger(trigger_name, subscription_params, credentials)
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).subscribe_trigger(
|
||||
trigger_name, subscription_params, credentials
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def unsubscribe_trigger(
|
||||
cls,
|
||||
tenant_id: str,
|
||||
plugin_id: str,
|
||||
provider_name: str,
|
||||
provider_id: TriggerProviderID,
|
||||
trigger_name: str,
|
||||
subscription_metadata: dict,
|
||||
credentials: dict,
|
||||
) -> dict:
|
||||
) -> Unsubscription:
|
||||
"""
|
||||
Unsubscribe from a trigger
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:param trigger_name: Trigger name
|
||||
:param subscription_metadata: Subscription metadata from subscribe operation
|
||||
:param credentials: Provider credentials
|
||||
:return: Unsubscription result
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
raise ValueError(f"Provider {plugin_id}/{provider_name} not found")
|
||||
|
||||
return provider.unsubscribe_trigger(trigger_name, subscription_metadata, credentials)
|
||||
|
||||
@classmethod
|
||||
def handle_webhook(
|
||||
cls,
|
||||
tenant_id: str,
|
||||
plugin_id: str,
|
||||
provider_name: str,
|
||||
webhook_path: str,
|
||||
request_data: dict,
|
||||
credentials: dict,
|
||||
) -> dict:
|
||||
"""
|
||||
Handle incoming webhook for a trigger
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param webhook_path: Webhook path
|
||||
:param request_data: Webhook request data
|
||||
:param credentials: Provider credentials
|
||||
:return: Webhook handling result
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
raise ValueError(f"Provider {plugin_id}/{provider_name} not found")
|
||||
|
||||
return provider.handle_webhook(webhook_path, request_data, credentials)
|
||||
|
||||
@classmethod
|
||||
def get_provider_credentials_schema(
|
||||
cls, tenant_id: str, plugin_id: str, provider_name: str
|
||||
) -> list[ProviderConfig]:
|
||||
"""
|
||||
Get provider credentials schema
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:return: List of provider config schemas
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
return []
|
||||
|
||||
return provider.get_credentials_schema()
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).unsubscribe_trigger(
|
||||
trigger_name, subscription_metadata, credentials
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_provider_subscription_schema(
|
||||
cls, tenant_id: str, plugin_id: str, provider_name: str
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID
|
||||
) -> list[ProviderConfig]:
|
||||
"""
|
||||
Get provider subscription schema
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:param provider_id: Provider ID
|
||||
:return: List of subscription config schemas
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
return []
|
||||
|
||||
return provider.get_subscription_schema()
|
||||
|
||||
@classmethod
|
||||
def get_provider_info(cls, tenant_id: str, plugin_id: str, provider_name: str) -> Optional[dict]:
|
||||
"""
|
||||
Get provider information
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param plugin_id: Plugin ID
|
||||
:param provider_name: Provider name
|
||||
:return: Provider info dict or None
|
||||
"""
|
||||
provider = cls.get_plugin_trigger_provider(tenant_id, plugin_id, provider_name)
|
||||
|
||||
if not provider:
|
||||
return None
|
||||
|
||||
return {
|
||||
"plugin_id": plugin_id,
|
||||
"provider_name": provider_name,
|
||||
"identity": provider.entity.identity.model_dump() if provider.entity.identity else {},
|
||||
"credentials_schema": [c.model_dump() for c in provider.entity.credentials_schema],
|
||||
"subscription_schema": [s.model_dump() for s in provider.entity.subscription_schema],
|
||||
"oauth_enabled": provider.entity.oauth_schema is not None,
|
||||
"trigger_count": len(provider.entity.triggers),
|
||||
"triggers": [t.identity.model_dump() for t in provider.entity.triggers],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def list_providers_for_workflow(cls, tenant_id: str) -> list[dict]:
|
||||
"""
|
||||
List trigger providers suitable for workflow usage
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:return: List of provider info dicts
|
||||
"""
|
||||
providers = cls.list_all_trigger_providers(tenant_id)
|
||||
|
||||
result = []
|
||||
for provider in providers:
|
||||
info = {
|
||||
"plugin_id": provider.plugin_id,
|
||||
"provider_name": provider.entity.identity.name,
|
||||
"label": provider.entity.identity.label.model_dump(),
|
||||
"description": provider.entity.identity.description.model_dump(),
|
||||
"icon": provider.entity.identity.icon,
|
||||
"trigger_count": len(provider.entity.triggers),
|
||||
}
|
||||
result.append(info)
|
||||
|
||||
return result
|
||||
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).get_subscription_schema()
|
||||
|
||||
# Export
|
||||
__all__ = ["TriggerManager"]
|
||||
|
||||
Reference in New Issue
Block a user