Coverage for src / lexigram / contracts / webhook / protocols.py: 0%
21 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""Webhook protocols: subscription store, delivery store, delivery service."""
3from __future__ import annotations
5from datetime import datetime
6from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
8if TYPE_CHECKING:
9 from lexigram.contracts.webhook.types import (
10 DeliveryAttempt,
11 DeliveryStatus,
12 WebhookEvent,
13 WebhookSubscription,
14 )
17@runtime_checkable
18class WebhookSubscriptionStoreProtocol(Protocol):
19 """Storage-agnostic CRUD for webhook subscriptions."""
21 async def create(self, subscription: WebhookSubscription) -> None:
22 """Persist a new subscription.
24 Args:
25 subscription: Subscription to store.
26 """
27 ...
29 async def get(self, subscription_id: str) -> WebhookSubscription | None:
30 """Return subscription by ID, or None.
32 Args:
33 subscription_id: UUID to look up.
35 Returns:
36 The subscription if found, else None.
37 """
38 ...
40 async def list(
41 self,
42 *,
43 active_only: bool = True,
44 event_type: str | None = None,
45 tenant_id: str | None = None,
46 limit: int = 100,
47 offset: int = 0,
48 ) -> list[WebhookSubscription]:
49 """List subscriptions matching filters.
51 Args:
52 active_only: Only return active subscriptions.
53 event_type: Filter to subscriptions that handle this event type.
54 tenant_id: Filter by tenant.
55 limit: Maximum results.
56 offset: Results to skip.
58 Returns:
59 Filtered and paginated subscriptions.
60 """
61 ...
63 async def update(self, subscription: WebhookSubscription) -> None:
64 """Update an existing subscription.
66 Args:
67 subscription: Updated subscription data.
68 """
69 ...
71 async def delete(self, subscription_id: str) -> None:
72 """Remove a subscription.
74 Args:
75 subscription_id: UUID to remove.
76 """
77 ...
80@runtime_checkable
81class WebhookDeliveryStoreProtocol(Protocol):
82 """Storage for webhook delivery attempts and dead-letter queue."""
84 async def record_attempt(self, attempt: DeliveryAttempt) -> None:
85 """Persist a delivery attempt record.
87 Args:
88 attempt: Attempt to record.
89 """
90 ...
92 async def get_attempts(
93 self,
94 *,
95 subscription_id: str | None = None,
96 event_id: str | None = None,
97 status: DeliveryStatus | None = None,
98 limit: int = 100,
99 offset: int = 0,
100 ) -> list[DeliveryAttempt]:
101 """Query delivery attempts matching filters, newest-first.
103 Args:
104 subscription_id: Filter by subscription.
105 event_id: Filter by event.
106 status: Filter by delivery status.
107 limit: Maximum results.
108 offset: Results to skip.
110 Returns:
111 Matching attempts in newest-first order.
112 """
113 ...
115 async def get_dead_letters(
116 self,
117 *,
118 subscription_id: str | None = None,
119 limit: int = 100,
120 offset: int = 0,
121 ) -> list[DeliveryAttempt]:
122 """Return attempts in dead-letter status.
124 Args:
125 subscription_id: Optional filter by subscription.
126 limit: Maximum results.
127 offset: Results to skip.
129 Returns:
130 Dead-lettered attempts.
131 """
132 ...
134 async def count_recent_failures(
135 self,
136 subscription_id: str,
137 since: datetime,
138 ) -> int:
139 """Count failed attempts for a subscription since a given time.
141 Args:
142 subscription_id: Subscription to check.
143 since: Count attempts after this timestamp.
145 Returns:
146 Count of failed attempts.
147 """
148 ...
151@runtime_checkable
152class WebhookDeliveryServiceProtocol(Protocol):
153 """High-level protocol for dispatching webhook events."""
155 async def dispatch(self, event: WebhookEvent) -> None:
156 """Deliver an event to all matching active subscriptions.
158 Args:
159 event: Event to deliver.
160 """
161 ...
163 async def redeliver(self, attempt_id: str) -> Any:
164 """Redeliver a failed or dead-lettered attempt.
166 Returns ``Result[None, WebhookError]``. Typed as ``Any`` because
167 ``Result`` lives in ``lexigram`` (not ``lexigram-contracts``).
169 Args:
170 attempt_id: ID of the attempt to redeliver.
172 Returns:
173 Result[None, WebhookError]
174 """
175 ...
178__all__ = [
179 "WebhookDeliveryServiceProtocol",
180 "WebhookDeliveryStoreProtocol",
181 "WebhookSubscriptionStoreProtocol",
182]