192 lines
5.7 KiB
Python
192 lines
5.7 KiB
Python
"""
|
|
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
|
|
)
|