feat(trigger): enhance plugin and trigger integration with updated naming conventions

- Refactored `PluginFetchDynamicSelectOptionsApi` to replace the `extra` argument with `credential_id`, improving clarity in dynamic option fetching.
- Updated `ProviderConfigEncrypter` to rename `mask_tool_credentials` to `mask_credentials` for consistency, and added a new method to maintain backward compatibility.
- Enhanced `PluginParameterService` to utilize `credential_id` for fetching subscriptions, improving the handling of trigger credentials.
- Adjusted various components and types in the frontend to replace `tool_name` with `trigger_name`, ensuring consistency across the application.
- Introduced `multiple` property in `TriggerParameter` to support multi-select functionality.

These changes improve the integration of triggers and plugins, enhance code clarity, and align naming conventions across the codebase.
This commit is contained in:
Harry
2025-09-08 23:14:50 +08:00
parent 01b2f9cff6
commit 2a3ce6baa9
40 changed files with 734 additions and 163 deletions

View File

@ -516,20 +516,20 @@ class PluginFetchDynamicSelectOptionsApi(Resource):
parser.add_argument("provider", type=str, required=True, location="args")
parser.add_argument("action", type=str, required=True, location="args")
parser.add_argument("parameter", type=str, required=True, location="args")
parser.add_argument("extra", type=dict, required=False, location="args")
parser.add_argument("credential_id", type=str, required=False, location="args")
parser.add_argument("provider_type", type=str, required=True, location="args")
args = parser.parse_args()
try:
options = PluginParameterService.get_dynamic_select_options(
tenant_id,
user_id,
args["plugin_id"],
args["provider"],
args["action"],
args["parameter"],
args["extra"],
args["provider_type"],
tenant_id=tenant_id,
user_id=user_id,
plugin_id=args["plugin_id"],
provider=args["provider"],
action=args["action"],
parameter=args["parameter"],
credential_id=args["credential_id"],
provider_type=args["provider_type"],
)
except PluginDaemonClientSideError as e:
raise ValueError(e)

View File

