Skip to content

Commit f42a68e

Browse files
feat: Add support for notification tokens in PushNotificationSender
1 parent 7c46e70 commit f42a68e

File tree

2 files changed

+41
-8
lines changed

2 files changed

+41
-8
lines changed

src/a2a/server/tasks/base_push_notification_sender.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,16 @@
99
from a2a.server.tasks.push_notification_sender import PushNotificationSender
1010
from a2a.types import PushNotificationConfig, Task
1111

12-
1312
logger = logging.getLogger(__name__)
1413

1514

1615
class BasePushNotificationSender(PushNotificationSender):
1716
"""Base implementation of PushNotificationSender interface."""
1817

1918
def __init__(
20-
self,
21-
httpx_client: httpx.AsyncClient,
22-
config_store: PushNotificationConfigStore,
19+
self,
20+
httpx_client: httpx.AsyncClient,
21+
config_store: PushNotificationConfigStore,
2322
) -> None:
2423
"""Initializes the BasePushNotificationSender.
2524
@@ -48,12 +47,17 @@ async def send_notification(self, task: Task) -> None:
4847
)
4948

5049
async def _dispatch_notification(
51-
self, task: Task, push_info: PushNotificationConfig
50+
self, task: Task, push_info: PushNotificationConfig
5251
) -> bool:
5352
url = push_info.url
5453
try:
54+
headers = None
55+
if push_info.token:
56+
headers = {'X-A2A-Notification-Token': push_info.token}
5557
response = await self._client.post(
56-
url, json=task.model_dump(mode='json', exclude_none=True)
58+
url,
59+
json=task.model_dump(mode='json', exclude_none=True),
60+
headers=headers
5761
)
5862
response.raise_for_status()
5963
logger.info(

tests/server/tasks/test_inmemory_push_notifications.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ def create_sample_task(task_id='task123', status_state=TaskState.completed):
2626

2727

2828
def create_sample_push_config(
29-
url='http://example.com/callback', config_id='cfg1'
29+
url='http://example.com/callback', config_id='cfg1', token=None
3030
):
31-
return PushNotificationConfig(id=config_id, url=url)
31+
return PushNotificationConfig(id=config_id, url=url, token=token)
3232

3333

3434
class TestInMemoryPushNotifier(unittest.IsolatedAsyncioTestCase):
@@ -158,6 +158,35 @@ async def test_send_notification_success(self):
158158
) # auth is not passed by current implementation
159159
mock_response.raise_for_status.assert_called_once()
160160

161+
async def test_send_notification_with_token_success(self):
162+
task_id = 'task_send_success'
163+
task_data = create_sample_task(task_id=task_id)
164+
config = create_sample_push_config(url='http://notify.me/here', token='unique_token')
165+
await self.config_store.set_info(task_id, config)
166+
167+
# Mock the post call to simulate success
168+
mock_response = AsyncMock(spec=httpx.Response)
169+
mock_response.status_code = 200
170+
self.mock_httpx_client.post.return_value = mock_response
171+
172+
await self.notifier.send_notification(task_data) # Pass only task_data
173+
174+
self.mock_httpx_client.post.assert_awaited_once()
175+
called_args, called_kwargs = self.mock_httpx_client.post.call_args
176+
self.assertEqual(called_args[0], config.url)
177+
self.assertEqual(
178+
called_kwargs['json'],
179+
task_data.model_dump(mode='json', exclude_none=True),
180+
)
181+
self.assertEqual(
182+
called_kwargs['headers'],
183+
{"X-A2A-Notification-Token": "unique_token"},
184+
)
185+
self.assertNotIn(
186+
'auth', called_kwargs
187+
) # auth is not passed by current implementation
188+
mock_response.raise_for_status.assert_called_once()
189+
161190
async def test_send_notification_no_config(self):
162191
task_id = 'task_send_no_config'
163192
task_data = create_sample_task(task_id=task_id)

0 commit comments

Comments
 (0)