Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/a2a/server/request_handlers/default_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,32 +537,38 @@

Requires a `PushNotifier` to be configured.
"""
if not self._push_config_store:
raise PushNotificationNotSupportedError

task_id = params.task_id
task: Task | None = await self.task_store.get(task_id, context)
if not task:
raise TaskNotFoundError

await self._reject_unsafe_push_url(params.url)

# Stores default an empty id to the task id, but only the in-memory
# store does so on the caller's object. Normalize here so the returned
# config carries the id that was persisted, on every store.
if not params.id:
params.id = task_id

await self._push_config_store.set_info(
task_id,
params,
context,
)

return params

@validate_request_params
@validate(
lambda self: self._agent_card.capabilities.push_notifications,
error_message='Push notifications are not supported by the agent',
error_type=PushNotificationNotSupportedError,
)
async def on_get_task_push_notification_config(
self,

Check notice on line 571 in src/a2a/server/request_handlers/default_request_handler.py

View workflow job for this annotation

GitHub Actions / Lint Code Base

Copy/pasted code

see src/a2a/server/request_handlers/default_request_handler_v2.py (378-408)
params: GetTaskPushNotificationConfigRequest,
context: ServerCallContext,
) -> TaskPushNotificationConfig:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,31 +375,37 @@
params: TaskPushNotificationConfig,
context: ServerCallContext,
) -> TaskPushNotificationConfig:
if not self._push_config_store:
raise PushNotificationNotSupportedError

task_id = params.task_id
task: Task | None = await self.task_store.get(task_id, context)
if not task:
raise TaskNotFoundError

await self._reject_unsafe_push_url(params.url)

# Stores default an empty id to the task id, but only the in-memory
# store does so on the caller's object. Normalize here so the returned
# config carries the id that was persisted, on every store.
if not params.id:
params.id = task_id

await self._push_config_store.set_info(
task_id,
params,
context,
)

return params

@validate_request_params
@validate(
lambda self: self._agent_card.capabilities.push_notifications,
error_message='Push notifications are not supported by the agent',
error_type=PushNotificationNotSupportedError,
)
async def on_get_task_push_notification_config( # noqa: D102

Check notice on line 408 in src/a2a/server/request_handlers/default_request_handler_v2.py

View workflow job for this annotation

GitHub Actions / Lint Code Base

Copy/pasted code

see src/a2a/server/request_handlers/default_request_handler.py (540-571)
self,
params: GetTaskPushNotificationConfigRequest,
context: ServerCallContext,
Expand Down
44 changes: 44 additions & 0 deletions tests/server/request_handlers/test_default_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,50 @@ async def test_set_task_push_notification_config_task_not_found(agent_card):
mock_push_store.set_info.assert_not_awaited()


@pytest.mark.asyncio
@pytest.mark.parametrize('store_kind', ['inmemory', 'database'])
async def test_create_task_push_notification_config_returns_stored_id(
agent_card, store_kind
):
"""Test on_create_task_push_notification_config returns the id that was stored."""
if store_kind == 'database':
from a2a.server.tasks.database_push_notification_config_store import (
DatabasePushNotificationConfigStore,
)
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine(
'sqlite+aiosqlite:///file:pushid?mode=memory&cache=shared&uri=true'
)
push_config_store = DatabasePushNotificationConfigStore(engine=engine)
else:
push_config_store = InMemoryPushNotificationConfigStore()

task = create_sample_task()
task_store = InMemoryTaskStore()
context = create_server_call_context()
await task_store.save(task, context)

request_handler = DefaultRequestHandler(
agent_executor=MockAgentExecutor(),
task_store=task_store,
push_config_store=push_config_store,
agent_card=agent_card,
)
params = TaskPushNotificationConfig(
task_id=task.id,
url='http://example.com',
)

response = await request_handler.on_create_task_push_notification_config(
params, context
)

stored = await push_config_store.get_info(task.id, context)
assert response.id == task.id
assert [config.id for config in stored] == [response.id]


@pytest.mark.asyncio
async def test_get_task_push_notification_config_no_store(agent_card):
"""Test on_get_task_push_notification_config when _push_config_store is None."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,49 @@ async def test_set_task_push_notification_config_task_not_found():
mock_push_store.set_info.assert_not_awaited()


@pytest.mark.asyncio
@pytest.mark.parametrize('store_kind', ['inmemory', 'database'])
async def test_create_task_push_notification_config_returns_stored_id(
store_kind,
):
"""Test on_create_task_push_notification_config returns the id that was stored."""
if store_kind == 'database':
from a2a.server.tasks.database_push_notification_config_store import (
DatabasePushNotificationConfigStore,
)
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine(
'sqlite+aiosqlite:///file:pushidv2?mode=memory&cache=shared&uri=true'
)
push_config_store = DatabasePushNotificationConfigStore(engine=engine)
else:
push_config_store = InMemoryPushNotificationConfigStore()

task = create_sample_task()
task_store = InMemoryTaskStore()
context = create_server_call_context()
await task_store.save(task, context)

request_handler = DefaultRequestHandlerV2(
agent_executor=MockAgentExecutor(),
task_store=task_store,
push_config_store=push_config_store,
agent_card=create_default_agent_card(),
)
params = TaskPushNotificationConfig(
task_id=task.id, url='http://example.com'
)

response = await request_handler.on_create_task_push_notification_config(
params, context
)

stored = await push_config_store.get_info(task.id, context)
assert response.id == task.id
assert [config.id for config in stored] == [response.id]


@pytest.mark.asyncio
async def test_get_task_push_notification_config_no_store():
"""Test on_get_task_push_notification_config when _push_config_store is None."""
Expand Down
Loading