chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
"""
|
||||
Webhook event types for typed event handling.
|
||||
|
||||
This module provides TypedDict definitions for specific webhook event types,
|
||||
enabling type-safe handling of events like connection expiration.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
from enum import Enum
|
||||
|
||||
import typing_extensions as te
|
||||
|
||||
|
||||
class WebhookEventType(str, Enum):
|
||||
"""Known webhook event types."""
|
||||
|
||||
CONNECTION_EXPIRED = "composio.connected_account.expired"
|
||||
TRIGGER_MESSAGE = "composio.trigger.message"
|
||||
|
||||
|
||||
class ConnectionStatusEnum(str, Enum):
|
||||
"""Connection status values."""
|
||||
|
||||
INITIALIZING = "INITIALIZING"
|
||||
INITIATED = "INITIATED"
|
||||
ACTIVE = "ACTIVE"
|
||||
FAILED = "FAILED"
|
||||
EXPIRED = "EXPIRED"
|
||||
INACTIVE = "INACTIVE"
|
||||
REVOKED = "REVOKED"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CONNECTED ACCOUNT DATA (matches GET /api/v3/connected_accounts/{id})
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ConnectedAccountToolkit(te.TypedDict):
|
||||
"""Toolkit information in connected account response."""
|
||||
|
||||
slug: str
|
||||
|
||||
|
||||
class ConnectedAccountAuthConfigDeprecated(te.TypedDict, total=False):
|
||||
"""Deprecated auth config fields."""
|
||||
|
||||
uuid: str
|
||||
|
||||
|
||||
class ConnectedAccountAuthConfig(te.TypedDict, total=False):
|
||||
"""Auth config details in connected account response."""
|
||||
|
||||
id: te.Required[str]
|
||||
auth_scheme: te.Required[str]
|
||||
is_composio_managed: te.Required[bool]
|
||||
is_disabled: te.Required[bool]
|
||||
deprecated: ConnectedAccountAuthConfigDeprecated
|
||||
|
||||
|
||||
class ConnectionStateVal(te.TypedDict, total=False):
|
||||
"""Connection state value - varies by auth scheme."""
|
||||
|
||||
status: str
|
||||
# OAuth2 fields
|
||||
access_token: str
|
||||
refresh_token: t.Optional[str]
|
||||
token_type: str
|
||||
expires_in: t.Union[int, str, None]
|
||||
scope: t.Union[str, t.List[str], None]
|
||||
id_token: str
|
||||
code_verifier: str
|
||||
callback_url: str
|
||||
# OAuth1 fields
|
||||
oauth_token: str
|
||||
oauth_token_secret: str
|
||||
# API Key fields
|
||||
api_key: str
|
||||
generic_api_key: str
|
||||
# Bearer Token fields
|
||||
token: str
|
||||
# Basic auth fields
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class ConnectionState(te.TypedDict):
|
||||
"""Connection state data discriminated by auth scheme."""
|
||||
|
||||
authScheme: str
|
||||
val: ConnectionStateVal
|
||||
|
||||
|
||||
class ConnectedAccountDeprecated(te.TypedDict, total=False):
|
||||
"""Deprecated connected account fields."""
|
||||
|
||||
labels: t.List[str]
|
||||
uuid: str
|
||||
|
||||
|
||||
class SingleConnectedAccountDetailedResponse(te.TypedDict, total=False):
|
||||
"""
|
||||
Connected account data matching GET /api/v3/connected_accounts/{id} response.
|
||||
|
||||
This is used in webhook payloads for connection lifecycle events.
|
||||
It intentionally does NOT reuse composio_client's ConnectedAccountRetrieveResponse
|
||||
because webhook payloads arrive in raw snake_case format, while the SDK client
|
||||
transforms responses to a different shape. This TypedDict validates the raw
|
||||
webhook payload directly without any transformation layer.
|
||||
|
||||
See Also:
|
||||
composio_client.types.connected_account_retrieve_response for the SDK version.
|
||||
"""
|
||||
|
||||
toolkit: te.Required[ConnectedAccountToolkit]
|
||||
auth_config: te.Required[ConnectedAccountAuthConfig]
|
||||
id: te.Required[str]
|
||||
user_id: te.Required[str]
|
||||
status: te.Required[str] # ConnectionStatusEnum value
|
||||
created_at: te.Required[str]
|
||||
updated_at: te.Required[str]
|
||||
state: te.Required[ConnectionState]
|
||||
data: te.Required[t.Dict[str, t.Any]]
|
||||
params: te.Required[t.Dict[str, t.Any]]
|
||||
status_reason: te.Required[t.Optional[str]]
|
||||
is_disabled: te.Required[bool]
|
||||
test_request_endpoint: str
|
||||
deprecated: ConnectedAccountDeprecated
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CONNECTION EXPIRED WEBHOOK EVENT
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class WebhookConnectionMetadata(te.TypedDict):
|
||||
"""Webhook metadata for connection events."""
|
||||
|
||||
project_id: str
|
||||
org_id: str
|
||||
|
||||
|
||||
class ConnectionExpiredEvent(te.TypedDict):
|
||||
"""
|
||||
Connection expired webhook event payload.
|
||||
|
||||
Emitted when a connected account expires due to authentication refresh failure.
|
||||
|
||||
Example:
|
||||
>>> from composio.core.models.webhook_events import is_connection_expired_event
|
||||
>>>
|
||||
>>> def handle_webhook(payload: dict) -> None:
|
||||
... if is_connection_expired_event(payload):
|
||||
... print(f"Connection {payload['data']['id']} expired")
|
||||
... print(f"Toolkit: {payload['data']['toolkit']['slug']}")
|
||||
... print(f"User: {payload['data']['user_id']}")
|
||||
"""
|
||||
|
||||
id: str # Unique message ID (e.g., "msg_847cdfcd-d219-4f18-a6dd-91acd42ca94a")
|
||||
timestamp: str # ISO-8601 timestamp
|
||||
type: t.Literal["composio.connected_account.expired"]
|
||||
data: SingleConnectedAccountDetailedResponse
|
||||
metadata: WebhookConnectionMetadata
|
||||
|
||||
|
||||
# Type alias for non-trigger webhook events with specific typed schemas.
|
||||
# Trigger events (composio.trigger.message) are handled through the
|
||||
# TriggerEvent type in triggers.py via the trigger subscription system.
|
||||
WebhookEvent = t.Union[ConnectionExpiredEvent]
|
||||
|
||||
|
||||
def is_connection_expired_event(
|
||||
payload: t.Dict[str, t.Any],
|
||||
) -> t.TypeGuard[ConnectionExpiredEvent]:
|
||||
"""
|
||||
Check if a webhook payload is a connection expired event.
|
||||
|
||||
:param payload: The webhook payload to check
|
||||
:return: True if this is a connection expired event
|
||||
|
||||
Example:
|
||||
>>> from composio.core.models.webhook_events import is_connection_expired_event
|
||||
>>>
|
||||
>>> if is_connection_expired_event(payload):
|
||||
... handle_connection_expired(payload) # payload is narrowed to ConnectionExpiredEvent
|
||||
"""
|
||||
return (
|
||||
isinstance(payload, dict)
|
||||
and payload.get("type") == WebhookEventType.CONNECTION_EXPIRED.value
|
||||
)
|
||||
Reference in New Issue
Block a user