From 084aca10113e7f783f13eef64c2269350a9dd056 Mon Sep 17 00:00:00 2001 From: rinaldiangoogle Date: Mon, 27 Oct 2025 15:50:26 +0000 Subject: [PATCH 1/4] Add util method to extract Task ID and Push Notification Config ID from request Both `GetTaskPushNotificationConfigRequest` and `DeleteTaskPushNotificationConfigRequest` have a `name` field that has the same format: `tasks/{task_id}/pushNotificationConfigs/{config_id}`. Adding here a utility to extract the Task ID and Config ID. Can't output a `types.TaskPushNotificationConfig` as the `PushNotificationConfig` requires a url. --- src/a2a/utils/proto_utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index e619cd72..9bd97cca 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -366,6 +366,26 @@ def task_push_notification_config( ), ) + @classmethod + def task_push_notification_config_params( + cls, + request: ( + a2a_pb2.GetTaskPushNotificationConfigRequest + | a2a_pb2.DeleteTaskPushNotificationConfigRequest + ), + ) -> tuple[str, str]: + m = _TASK_PUSH_CONFIG_NAME_MATCH.match(request.name) + if not m: + raise ServerError( + error=types.InvalidParamsError( + message=f'No task or config id for {request.name}' + ) + ) + return ( + m.group(1), + m.group(2), + ) + @classmethod def agent_card( cls, From 56e7448819853e5b62197a9706e1c75a7dfcf99e Mon Sep 17 00:00:00 2001 From: rinaldiangoogle Date: Mon, 27 Oct 2025 15:50:26 +0000 Subject: [PATCH 2/4] Add docstring for `task_push_notification_config_params` method --- src/a2a/utils/proto_utils.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index 9bd97cca..c584116b 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -374,6 +374,17 @@ def task_push_notification_config_params( | a2a_pb2.DeleteTaskPushNotificationConfigRequest ), ) -> tuple[str, str]: + """Parses the task ID and push notification config ID from the request. + + Args: + request: The request to parse. + + Returns: + A tuple containing the task ID and push notification config ID. + + Raises: + ServerError: If the request name is invalid. + """ m = _TASK_PUSH_CONFIG_NAME_MATCH.match(request.name) if not m: raise ServerError( From b4fef7f52952edefeae820f4f690538f4231cb45 Mon Sep 17 00:00:00 2001 From: Holt Skinner <13262395+holtskinner@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:29:58 -0500 Subject: [PATCH 3/4] Format --- src/a2a/utils/proto_utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index c584116b..79f248b3 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -387,11 +387,11 @@ def task_push_notification_config_params( """ m = _TASK_PUSH_CONFIG_NAME_MATCH.match(request.name) if not m: - raise ServerError( - error=types.InvalidParamsError( - message=f'No task or config id for {request.name}' - ) - ) + raise ServerError( + error=types.InvalidParamsError( + message=f'No task or config id for {request.name}' + ) + ) return ( m.group(1), m.group(2), From 65cdf3d10440e848cc72a71497e0309f05bb68ad Mon Sep 17 00:00:00 2001 From: rinaldiangoogle Date: Tue, 28 Oct 2025 20:39:27 +0000 Subject: [PATCH 4/4] Add tests for valid and invalid calls to `task_push_notification_config_params` --- tests/utils/test_proto_utils.py | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/utils/test_proto_utils.py b/tests/utils/test_proto_utils.py index da54f833..9e0fdbec 100644 --- a/tests/utils/test_proto_utils.py +++ b/tests/utils/test_proto_utils.py @@ -244,6 +244,62 @@ def test_task_push_config_from_proto_invalid_parent(self): proto_utils.FromProto.task_push_notification_config(request) assert isinstance(exc_info.value.error, types.InvalidParamsError) + @pytest.mark.parametrize( + 'request_type, name, expected_task_id, expected_config_id', + [ + ( + a2a_pb2.GetTaskPushNotificationConfigRequest, + 'tasks/task-123/pushNotificationConfigs/config-456', + 'task-123', + 'config-456', + ), + ( + a2a_pb2.DeleteTaskPushNotificationConfigRequest, + 'tasks/task-abc/pushNotificationConfigs/config-def', + 'task-abc', + 'config-def', + ), + ], + ) + def test_task_push_notification_config_params_valid( + self, request_type, name, expected_task_id, expected_config_id + ): + """Test valid name in task_push_notification_config_params.""" + request = request_type(name=name) + task_id, config_id = ( + proto_utils.ToProto.task_push_notification_config_params(request) + ) + assert task_id == expected_task_id + assert config_id == expected_config_id + + @pytest.mark.parametrize( + 'request_type', + [ + a2a_pb2.GetTaskPushNotificationConfigRequest, + a2a_pb2.DeleteTaskPushNotificationConfigRequest, + ], + ) + @pytest.mark.parametrize( + 'name', + [ + 'invalid-name-format', + 'tasks/task-123', + 'tasks/task-123/pushNotificationConfigs', + 'tasks//pushNotificationConfigs/config-456', + 'tasks/task-123/pushNotificationConfigs/', + 'foo/task-123/bar/config-456', + ], + ) + def test_task_push_notification_config_params_invalid( + self, request_type, name + ): + """Test invalid names in task_push_notification_config_params.""" + request = request_type(name=name) + with pytest.raises(ServerError) as exc_info: + proto_utils.ToProto.task_push_notification_config_params(request) + assert isinstance(exc_info.value.error, types.InvalidParamsError) + assert 'No task or config id for' in exc_info.value.error.message + def test_none_handling(self): """Test that None inputs are handled gracefully.""" assert proto_utils.ToProto.message(None) is None