mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 09:58:04 +08:00
feat(trigger): introduce subscription builder and enhance trigger management
- Refactor trigger provider classes to improve naming consistency, including renaming classes for subscription management - Implement new TriggerSubscriptionBuilderService for creating and verifying subscription builders - Update API endpoints to support subscription builder creation and verification - Enhance data models to include new attributes for subscription builders - Remove the deprecated TriggerSubscriptionValidationService to streamline the codebase Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@ -210,12 +210,15 @@ class PluginTriggerProviderEntity(BaseModel):
|
||||
class CredentialType(enum.StrEnum):
|
||||
API_KEY = "api-key"
|
||||
OAUTH2 = "oauth2"
|
||||
UNAUTHORIZED = "unauthorized"
|
||||
|
||||
def get_name(self):
|
||||
if self == CredentialType.API_KEY:
|
||||
return "API KEY"
|
||||
elif self == CredentialType.OAUTH2:
|
||||
return "AUTH"
|
||||
elif self == CredentialType.UNAUTHORIZED:
|
||||
return "UNAUTHORIZED"
|
||||
else:
|
||||
return self.value.replace("-", " ").upper()
|
||||
|
||||
@ -236,5 +239,7 @@ class CredentialType(enum.StrEnum):
|
||||
return cls.API_KEY
|
||||
elif type_name == "oauth2":
|
||||
return cls.OAUTH2
|
||||
elif type_name == "unauthorized":
|
||||
return cls.UNAUTHORIZED
|
||||
else:
|
||||
raise ValueError(f"Invalid credential type: {credential_type}")
|
||||
|
||||
@ -248,14 +248,17 @@ class PluginTriggerDispatchResponse(BaseModel):
|
||||
triggers: list[str]
|
||||
raw_http_response: str
|
||||
|
||||
|
||||
class TriggerSubscriptionResponse(BaseModel):
|
||||
subscription: dict[str, Any]
|
||||
|
||||
|
||||
class TriggerValidateProviderCredentialsResponse(BaseModel):
|
||||
valid: bool
|
||||
message: str
|
||||
error: str
|
||||
|
||||
|
||||
class TriggerDispatchResponse:
|
||||
triggers: list[str]
|
||||
response: Response
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import binascii
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from flask import Request
|
||||
@ -84,10 +85,10 @@ class PluginTriggerManager(BasePluginClient):
|
||||
user_id: str,
|
||||
provider: str,
|
||||
trigger: str,
|
||||
credentials: dict[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
credential_type: CredentialType,
|
||||
request: Request,
|
||||
parameters: dict[str, Any],
|
||||
parameters: Mapping[str, Any],
|
||||
) -> TriggerInvokeResponse:
|
||||
"""
|
||||
Invoke a trigger with the given parameters.
|
||||
@ -121,7 +122,7 @@ class PluginTriggerManager(BasePluginClient):
|
||||
raise ValueError("No response received from plugin daemon for invoke trigger")
|
||||
|
||||
def validate_provider_credentials(
|
||||
self, tenant_id: str, user_id: str, provider: str, credentials: dict[str, Any]
|
||||
self, tenant_id: str, user_id: str, provider: str, credentials: Mapping[str, str]
|
||||
) -> TriggerValidateProviderCredentialsResponse:
|
||||
"""
|
||||
Validate the credentials of the trigger provider.
|
||||
@ -155,7 +156,7 @@ class PluginTriggerManager(BasePluginClient):
|
||||
tenant_id: str,
|
||||
user_id: str,
|
||||
provider: str,
|
||||
subscription: dict[str, Any],
|
||||
subscription: Mapping[str, Any],
|
||||
request: Request,
|
||||
) -> TriggerDispatchResponse:
|
||||
"""
|
||||
@ -194,9 +195,9 @@ class PluginTriggerManager(BasePluginClient):
|
||||
tenant_id: str,
|
||||
user_id: str,
|
||||
provider: str,
|
||||
credentials: dict[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
endpoint: str,
|
||||
parameters: dict[str, Any],
|
||||
parameters: Mapping[str, Any],
|
||||
) -> TriggerSubscriptionResponse:
|
||||
"""
|
||||
Subscribe to a trigger.
|
||||
@ -233,7 +234,7 @@ class PluginTriggerManager(BasePluginClient):
|
||||
user_id: str,
|
||||
provider: str,
|
||||
subscription: Subscription,
|
||||
credentials: dict[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
) -> TriggerSubscriptionResponse:
|
||||
"""
|
||||
Unsubscribe from a trigger.
|
||||
@ -269,7 +270,7 @@ class PluginTriggerManager(BasePluginClient):
|
||||
user_id: str,
|
||||
provider: str,
|
||||
subscription: Subscription,
|
||||
credentials: dict[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
) -> TriggerSubscriptionResponse:
|
||||
"""
|
||||
Refresh a trigger subscription.
|
||||
|
||||
@ -7,7 +7,6 @@ from core.entities.provider_entities import ProviderConfig
|
||||
from core.plugin.entities.plugin_daemon import CredentialType
|
||||
from core.trigger.entities.entities import (
|
||||
OAuthSchema,
|
||||
Subscription,
|
||||
SubscriptionSchema,
|
||||
TriggerDescription,
|
||||
TriggerEntity,
|
||||
@ -40,27 +39,4 @@ class TriggerApiEntity(BaseModel):
|
||||
parameters: list[TriggerParameter] = Field(description="The parameters of the trigger")
|
||||
output_schema: Optional[Mapping[str, Any]] = Field(description="The output schema of the trigger")
|
||||
|
||||
class SubscriptionValidation(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
tenant_id: str
|
||||
user_id: str
|
||||
provider_id: str
|
||||
endpoint: str
|
||||
parameters: dict
|
||||
properties: dict
|
||||
credentials: dict
|
||||
credential_type: str
|
||||
credential_expires_at: int
|
||||
expires_at: int
|
||||
|
||||
def to_subscription(self) -> Subscription:
|
||||
return Subscription(
|
||||
expires_at=self.expires_at,
|
||||
endpoint=self.endpoint,
|
||||
parameters=self.parameters,
|
||||
properties=self.properties,
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["TriggerApiEntity", "TriggerProviderApiEntity", "TriggerProviderSubscriptionApiEntity"]
|
||||
|
||||
@ -115,6 +115,18 @@ class SubscriptionSchema(BaseModel):
|
||||
description="The configuration schema stored in the subscription entity",
|
||||
)
|
||||
|
||||
def get_default_parameters(self) -> Mapping[str, Any]:
|
||||
"""Get the default parameters from the parameters schema"""
|
||||
if not self.parameters_schema:
|
||||
return {}
|
||||
return {param.name: param.default for param in self.parameters_schema if param.default}
|
||||
|
||||
def get_default_properties(self) -> Mapping[str, Any]:
|
||||
"""Get the default properties from the properties schema"""
|
||||
if not self.properties_schema:
|
||||
return {}
|
||||
return {prop.name: prop.default for prop in self.properties_schema if prop.default}
|
||||
|
||||
|
||||
class TriggerProviderEntity(BaseModel):
|
||||
"""
|
||||
@ -148,13 +160,7 @@ class Subscription(BaseModel):
|
||||
)
|
||||
|
||||
endpoint: str = Field(..., description="The webhook endpoint URL allocated by Dify for receiving events")
|
||||
|
||||
parameters: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="""The parameters of the subscription, this is the creation parameters.
|
||||
Only available when creating a new subscription by credentials(auto subscription), not manual subscription""",
|
||||
)
|
||||
properties: dict[str, Any] = Field(
|
||||
properties: Mapping[str, Any] = Field(
|
||||
..., description="Subscription data containing all properties and provider-specific information"
|
||||
)
|
||||
|
||||
@ -177,10 +183,43 @@ class Unsubscription(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class RequestLog(BaseModel):
|
||||
id: str
|
||||
endpoint: str
|
||||
request: dict
|
||||
response: dict
|
||||
created_at: str
|
||||
|
||||
|
||||
class SubscriptionBuilder(BaseModel):
|
||||
id: str
|
||||
name: str | None = None
|
||||
tenant_id: str
|
||||
user_id: str
|
||||
provider_id: str
|
||||
endpoint_id: str
|
||||
parameters: Mapping[str, Any]
|
||||
properties: Mapping[str, Any]
|
||||
credentials: Mapping[str, str]
|
||||
credential_type: str | None = None
|
||||
credential_expires_at: int | None = None
|
||||
expires_at: int
|
||||
|
||||
def to_subscription(self) -> Subscription:
|
||||
return Subscription(
|
||||
expires_at=self.expires_at,
|
||||
endpoint=self.endpoint_id,
|
||||
parameters=self.parameters,
|
||||
properties=self.properties,
|
||||
)
|
||||
|
||||
|
||||
# Export all entities
|
||||
__all__ = [
|
||||
"OAuthSchema",
|
||||
"RequestLog",
|
||||
"Subscription",
|
||||
"SubscriptionBuilder",
|
||||
"TriggerDescription",
|
||||
"TriggerEntity",
|
||||
"TriggerIdentity",
|
||||
|
||||
@ -3,7 +3,8 @@ Trigger Provider Controller for managing trigger providers
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, Optional
|
||||
|
||||
from flask import Request
|
||||
|
||||
@ -20,6 +21,7 @@ from core.trigger.entities.api_entities import TriggerProviderApiEntity
|
||||
from core.trigger.entities.entities import (
|
||||
ProviderConfig,
|
||||
Subscription,
|
||||
SubscriptionSchema,
|
||||
TriggerEntity,
|
||||
TriggerProviderEntity,
|
||||
TriggerProviderIdentity,
|
||||
@ -91,18 +93,17 @@ class PluginTriggerProviderController:
|
||||
return trigger
|
||||
return None
|
||||
|
||||
def get_subscription_schema(self) -> list[ProviderConfig]:
|
||||
def get_subscription_schema(self) -> SubscriptionSchema:
|
||||
"""
|
||||
Get subscription schema for this provider
|
||||
|
||||
:return: List of subscription config schemas
|
||||
"""
|
||||
# Return the parameters schema from the subscription schema
|
||||
if self.entity.subscription_schema and self.entity.subscription_schema.parameters_schema:
|
||||
return self.entity.subscription_schema.parameters_schema
|
||||
return []
|
||||
return self.entity.subscription_schema
|
||||
|
||||
def validate_credentials(self, credentials: dict) -> TriggerValidateProviderCredentialsResponse:
|
||||
def validate_credentials(
|
||||
self, user_id: str, credentials: Mapping[str, str]
|
||||
) -> TriggerValidateProviderCredentialsResponse:
|
||||
"""
|
||||
Validate credentials against schema
|
||||
|
||||
@ -123,7 +124,7 @@ class PluginTriggerProviderController:
|
||||
provider_id = self.get_provider_id()
|
||||
return manager.validate_provider_credentials(
|
||||
tenant_id=self.tenant_id,
|
||||
user_id="system", # System validation
|
||||
user_id=user_id,
|
||||
provider=str(provider_id),
|
||||
credentials=credentials,
|
||||
)
|
||||
@ -153,6 +154,7 @@ class PluginTriggerProviderController:
|
||||
return self.entity.oauth_schema.credentials_schema.copy() if self.entity.oauth_schema else []
|
||||
if credential_type == CredentialType.API_KEY:
|
||||
return self.entity.credentials_schema.copy() if self.entity.credentials_schema else []
|
||||
raise ValueError(f"Invalid credential type: {credential_type}")
|
||||
|
||||
def get_credential_schema_config(self, credential_type: CredentialType | str) -> list[BasicProviderConfig]:
|
||||
"""
|
||||
@ -168,7 +170,7 @@ class PluginTriggerProviderController:
|
||||
"""
|
||||
return self.entity.oauth_schema.client_schema.copy() if self.entity.oauth_schema else []
|
||||
|
||||
def dispatch(self,user_id: str, request: Request, subscription: Subscription) -> TriggerDispatchResponse:
|
||||
def dispatch(self, user_id: str, request: Request, subscription: Subscription) -> TriggerDispatchResponse:
|
||||
"""
|
||||
Dispatch a trigger through plugin runtime
|
||||
|
||||
@ -193,8 +195,8 @@ class PluginTriggerProviderController:
|
||||
self,
|
||||
user_id: str,
|
||||
trigger_name: str,
|
||||
parameters: dict,
|
||||
credentials: dict,
|
||||
parameters: Mapping[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
credential_type: CredentialType,
|
||||
request: Request,
|
||||
) -> TriggerInvokeResponse:
|
||||
@ -223,7 +225,9 @@ class PluginTriggerProviderController:
|
||||
parameters=parameters,
|
||||
)
|
||||
|
||||
def subscribe_trigger(self, user_id: str, endpoint: str, parameters: dict, credentials: dict) -> Subscription:
|
||||
def subscribe_trigger(
|
||||
self, user_id: str, endpoint: str, parameters: Mapping[str, Any], credentials: Mapping[str, str]
|
||||
) -> Subscription:
|
||||
"""
|
||||
Subscribe to a trigger through plugin runtime
|
||||
|
||||
@ -247,7 +251,9 @@ class PluginTriggerProviderController:
|
||||
|
||||
return Subscription.model_validate(response.subscription)
|
||||
|
||||
def unsubscribe_trigger(self, user_id: str, subscription: Subscription, credentials: dict) -> Unsubscription:
|
||||
def unsubscribe_trigger(
|
||||
self, user_id: str, subscription: Subscription, credentials: Mapping[str, str]
|
||||
) -> Unsubscription:
|
||||
"""
|
||||
Unsubscribe from a trigger through plugin runtime
|
||||
|
||||
@ -269,7 +275,7 @@ class PluginTriggerProviderController:
|
||||
|
||||
return Unsubscription.model_validate(response.subscription)
|
||||
|
||||
def refresh_trigger(self, subscription: Subscription, credentials: dict) -> Subscription:
|
||||
def refresh_trigger(self, subscription: Subscription, credentials: Mapping[str, str]) -> Subscription:
|
||||
"""
|
||||
Refresh a trigger subscription through plugin runtime
|
||||
|
||||
|
||||
@ -3,7 +3,8 @@ Trigger Manager for loading and managing trigger providers and triggers
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, Optional
|
||||
|
||||
from flask import Request
|
||||
|
||||
@ -12,8 +13,8 @@ from core.plugin.entities.plugin_daemon import CredentialType
|
||||
from core.plugin.entities.request import TriggerInvokeResponse
|
||||
from core.plugin.impl.trigger import PluginTriggerManager
|
||||
from core.trigger.entities.entities import (
|
||||
ProviderConfig,
|
||||
Subscription,
|
||||
SubscriptionSchema,
|
||||
TriggerEntity,
|
||||
Unsubscription,
|
||||
)
|
||||
@ -116,19 +117,20 @@ class TriggerManager:
|
||||
|
||||
@classmethod
|
||||
def validate_trigger_credentials(
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID, credentials: dict
|
||||
cls, tenant_id: str, provider_id: TriggerProviderID, user_id: str, credentials: Mapping[str, str]
|
||||
) -> tuple[bool, str]:
|
||||
"""
|
||||
Validate trigger provider credentials
|
||||
|
||||
:param tenant_id: Tenant ID
|
||||
:param provider_id: Provider ID
|
||||
:param user_id: User ID
|
||||
:param credentials: Credentials to validate
|
||||
:return: Tuple of (is_valid, error_message)
|
||||
"""
|
||||
try:
|
||||
provider = cls.get_trigger_provider(tenant_id, provider_id)
|
||||
validation_result = provider.validate_credentials(credentials)
|
||||
validation_result = provider.validate_credentials(user_id, credentials)
|
||||
return validation_result.valid, validation_result.message if not validation_result.valid else ""
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
@ -140,8 +142,8 @@ class TriggerManager:
|
||||
user_id: str,
|
||||
provider_id: TriggerProviderID,
|
||||
trigger_name: str,
|
||||
parameters: dict,
|
||||
credentials: dict,
|
||||
parameters: Mapping[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
credential_type: CredentialType,
|
||||
request: Request,
|
||||
) -> TriggerInvokeResponse:
|
||||
@ -171,8 +173,8 @@ class TriggerManager:
|
||||
user_id: str,
|
||||
provider_id: TriggerProviderID,
|
||||
endpoint: str,
|
||||
parameters: dict,
|
||||
credentials: dict,
|
||||
parameters: Mapping[str, Any],
|
||||
credentials: Mapping[str, str],
|
||||
) -> Subscription:
|
||||
"""
|
||||
Subscribe to a trigger (e.g., register webhook)
|
||||
@ -197,7 +199,7 @@ class TriggerManager:
|
||||
user_id: str,
|
||||
provider_id: TriggerProviderID,
|
||||
subscription: Subscription,
|
||||
credentials: dict,
|
||||
credentials: Mapping[str, str],
|
||||
) -> Unsubscription:
|
||||
"""
|
||||
Unsubscribe from a trigger
|
||||
@ -213,7 +215,7 @@ class TriggerManager:
|
||||
return provider.unsubscribe_trigger(user_id=user_id, subscription=subscription, credentials=credentials)
|
||||
|
||||
@classmethod
|
||||
def get_provider_subscription_schema(cls, tenant_id: str, provider_id: TriggerProviderID) -> list[ProviderConfig]:
|
||||
def get_provider_subscription_schema(cls, tenant_id: str, provider_id: TriggerProviderID) -> SubscriptionSchema:
|
||||
"""
|
||||
Get provider subscription schema
|
||||
|
||||
@ -228,9 +230,8 @@ class TriggerManager:
|
||||
cls,
|
||||
tenant_id: str,
|
||||
provider_id: TriggerProviderID,
|
||||
trigger_name: str,
|
||||
subscription: Subscription,
|
||||
credentials: dict,
|
||||
credentials: Mapping[str, str],
|
||||
) -> Subscription:
|
||||
"""
|
||||
Refresh a trigger subscription
|
||||
@ -242,7 +243,7 @@ class TriggerManager:
|
||||
:param credentials: Provider credentials
|
||||
:return: Refreshed subscription result
|
||||
"""
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).refresh_trigger(trigger_name, subscription, credentials)
|
||||
return cls.get_trigger_provider(tenant_id, provider_id).refresh_trigger(subscription, credentials)
|
||||
|
||||
|
||||
# Export
|
||||
|
||||
5
api/core/trigger/utils/endpoint.py
Normal file
5
api/core/trigger/utils/endpoint.py
Normal file
@ -0,0 +1,5 @@
|
||||
from configs import dify_config
|
||||
|
||||
|
||||
def parse_endpoint_id(endpoint_id: str) -> str:
|
||||
return f"{dify_config.CONSOLE_API_URL}/console/api/trigger/endpoint/{endpoint_id}"
|
||||
Reference in New Issue
Block a user