Skip to content

Ishymko/push notifications#711

Draft
ishymko wants to merge 2 commits into1.0-devfrom
ishymko/push-notifications
Draft

Ishymko/push notifications#711
ishymko wants to merge 2 commits into1.0-devfrom
ishymko/push-notifications

Conversation

@ishymko
Copy link
Member

@ishymko ishymko commented Feb 19, 2026

Fixes #702

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ishymko, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request expands the existing push notification configuration management by adding capabilities to list all configurations for a given task and to delete specific configurations. This provides users with more granular control over their task-related push notifications, moving beyond just creation and retrieval of individual configurations. The changes span client interfaces, transport implementations, and server-side request handlers, ensuring consistent functionality across different communication protocols.

Highlights

  • New Push Notification Management APIs: Introduced new client and server-side APIs to list and delete task-specific push notification configurations, enhancing control over task notifications.
  • Multi-Protocol Support: Implemented the new list and delete functionalities across gRPC, JSON-RPC, and REST transport layers, ensuring broad compatibility.
  • Comprehensive Testing: Added unit and integration tests for the new push notification list and delete operations, covering all supported client transports and server handlers.
Changelog
  • src/a2a/client/base_client.py
    • Imported new request/response types for listing and deleting push notification configurations.
    • Added list_task_callback and delete_task_callback methods to the base client.
  • src/a2a/client/client.py
    • Imported new request/response types for listing and deleting push notification configurations.
    • Added abstract list_task_callback and delete_task_callback methods to the client interface.
  • src/a2a/client/transports/base.py
    • Imported new request/response types for listing and deleting push notification configurations.
    • Added abstract list_task_callback and delete_task_callback methods to the base transport interface.
  • src/a2a/client/transports/grpc.py
    • Imported new request/response types for listing and deleting push notification configurations.
    • Implemented list_task_callback and delete_task_callback methods using gRPC stubs.
  • src/a2a/client/transports/jsonrpc.py
    • Imported new request/response types for listing and deleting push notification configurations.
    • Implemented list_task_callback and delete_task_callback methods with JSON-RPC request/response handling.
  • src/a2a/client/transports/rest.py
    • Imported new request/response types for listing and deleting push notification configurations.
    • Added a private helper method _send_delete_request for HTTP DELETE operations.
    • Implemented list_task_callback and delete_task_callback methods with REST API call logic.
  • src/a2a/server/apps/rest/rest_adapter.py
    • Added a new DELETE route for /v1/tasks/{id}/pushNotificationConfigs/{push_id} to handle deletion requests.
  • src/a2a/server/request_handlers/grpc_handler.py
    • Imported empty_pb2 for gRPC empty responses.
    • Implemented ListTaskPushNotificationConfig and DeleteTaskPushNotificationConfig gRPC service methods.
  • src/a2a/server/request_handlers/rest_handler.py
    • Implemented delete_push_notification to process REST DELETE requests for push notification configurations.
    • Updated list_tasks and list_push_notifications to provide concrete implementations instead of raising NotImplementedError.
  • tests/client/transports/test_grpc_client.py
    • Imported new request/response types for push notification configurations.
    • Mocked the new gRPC stub methods for listing and deleting push notification configurations.
    • Added new asynchronous tests for list_task_callback and delete_task_callback in the gRPC transport.
  • tests/client/transports/test_jsonrpc_client.py
    • Imported new request/response types for push notification configurations.
    • Added new asynchronous tests for list_task_callback and delete_task_callback in the JSON-RPC transport.
  • tests/client/transports/test_rest_client.py
    • Imported new request/response types for push notification configurations.
    • Added new asynchronous tests for list_task_callback and delete_task_callback in the REST transport.
  • tests/integration/test_client_server_integration.py
    • Imported new request/response types for push notification configurations.
    • Configured mock handler responses for on_list_task_push_notification_config and on_delete_task_push_notification_config.
    • Added integration tests for list_task_callback and delete_task_callback across HTTP (JSON-RPC, REST) and gRPC transports.
Activity
  • The pull request addresses and fixes issue [Feat]: Implement remaining methods of the push notifications API #702, which likely involved the missing functionality for managing push notification configurations.
  • The author, ishymko, has implemented new features for listing and deleting task push notification configurations and provided comprehensive tests.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces functionality for listing and deleting task push notification configurations, with changes spanning the client, server, and all transport layers (gRPC, JSON-RPC, REST), complemented by new tests. The implementation is solid, but I've identified a few areas for improvement. There's significant code duplication in the new JSON-RPC transport methods that could be refactored. Additionally, I've found some minor inconsistencies in test mocks that should be corrected to better align with the production code's behavior.