@ -66,9 +66,9 @@ class ProviderConfigEncrypter:
return data
def mask_tool_credentials(self, data: dict[str, Any]) -> dict[str, Any]:
def mask_credentials(self, data: dict[str, Any]) -> dict[str, Any]:
"""
mask tool credentials
mask credentials
return a deep copy of credentials with masked values
"""
@ -91,6 +91,10 @@ class ProviderConfigEncrypter:
return data
def mask_tool_credentials(self, data: dict[str, Any]) -> dict[str, Any]:
return self.mask_credentials(data)
def decrypt(self, data: dict[str, str]) -> dict[str, Any]:
"""
decrypt tool credentials with tenant id

View File

@ -27,9 +27,9 @@ class PluginTriggerManager(BasePluginClient):
def transformer(json_response: dict[str, Any]) -> dict:
for provider in json_response.get("data", []):
declaration = provider.get("declaration", {}) or {}
provider_name = declaration.get("identity", {}).get("name")
provider_id = provider.get("plugin_id") + "/" + provider.get("provider")
for trigger in declaration.get("triggers", []):
trigger["identity"]["provider"] = provider_name
trigger["identity"]["provider"] = provider_id
return json_response
@ -42,10 +42,11 @@ class PluginTriggerManager(BasePluginClient):
)
for provider in response:
provider.declaration.identity.name = str(provider.provider)
provider.declaration.identity.name = f"{provider.plugin_id}/{provider.declaration.identity.name}"
# override the provider name for each trigger to plugin_id/provider_name
for trigger in provider.declaration.triggers:
trigger.identity.provider = str(provider.provider)
trigger.identity.provider = provider.declaration.identity.name
return response

View File

@ -40,6 +40,10 @@ class TriggerParameter(BaseModel):
template: Optional[PluginParameterTemplate] = Field(default=None, description="The template of the parameter")
scope: Optional[str] = None
required: Optional[bool] = False
multiple: bool | None = Field(
default=False,
description="Whether the parameter is multiple select, only valid for select or dynamic-select type",
)
default: Union[int, float, str, list, None] = None
min: Union[float, int, None] = None
max: Union[float, int, None] = None

View File

@ -9,9 +9,13 @@ from core.plugin.entities.plugin_daemon import CredentialType
from core.plugin.impl.dynamic_select import DynamicSelectClient
from core.tools.tool_manager import ToolManager
from core.tools.utils.encryption import create_tool_provider_encrypter
from core.trigger.entities.api_entities import TriggerProviderSubscriptionApiEntity
from core.trigger.entities.entities import SubscriptionBuilder
from core.trigger.trigger_manager import TriggerManager
from extensions.ext_database import db
from models.tools import BuiltinToolProvider
from services.trigger.trigger_provider_service import TriggerProviderService
from services.trigger.trigger_subscription_builder_service import TriggerSubscriptionBuilderService
class PluginParameterService:
@ -23,7 +27,7 @@ class PluginParameterService:
provider: str,
action: str,
parameter: str,
extra: dict | None,
credential_id: str | None,
provider_type: Literal["tool", "trigger"],
) -> Sequence[PluginParameterOption]:
"""
@ -37,7 +41,7 @@ class PluginParameterService:
parameter: The parameter name.
"""
credentials: Mapping[str, Any] = {}
credential_type: str = CredentialType.API_KEY.value
credential_type: str = CredentialType.UNAUTHORIZED.value
match provider_type:
case "tool":
provider_controller = ToolManager.get_builtin_provider(provider, tenant_id)
@ -53,8 +57,7 @@ class PluginParameterService:
else:
# fetch credentials from db
with Session(db.engine) as session:
if extra and "credential_id" in extra:
credential_id = extra["credential_id"]
if credential_id:
db_record = (
session.query(BuiltinToolProvider)
.where(
@ -82,7 +85,21 @@ class PluginParameterService:
credential_type = db_record.credential_type
case "trigger":
provider_controller = TriggerManager.get_trigger_provider(tenant_id, TriggerProviderID(provider))
if credential_id:
subscription: TriggerProviderSubscriptionApiEntity | SubscriptionBuilder | None = (
TriggerSubscriptionBuilderService.get_subscription_builder(credential_id)
or TriggerProviderService.get_subscription_by_id(tenant_id, credential_id)
)
else:
subscription: TriggerProviderSubscriptionApiEntity | SubscriptionBuilder | None = (
TriggerProviderService.get_subscription_by_id(tenant_id)
)
if subscription is None:
raise ValueError(f"Subscription {credential_id} not found")
credentials = subscription.credentials
credential_type = subscription.credential_type or CredentialType.UNAUTHORIZED
case _:
raise ValueError(f"Invalid provider type: {provider_type}")

View File

@ -69,7 +69,7 @@ class TriggerProviderService:
controller=provider_controller,
subscription=subscription,
)
subscription.credentials = encrypter.decrypt(subscription.credentials)
subscription.credentials = encrypter.mask_credentials(subscription.credentials)
return subscriptions
@classmethod
@ -165,6 +165,34 @@ class TriggerProviderService:
logger.exception("Failed to add trigger provider")
raise ValueError(str(e))
@classmethod
def get_subscription_by_id(
cls, tenant_id: str, subscription_id: str | None = None
) -> TriggerProviderSubscriptionApiEntity | None:
"""
Get a trigger subscription by the ID.
"""
with Session(db.engine, expire_on_commit=False) as session:
subscription: TriggerSubscription | None = None
if subscription_id:
subscription = (
session.query(TriggerSubscription).filter_by(tenant_id=tenant_id, id=subscription_id).first()
)
else:
subscription = session.query(TriggerSubscription).filter_by(tenant_id=tenant_id).first()
if subscription:
provider_controller = TriggerManager.get_trigger_provider(
tenant_id, TriggerProviderID(subscription.provider_id)
)
encrypter, _ = create_trigger_provider_encrypter_for_subscription(
tenant_id=tenant_id,
controller=provider_controller,
subscription=subscription,
)
subscription.credentials = encrypter.decrypt(subscription.credentials)
return subscription.to_api_entity()
return None
@classmethod
def delete_trigger_provider(cls, session: Session, tenant_id: str, subscription_id: str):
"""