mirror of
https://github.com/langgenius/dify.git
synced 2026-05-01 07:58:02 +08:00
feat(trigger): add plugin trigger workflow support and refactor trigger system
- Add new workflow plugin trigger service for managing plugin-based triggers - Implement trigger provider encryption utilities for secure credential storage - Add custom trigger errors module for better error handling - Refactor trigger provider and manager classes for improved plugin integration - Update API endpoints to support plugin trigger workflows - Add database migration for plugin trigger workflow support 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -18,7 +18,7 @@ from models.workflow import AppTrigger, AppTriggerStatus, WorkflowWebhookTrigger
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from models.workflow import WorkflowPluginTrigger
|
||||
from services.workflow_plugin_trigger_service import WorkflowPluginTriggerService
|
||||
|
||||
|
||||
class PluginTriggerApi(Resource):
|
||||
@ -34,54 +34,21 @@ class PluginTriggerApi(Resource):
|
||||
parser.add_argument("node_id", type=str, required=True, help="Node ID is required")
|
||||
parser.add_argument("provider_id", type=str, required=True, help="Provider ID is required")
|
||||
parser.add_argument("trigger_name", type=str, required=True, help="Trigger name is required")
|
||||
parser.add_argument(
|
||||
"triggered_by",
|
||||
type=str,
|
||||
required=False,
|
||||
default="production",
|
||||
choices=["debugger", "production"],
|
||||
help="triggered_by must be debugger or production",
|
||||
)
|
||||
parser.add_argument("subscription_id", type=str, required=True, help="Subscription ID is required")
|
||||
args = parser.parse_args()
|
||||
|
||||
# The role of the current user in the ta table must be admin, owner, or editor
|
||||
if not current_user.is_editor:
|
||||
raise Forbidden()
|
||||
|
||||
node_id = args["node_id"]
|
||||
provider_id = args["provider_id"]
|
||||
trigger_name = args["trigger_name"]
|
||||
triggered_by = args["triggered_by"]
|
||||
|
||||
# Create trigger_id from provider_id and trigger_name
|
||||
trigger_id = f"{provider_id}:{trigger_name}"
|
||||
|
||||
with Session(db.engine) as session:
|
||||
# Check if plugin trigger already exists for this app, node, and environment
|
||||
existing_trigger = session.scalar(
|
||||
select(WorkflowPluginTrigger).where(
|
||||
WorkflowPluginTrigger.app_id == app_model.id,
|
||||
WorkflowPluginTrigger.node_id == node_id,
|
||||
WorkflowPluginTrigger.triggered_by == triggered_by,
|
||||
)
|
||||
)
|
||||
|
||||
if existing_trigger:
|
||||
raise BadRequest("Plugin trigger already exists for this node and environment")
|
||||
|
||||
# Create new plugin trigger
|
||||
plugin_trigger = WorkflowPluginTrigger(
|
||||
app_id=app_model.id,
|
||||
node_id=node_id,
|
||||
tenant_id=current_user.current_tenant_id,
|
||||
provider_id=provider_id,
|
||||
trigger_id=trigger_id,
|
||||
triggered_by=triggered_by,
|
||||
)
|
||||
|
||||
session.add(plugin_trigger)
|
||||
session.commit()
|
||||
session.refresh(plugin_trigger)
|
||||
plugin_trigger = WorkflowPluginTriggerService.create_plugin_trigger(
|
||||
app_id=app_model.id,
|
||||
tenant_id=current_user.current_tenant_id,
|
||||
node_id=args["node_id"],
|
||||
provider_id=args["provider_id"],
|
||||
trigger_name=args["trigger_name"],
|
||||
subscription_id=args["subscription_id"],
|
||||
)
|
||||
|
||||
return plugin_trigger
|
||||
|
||||
@ -93,33 +60,14 @@ class PluginTriggerApi(Resource):
|
||||
"""Get plugin trigger"""
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument("node_id", type=str, required=True, help="Node ID is required")
|
||||
parser.add_argument(
|
||||
"triggered_by",
|
||||
type=str,
|
||||
required=False,
|
||||
default="production",
|
||||
choices=["debugger", "production"],
|
||||
help="triggered_by must be debugger or production",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
node_id = args["node_id"]
|
||||
triggered_by = args["triggered_by"]
|
||||
plugin_trigger = WorkflowPluginTriggerService.get_plugin_trigger(
|
||||
app_id=app_model.id,
|
||||
node_id=args["node_id"],
|
||||
)
|
||||
|
||||
with Session(db.engine) as session:
|
||||
# Find plugin trigger
|
||||
plugin_trigger = session.scalar(
|
||||
select(WorkflowPluginTrigger).where(
|
||||
WorkflowPluginTrigger.app_id == app_model.id,
|
||||
WorkflowPluginTrigger.node_id == node_id,
|
||||
WorkflowPluginTrigger.triggered_by == triggered_by,
|
||||
WorkflowPluginTrigger.tenant_id == current_user.current_tenant_id,
|
||||
)
|
||||
)
|
||||
|
||||
if not plugin_trigger:
|
||||
raise NotFound("Plugin trigger not found")
|
||||
return plugin_trigger
|
||||
return plugin_trigger
|
||||
|
||||
@setup_required
|
||||
@login_required
|
||||
@ -131,51 +79,22 @@ class PluginTriggerApi(Resource):
|
||||
parser.add_argument("node_id", type=str, required=True, help="Node ID is required")
|
||||
parser.add_argument("provider_id", type=str, required=False, help="Provider ID")
|
||||
parser.add_argument("trigger_name", type=str, required=False, help="Trigger name")
|
||||
parser.add_argument(
|
||||
"triggered_by",
|
||||
type=str,
|
||||
required=False,
|
||||
default="production",
|
||||
choices=["debugger", "production"],
|
||||
help="triggered_by must be debugger or production",
|
||||
)
|
||||
parser.add_argument("subscription_id", type=str, required=False, help="Subscription ID")
|
||||
args = parser.parse_args()
|
||||
|
||||
# The role of the current user in the ta table must be admin, owner, or editor
|
||||
if not current_user.is_editor:
|
||||
raise Forbidden()
|
||||
|
||||
node_id = args["node_id"]
|
||||
triggered_by = args["triggered_by"]
|
||||
plugin_trigger = WorkflowPluginTriggerService.update_plugin_trigger(
|
||||
app_id=app_model.id,
|
||||
node_id=args["node_id"],
|
||||
provider_id=args.get("provider_id"),
|
||||
trigger_name=args.get("trigger_name"),
|
||||
subscription_id=args.get("subscription_id"),
|
||||
)
|
||||
|
||||
with Session(db.engine) as session:
|
||||
# Find plugin trigger
|
||||
plugin_trigger = session.scalar(
|
||||
select(WorkflowPluginTrigger).where(
|
||||
WorkflowPluginTrigger.app_id == app_model.id,
|
||||
WorkflowPluginTrigger.node_id == node_id,
|
||||
WorkflowPluginTrigger.triggered_by == triggered_by,
|
||||
WorkflowPluginTrigger.tenant_id == current_user.current_tenant_id,
|
||||
)
|
||||
)
|
||||
|
||||
if not plugin_trigger:
|
||||
raise NotFound("Plugin trigger not found")
|
||||
|
||||
# Update fields if provided
|
||||
if args.get("provider_id"):
|
||||
plugin_trigger.provider_id = args["provider_id"]
|
||||
|
||||
if args.get("trigger_name"):
|
||||
# Update trigger_id if provider_id or trigger_name changed
|
||||
provider_id = args.get("provider_id") or plugin_trigger.provider_id
|
||||
trigger_name = args["trigger_name"]
|
||||
plugin_trigger.trigger_id = f"{provider_id}:{trigger_name}"
|
||||
|
||||
session.commit()
|
||||
session.refresh(plugin_trigger)
|
||||
|
||||
return plugin_trigger
|
||||
return plugin_trigger
|
||||
|
||||
@setup_required
|
||||
@login_required
|
||||
@ -185,39 +104,16 @@ class PluginTriggerApi(Resource):
|
||||
"""Delete plugin trigger"""
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument("node_id", type=str, required=True, help="Node ID is required")
|
||||
parser.add_argument(
|
||||
"triggered_by",
|
||||
type=str,
|
||||
required=False,
|
||||
default="production",
|
||||
choices=["debugger", "production"],
|
||||
help="triggered_by must be debugger or production",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# The role of the current user in the ta table must be admin, owner, or editor
|
||||
if not current_user.is_editor:
|
||||
raise Forbidden()
|
||||
|
||||
node_id = args["node_id"]
|
||||
triggered_by = args["triggered_by"]
|
||||
|
||||
with Session(db.engine) as session:
|
||||
# Find plugin trigger
|
||||
plugin_trigger = session.scalar(
|
||||
select(WorkflowPluginTrigger).where(
|
||||
WorkflowPluginTrigger.app_id == app_model.id,
|
||||
WorkflowPluginTrigger.node_id == node_id,
|
||||
WorkflowPluginTrigger.triggered_by == triggered_by,
|
||||
WorkflowPluginTrigger.tenant_id == current_user.current_tenant_id,
|
||||
)
|
||||
)
|
||||
|
||||
if not plugin_trigger:
|
||||
raise NotFound("Plugin trigger not found")
|
||||
|
||||
session.delete(plugin_trigger)
|
||||
session.commit()
|
||||
WorkflowPluginTriggerService.delete_plugin_trigger(
|
||||
app_id=app_model.id,
|
||||
node_id=args["node_id"],
|
||||
)
|
||||
|
||||
return {"result": "success"}, 204
|
||||
|
||||
|
||||
@ -117,6 +117,43 @@ class TriggerSubscriptionBuilderVerifyApi(Resource):
|
||||
raise
|
||||
|
||||
|
||||
class TriggerSubscriptionBuilderUpdateApi(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
def post(self, provider, subscription_builder_id):
|
||||
"""Update a subscription instance for a trigger provider"""
|
||||
user = current_user
|
||||
assert isinstance(user, Account)
|
||||
assert user.current_tenant_id is not None
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
# The name of the subscription builder
|
||||
parser.add_argument("name", type=str, required=False, nullable=True, location="json")
|
||||
# The parameters of the subscription builder
|
||||
parser.add_argument("parameters", type=dict, required=False, nullable=True, location="json")
|
||||
# The properties of the subscription builder
|
||||
parser.add_argument("properties", type=dict, required=False, nullable=True, location="json")
|
||||
# The credentials of the subscription builder
|
||||
parser.add_argument("credentials", type=dict, required=False, nullable=True, location="json")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
return jsonable_encoder(
|
||||
TriggerSubscriptionBuilderService.update_trigger_subscription_builder(
|
||||
tenant_id=user.current_tenant_id,
|
||||
provider_id=TriggerProviderID(provider),
|
||||
subscription_builder_id=subscription_builder_id,
|
||||
name=args.get("name", None),
|
||||
parameters=args.get("parameters", None),
|
||||
properties=args.get("properties", None),
|
||||
credentials=args.get("credentials", None),
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("Error updating provider credential", exc_info=e)
|
||||
raise
|
||||
|
||||
|
||||
class TriggerSubscriptionBuilderBuildApi(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@ -216,9 +253,26 @@ class TriggerOAuthAuthorizeApi(Resource):
|
||||
redirect_uri=redirect_uri,
|
||||
system_credentials=oauth_client_params,
|
||||
)
|
||||
# Create subscription builder
|
||||
subscription_builder = TriggerSubscriptionBuilderService.create_trigger_subscription_builder(
|
||||
tenant_id=tenant_id,
|
||||
user_id=user.id,
|
||||
provider_id=provider_id,
|
||||
credentials={},
|
||||
credential_type=CredentialType.OAUTH2,
|
||||
credential_expires_at=0,
|
||||
expires_at=0,
|
||||
)
|
||||
|
||||
# Create response with cookie
|
||||
response = make_response(jsonable_encoder(authorization_url_response))
|
||||
response = make_response(
|
||||
jsonable_encoder(
|
||||
{
|
||||
"authorization_url": authorization_url_response,
|
||||
"subscription_builder": subscription_builder,
|
||||
}
|
||||
)
|
||||
)
|
||||
response.set_cookie(
|
||||
"context_id",
|
||||
context_id,
|
||||
@ -410,6 +464,10 @@ api.add_resource(
|
||||
TriggerSubscriptionBuilderCreateApi,
|
||||
"/workspaces/current/trigger-provider/<path:provider>/subscriptions/builder/create",
|
||||
)
|
||||
api.add_resource(
|
||||
TriggerSubscriptionBuilderUpdateApi,
|
||||
"/workspaces/current/trigger-provider/<path:provider>/subscriptions/builder/update/<path:subscription_builder_id>",
|
||||
)
|
||||
api.add_resource(
|
||||
TriggerSubscriptionBuilderVerifyApi,
|
||||
"/workspaces/current/trigger-provider/<path:provider>/subscriptions/builder/verify/<path:subscription_builder_id>",
|
||||
|
||||
Reference in New Issue
Block a user