Comment on lines +384 to +445
async def list_task_callback(
self,
request: ListTaskPushNotificationConfigRequest,
*,
context: ClientCallContext | None = None,
extensions: list[str] | None = None,
) -> ListTaskPushNotificationConfigResponse:
"""Lists push notification configurations for a specific task."""
rpc_request = JSONRPC20Request(
method='ListTaskPushNotificationConfig',
params=json_format.MessageToDict(request),
_id=str(uuid4()),
)
modified_kwargs = update_extension_header(
self._get_http_args(context),
extensions if extensions is not None else self.extensions,
)
payload, modified_kwargs = await self._apply_interceptors(
'ListTaskPushNotificationConfig',
cast('dict[str, Any]', rpc_request.data),
modified_kwargs,
context,
)
response_data = await self._send_request(payload, modified_kwargs)
json_rpc_response = JSONRPC20Response(**response_data)
if json_rpc_response.error:
raise A2AClientJSONRPCError(json_rpc_response.error)
response: ListTaskPushNotificationConfigResponse = (
json_format.ParseDict(
json_rpc_response.result,
ListTaskPushNotificationConfigResponse(),
)
)
return response

async def delete_task_callback(
self,
request: DeleteTaskPushNotificationConfigRequest,
*,
context: ClientCallContext | None = None,
extensions: list[str] | None = None,
) -> None:
"""Deletes the push notification configuration for a specific task."""
rpc_request = JSONRPC20Request(
method='DeleteTaskPushNotificationConfig',
params=json_format.MessageToDict(request),
_id=str(uuid4()),
)
modified_kwargs = update_extension_header(
self._get_http_args(context),
extensions if extensions is not None else self.extensions,
)
payload, modified_kwargs = await self._apply_interceptors(
'DeleteTaskPushNotificationConfig',
cast('dict[str, Any]', rpc_request.data),
modified_kwargs,
context,
)
response_data = await self._send_request(payload, modified_kwargs)
json_rpc_response = JSONRPC20Response(**response_data)
if json_rpc_response.error:
raise A2AClientJSONRPCError(json_rpc_response.error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These two new methods, list_task_callback and delete_task_callback, share a lot of boilerplate code for making a JSON-RPC request. This is a pattern repeated across other methods in this class as well. To improve maintainability and reduce code duplication, consider extracting this logic into a private helper method.

A helper method could handle creating the JSONRPC20Request, applying interceptors, sending the request, and handling the response, including error checking. The specific methods could then call this helper with the RPC method name and expected response type.

For example, you could have a helper like _make_rpc_call(self, method, request, response_cls, ...).

Comment on lines 295 to 305
@@ -285,3 +304,3 @@
result = await self.request_handler.on_list_tasks(params, context)
return MessageToDict(result)

@@ -292,16 +311,24 @@
) -> dict[str, Any]:
"""Handles the 'tasks/pushNotificationConfig/list' REST method.

This method is currently not implemented.

Args:
request: The incoming `Request` object.
context: Context provided by the server.

Returns:
A list of `dict` representing the `TaskPushNotificationConfig` objects.

Raises:
NotImplementedError: This method is not yet implemented.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The docstring for list_tasks still mentions NotImplementedError, but the method is now implemented. The docstring should be updated to reflect the current implementation.

Comment on lines 304 to 306
@@ -292,16 +311,24 @@
) -> dict[str, Any]:
"""Handles the 'tasks/pushNotificationConfig/list' REST method.

This method is currently not implemented.

Args:
request: The incoming `Request` object.
context: Context provided by the server.

Returns:
A list of `dict` representing the `TaskPushNotificationConfig` objects.

Raises:
NotImplementedError: This method is not yet implemented.
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The Raises section in the docstring for list_push_notifications is no longer accurate as the method is now implemented. It should be removed.

Comment on lines +576 to +578
mock_grpc_stub.DeleteTaskPushNotificationConfig.return_value = (
sample_task_push_notification_config
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The DeleteTaskPushNotificationConfig gRPC method is defined to return google.protobuf.Empty. To make the mock more accurate, it should return None since the transport method does not use the return value and empty_pb2 is not imported in this file.

    mock_grpc_stub.DeleteTaskPushNotificationConfig.return_value = None

)
response = await transport.delete_task_callback(request)

mock_httpx_client.post.assert_called_once() # Assuming mock_send_request was a typo for mock_httpx_client.post
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This comment appears to be a leftover development note and can be removed for cleaner code.

Suggested change
mock_httpx_client.post.assert_called_once() # Assuming mock_send_request was a typo for mock_httpx_client.post
mock_httpx_client.post.assert_called_once()

Comment on lines +376 to +383
mock_response.json.return_value = {
'taskId': task_id,
'id': 'config-1',
'pushNotificationConfig': {
'id': 'config-1',
'url': 'https://example.com',
},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The mocked JSON response for a successful deletion returns a full object. However, the corresponding server-side handler (delete_push_notification in rest_handler.py) returns an empty dictionary {}. To make the test more aligned with the server implementation, the mocked json.return_value should be {}.

        mock_response.json.return_value = {}

Comment on lines +144 to +146
handler.on_delete_task_push_notification_config.return_value = (
CALLBACK_CONFIG
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The on_delete_task_push_notification_config method in the RequestHandler interface is defined to return None. To align the mock with the interface definition, it should be configured to return None.

    handler.on_delete_task_push_notification_config.return_value = None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments