mirror of
https://github.com/langgenius/dify-plugin-sdks.git
synced 2026-07-22 10:25:23 -04:00
refactor(trigger): remove resubscribe functionality and update subscription handling
- Removed the resubscribe_trigger method and its associated request entity. - Updated subscription parameters to streamline the subscribe and unsubscribe processes. - Enhanced the trigger provider interface to utilize the new subscription schema. - Adjusted GitHub provider implementation to align with the updated subscription structure.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import secrets
|
||||
import time
|
||||
import urllib.parse
|
||||
@@ -12,7 +11,6 @@ from werkzeug import Request, Response
|
||||
|
||||
from dify_plugin.interfaces.trigger import TriggerProvider
|
||||
from dify_plugin.entities.trigger import TriggerEventDispatch, Subscription, Unsubscription
|
||||
from dify_plugin.entities.oauth import ToolOAuthCredentials
|
||||
from dify_plugin.errors.tool import ToolProviderCredentialValidationError, ToolProviderOAuthError
|
||||
from dify_plugin.errors.trigger import SubscriptionError, WebhookValidationError, TriggerDispatchError
|
||||
|
||||
@@ -53,7 +51,6 @@ class GithubProvider(TriggerProvider):
|
||||
access_token = response_json.get("access_token")
|
||||
if not access_token:
|
||||
raise ToolProviderOAuthError(f"Error in GitHub OAuth: {response_json}")
|
||||
|
||||
return {"access_tokens": access_token}
|
||||
|
||||
def _validate_credentials(self, credentials: dict) -> None:
|
||||
@@ -70,12 +67,12 @@ class GithubProvider(TriggerProvider):
|
||||
except Exception as e:
|
||||
raise ToolProviderCredentialValidationError(str(e)) from e
|
||||
|
||||
def _dispatch_event(self, settings: Mapping[str, Any], request: Request) -> TriggerEventDispatch:
|
||||
def _dispatch_event(self, subscription: Subscription, request: Request) -> TriggerEventDispatch:
|
||||
"""
|
||||
Dispatch GitHub webhook events - focusing on issue comment events
|
||||
"""
|
||||
# Verify webhook signature if secret is provided
|
||||
webhook_secret = settings.get("webhook_secret")
|
||||
webhook_secret = subscription.configuration.get("webhook_secret")
|
||||
if webhook_secret:
|
||||
signature = request.headers.get("X-Hub-Signature-256")
|
||||
if not signature:
|
||||
@@ -111,50 +108,46 @@ class GithubProvider(TriggerProvider):
|
||||
else:
|
||||
# For other events, pass them through with prefix
|
||||
return TriggerEventDispatch(event=f"github.{event_type}", response=response)
|
||||
|
||||
|
||||
def _subscribe(self, credentials: Mapping[str, Any], subscription_params: Mapping[str, Any]) -> Subscription:
|
||||
"""
|
||||
Create a GitHub webhook subscription for issue comment events
|
||||
"""
|
||||
# Extract parameters
|
||||
callback_url = subscription_params.get("callback_url")
|
||||
endpoint = subscription_params.get("endpoint")
|
||||
webhook_secret = subscription_params.get("webhook_secret")
|
||||
repository = subscription_params.get("repository") # format: "owner/repo"
|
||||
events = subscription_params.get("events", ["issue_comment", "issues"])
|
||||
|
||||
if not callback_url:
|
||||
raise ValueError("callback_url is required for webhook subscription")
|
||||
|
||||
if not endpoint:
|
||||
raise ValueError("endpoint is required for webhook subscription")
|
||||
if not repository:
|
||||
raise ValueError("repository is required (format: owner/repo)")
|
||||
|
||||
|
||||
# Parse repository owner and name
|
||||
try:
|
||||
owner, repo = repository.split("/")
|
||||
except ValueError:
|
||||
raise ValueError("repository must be in format 'owner/repo'")
|
||||
|
||||
|
||||
# Create webhook using GitHub API
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/hooks"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {credentials.get('access_tokens')}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
}
|
||||
|
||||
|
||||
webhook_data = {
|
||||
"name": "web",
|
||||
"active": True,
|
||||
"events": events,
|
||||
"config": {
|
||||
"url": callback_url,
|
||||
"content_type": "json",
|
||||
"insecure_ssl": "0"
|
||||
}
|
||||
"config": {"url": endpoint, "content_type": "json", "insecure_ssl": "0"},
|
||||
}
|
||||
|
||||
|
||||
# Add secret if provided
|
||||
if webhook_secret:
|
||||
webhook_data["config"]["secret"] = webhook_secret
|
||||
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=webhook_data, headers=headers, timeout=10)
|
||||
if response.status_code == 201:
|
||||
@@ -162,83 +155,78 @@ class GithubProvider(TriggerProvider):
|
||||
# Return subscription with webhook details
|
||||
return Subscription(
|
||||
expire_at=int(time.time()) + 30 * 24 * 60 * 60, # 30 days expiration
|
||||
metadata={
|
||||
endpoint=endpoint,
|
||||
configuration={
|
||||
"external_id": str(webhook["id"]),
|
||||
"webhook_url": webhook["url"],
|
||||
"callback_url": callback_url,
|
||||
"repository": repository,
|
||||
"events": events,
|
||||
"active": webhook["active"]
|
||||
}
|
||||
"webhook_secret": webhook_secret,
|
||||
"active": webhook["active"],
|
||||
},
|
||||
)
|
||||
else:
|
||||
error_msg = response.json().get("message", "Unknown error")
|
||||
raise SubscriptionError(f"Failed to create GitHub webhook: {error_msg}",
|
||||
error_code="WEBHOOK_CREATION_FAILED",
|
||||
external_response=response.json())
|
||||
raise SubscriptionError(
|
||||
f"Failed to create GitHub webhook: {error_msg}",
|
||||
error_code="WEBHOOK_CREATION_FAILED",
|
||||
external_response=response.json(),
|
||||
)
|
||||
except requests.RequestException as e:
|
||||
raise SubscriptionError(f"Network error while creating webhook: {e}",
|
||||
error_code="NETWORK_ERROR")
|
||||
|
||||
def _unsubscribe(self, subscription: Subscription, credentials: Mapping[str, Any], settings: Mapping[str, Any]) -> Unsubscription:
|
||||
raise SubscriptionError(f"Network error while creating webhook: {e}", error_code="NETWORK_ERROR")
|
||||
|
||||
def _unsubscribe(self, subscription: Subscription, credentials: Mapping[str, Any]) -> Unsubscription:
|
||||
"""
|
||||
Remove a GitHub webhook subscription
|
||||
"""
|
||||
# Extract webhook details from metadata
|
||||
external_id = subscription.metadata.get("external_id")
|
||||
repository = subscription.metadata.get("repository")
|
||||
|
||||
# Extract webhook details from data
|
||||
external_id = subscription.configuration.get("external_id")
|
||||
repository = subscription.configuration.get("repository")
|
||||
|
||||
if not external_id or not repository:
|
||||
return Unsubscription(
|
||||
success=False,
|
||||
message="Missing webhook ID or repository information",
|
||||
error_code="MISSING_METADATA"
|
||||
success=False, message="Missing webhook ID or repository information", error_code="MISSING_DATA"
|
||||
)
|
||||
|
||||
|
||||
# Parse repository
|
||||
try:
|
||||
owner, repo = repository.split("/")
|
||||
except ValueError:
|
||||
return Unsubscription(
|
||||
success=False,
|
||||
message="Invalid repository format in metadata",
|
||||
error_code="INVALID_REPOSITORY"
|
||||
success=False, message="Invalid repository format in data", error_code="INVALID_REPOSITORY"
|
||||
)
|
||||
|
||||
|
||||
# Delete webhook using GitHub API
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/hooks/{external_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {credentials.get('access_tokens')}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
response = requests.delete(url, headers=headers, timeout=10)
|
||||
if response.status_code == 204:
|
||||
return Unsubscription(
|
||||
success=True,
|
||||
message=f"Successfully removed webhook {external_id} from {repository}"
|
||||
success=True, message=f"Successfully removed webhook {external_id} from {repository}"
|
||||
)
|
||||
elif response.status_code == 404:
|
||||
return Unsubscription(
|
||||
success=False,
|
||||
message=f"Webhook {external_id} not found in repository {repository}",
|
||||
error_code="WEBHOOK_NOT_FOUND"
|
||||
error_code="WEBHOOK_NOT_FOUND",
|
||||
)
|
||||
else:
|
||||
return Unsubscription(
|
||||
success=False,
|
||||
message=f"Failed to delete webhook: {response.json().get('message', 'Unknown error')}",
|
||||
error_code="API_ERROR",
|
||||
external_response=response.json()
|
||||
external_response=response.json(),
|
||||
)
|
||||
except requests.RequestException as e:
|
||||
return Unsubscription(
|
||||
success=False,
|
||||
message=f"Network error while deleting webhook: {e}",
|
||||
error_code="NETWORK_ERROR"
|
||||
success=False, message=f"Network error while deleting webhook: {e}", error_code="NETWORK_ERROR"
|
||||
)
|
||||
|
||||
|
||||
def _refresh(self, subscription: Subscription, credentials: Mapping[str, Any]) -> Subscription:
|
||||
"""
|
||||
Refresh a GitHub webhook subscription (extend expiration)
|
||||
@@ -248,31 +236,6 @@ class GithubProvider(TriggerProvider):
|
||||
# GitHub webhooks don't have built-in expiration
|
||||
return Subscription(
|
||||
expire_at=int(time.time()) + 30 * 24 * 60 * 60, # Extend by 30 days
|
||||
metadata=subscription.metadata # Keep the same metadata
|
||||
endpoint=subscription.endpoint, # Keep the same endpoint
|
||||
configuration=subscription.configuration, # Keep the same data
|
||||
)
|
||||
|
||||
def _resubscribe(self, subscription: Subscription, credentials: Mapping[str, Any], settings: Mapping[str, Any]) -> Subscription:
|
||||
"""
|
||||
Update a GitHub webhook subscription with new settings
|
||||
"""
|
||||
# First unsubscribe the old webhook
|
||||
unsubscribe_result = self._unsubscribe(subscription, credentials, settings)
|
||||
if not unsubscribe_result.success:
|
||||
# If we can't delete the old one, still try to create a new one
|
||||
# This handles cases where the old webhook was already deleted
|
||||
pass
|
||||
|
||||
# Extract new subscription parameters from settings
|
||||
repository = subscription.metadata.get("repository")
|
||||
callback_url = subscription.metadata.get("callback_url")
|
||||
|
||||
# Create new subscription params with updated settings
|
||||
new_subscription_params = {
|
||||
"callback_url": callback_url,
|
||||
"repository": repository,
|
||||
"webhook_secret": settings.get("webhook_secret"),
|
||||
"events": settings.get("events", ["issue_comment", "issues"])
|
||||
}
|
||||
|
||||
# Create new webhook with updated settings
|
||||
return self._subscribe(credentials, new_subscription_params)
|
||||
|
||||
@@ -56,57 +56,59 @@ oauth_schema:
|
||||
en_US: "Access Token"
|
||||
|
||||
subscription_schema:
|
||||
- name: "repository"
|
||||
type: "text-input"
|
||||
required: true
|
||||
label:
|
||||
zh_Hans: "仓库"
|
||||
en_US: "Repository"
|
||||
placeholder:
|
||||
zh_Hans: "owner/repo"
|
||||
en_US: "owner/repo"
|
||||
help:
|
||||
en_US: "GitHub repository in format owner/repo (e.g., microsoft/vscode)"
|
||||
pt_BR: "Repositório do GitHub no formato owner/repo (ex: microsoft/vscode)"
|
||||
zh_Hans: "GitHub 仓库,格式为 owner/repo(例如:microsoft/vscode)"
|
||||
- name: "events"
|
||||
type: "select"
|
||||
required: true
|
||||
default: ["issue_comment", "issues"]
|
||||
options:
|
||||
- value: "issue_comment"
|
||||
label:
|
||||
en_US: "Issue Comments"
|
||||
zh_Hans: "Issue 评论"
|
||||
- value: "issues"
|
||||
label:
|
||||
en_US: "Issues"
|
||||
zh_Hans: "Issues"
|
||||
- value: "pull_request"
|
||||
label:
|
||||
en_US: "Pull Requests"
|
||||
zh_Hans: "Pull Requests"
|
||||
- value: "push"
|
||||
label:
|
||||
en_US: "Push"
|
||||
zh_Hans: "推送"
|
||||
label:
|
||||
zh_Hans: "监听事件"
|
||||
en_US: "Events to Listen"
|
||||
help:
|
||||
en_US: "Select which GitHub events to subscribe to"
|
||||
pt_BR: "Selecione quais eventos do GitHub inscrever-se"
|
||||
zh_Hans: "选择要订阅的 GitHub 事件"
|
||||
- name: "webhook_secret"
|
||||
type: "secret-input"
|
||||
required: false
|
||||
label:
|
||||
zh_Hans: "Webhook Secret"
|
||||
en_US: "Webhook Secret"
|
||||
help:
|
||||
en_US: "Optional webhook secret for validating GitHub webhook requests"
|
||||
pt_BR: "Segredo opcional do webhook para validar solicitações do webhook do GitHub"
|
||||
zh_Hans: "可选的用于验证 GitHub webhook 请求的 webhook 密钥"
|
||||
parameters_schema:
|
||||
- name: "repository"
|
||||
type: "text-input"
|
||||
required: true
|
||||
label:
|
||||
zh_Hans: "仓库"
|
||||
en_US: "Repository"
|
||||
placeholder:
|
||||
zh_Hans: "owner/repo"
|
||||
en_US: "owner/repo"
|
||||
help:
|
||||
en_US: "GitHub repository in format owner/repo (e.g., microsoft/vscode)"
|
||||
pt_BR: "Repositório do GitHub no formato owner/repo (ex: microsoft/vscode)"
|
||||
zh_Hans: "GitHub 仓库,格式为 owner/repo(例如:microsoft/vscode)"
|
||||
- name: "events"
|
||||
type: "select"
|
||||
required: true
|
||||
default: ["issue_comment", "issues"]
|
||||
options:
|
||||
- value: "issue_comment"
|
||||
label:
|
||||
en_US: "Issue Comments"
|
||||
zh_Hans: "Issue 评论"
|
||||
- value: "issues"
|
||||
label:
|
||||
en_US: "Issues"
|
||||
zh_Hans: "Issues"
|
||||
- value: "pull_request"
|
||||
label:
|
||||
en_US: "Pull Requests"
|
||||
zh_Hans: "Pull Requests"
|
||||
- value: "push"
|
||||
label:
|
||||
en_US: "Push"
|
||||
zh_Hans: "推送"
|
||||
label:
|
||||
zh_Hans: "监听事件"
|
||||
en_US: "Events to Listen"
|
||||
help:
|
||||
en_US: "Select which GitHub events to subscribe to"
|
||||
pt_BR: "Selecione quais eventos do GitHub inscrever-se"
|
||||
zh_Hans: "选择要订阅的 GitHub 事件"
|
||||
properties_schema:
|
||||
- name: "webhook_secret"
|
||||
type: "secret-input"
|
||||
required: false
|
||||
label:
|
||||
zh_Hans: "Webhook Secret"
|
||||
en_US: "Webhook Secret"
|
||||
help:
|
||||
en_US: "Optional webhook secret for validating GitHub webhook requests"
|
||||
pt_BR: "Segredo opcional do webhook para validar solicitações do webhook do GitHub"
|
||||
zh_Hans: "可选的用于验证 GitHub webhook 请求的 webhook 密钥"
|
||||
|
||||
triggers:
|
||||
- triggers/issue_comment.yaml
|
||||
|
||||
Reference in New Issue
Block a user