Skip to content

Commit 45e7d33

Browse files
ehsavoieclaude
andcommitted
fix: Add tenant parameter to REST push notification config endpoints
- Add tenant parameter to RestHandler.getTaskPushNotificationConfiguration to match other push notification methods - Add route with trailing slash to distinguish GET (single config) from LIST in A2AServerRoutes - Update RestTransport to use trailing slash when configId is null - Update tests to use new method signatures This ensures consistent tenant handling across all push notification endpoints and fixes routing ambiguity between GET and LIST operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
1 parent b30613d commit 45e7d33

File tree

5 files changed

+31
-10
lines changed

5 files changed

+31
-10
lines changed

client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,8 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu
320320
if (configId != null && !configId.isEmpty()) {
321321
builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), configId));
322322
} else {
323-
builder.setName(String.format("/tasks/%1s/pushNotificationConfigs", request.id()));
323+
// Use trailing slash to distinguish GET from LIST
324+
builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/", request.id()));
324325
}
325326
PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskPushNotificationConfigRequest.METHOD, builder,
326327
agentCard, context);
@@ -329,7 +330,8 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu
329330
if (configId != null && !configId.isEmpty()) {
330331
url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), configId);
331332
} else {
332-
url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.id());
333+
// Trailing slash distinguishes GET from LIST
334+
url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs/", request.id());
333335
}
334336
A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url);
335337
if (payloadAndHeaders.getHeaders() != null) {

reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public void setTaskPushNotificationConfiguration(@Body String body, RoutingConte
265265
}
266266
}
267267

268-
@Route(regex = "^\\/(?<tenant>[^\\/]*\\/?)tasks\\/(?<id>[^/]+)\\/pushNotificationConfigs\\/(?<configId>[^\\/]+)", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING)
268+
@Route(regex = "^\\/(?<tenant>[^\\/]*\\/?)tasks\\/(?<id>[^/]+)\\/pushNotificationConfigs\\/(?<configId>[^\\/]+)", order = 2, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING)
269269
public void getTaskPushNotificationConfiguration(RoutingContext rc) {
270270
String taskId = rc.pathParam("id");
271271
String configId = rc.pathParam("configId");
@@ -275,7 +275,7 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) {
275275
if (taskId == null || taskId.isEmpty()) {
276276
response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id"));
277277
} else {
278-
response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, context);
278+
response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, extractTenant(rc), context);
279279
}
280280
} catch (Throwable t) {
281281
response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage()));
@@ -284,7 +284,26 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) {
284284
}
285285
}
286286

287-
@Route(regex = "^\\/(?<tenant>[^\\/]*\\/?)tasks\\/(?<id>[^/]+)\\/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING)
287+
@Route(regex = "^\\/(?<tenant>[^\\/]*\\/?)tasks\\/(?<id>[^/]+)\\/pushNotificationConfigs\\/$", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING)
288+
public void getTaskPushNotificationConfigurationWithoutId(RoutingContext rc) {
289+
String taskId = rc.pathParam("id");
290+
ServerCallContext context = createCallContext(rc, GetTaskPushNotificationConfigRequest.METHOD);
291+
HTTPRestResponse response = null;
292+
try {
293+
if (taskId == null || taskId.isEmpty()) {
294+
response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id"));
295+
} else {
296+
// Call get with null configId - trailing slash distinguishes this from list
297+
response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, null, extractTenant(rc), context);
298+
}
299+
} catch (Throwable t) {
300+
response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage()));
301+
} finally {
302+
sendResponse(rc, response);
303+
}
304+
}
305+
306+
@Route(regex = "^\\/(?<tenant>[^\\/]*\\/?)tasks\\/(?<id>[^/]+)\\/pushNotificationConfigs", order = 3, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING)
288307
public void listTaskPushNotificationConfigurations(RoutingContext rc) {
289308
String taskId = rc.pathParam("id");
290309
ServerCallContext context = createCallContext(rc, ListTaskPushNotificationConfigRequest.METHOD);

reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() {
229229
when(mockHttpResponse.getStatusCode()).thenReturn(200);
230230
when(mockHttpResponse.getContentType()).thenReturn("application/json");
231231
when(mockHttpResponse.getBody()).thenReturn("{}");
232-
when(mockRestHandler.getTaskPushNotificationConfiguration(anyString(), anyString(),
232+
when(mockRestHandler.getTaskPushNotificationConfiguration(anyString(), anyString(), anyString(),
233233
any(ServerCallContext.class))).thenReturn(mockHttpResponse);
234234

235235
ArgumentCaptor<ServerCallContext> contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class);
@@ -238,7 +238,7 @@ public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() {
238238
routes.getTaskPushNotificationConfiguration(mockRoutingContext);
239239

240240
// Assert
241-
verify(mockRestHandler).getTaskPushNotificationConfiguration(eq("task123"), eq("config456"),
241+
verify(mockRestHandler).getTaskPushNotificationConfiguration(eq("task123"), eq("config456"), anyString(),
242242
contextCaptor.capture());
243243
ServerCallContext capturedContext = contextCaptor.getValue();
244244
assertNotNull(capturedContext);

transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,12 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s
236236
}
237237
}
238238

239-
public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nullable String configId, ServerCallContext context) {
239+
public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nullable String configId, String tenant, ServerCallContext context) {
240240
try {
241241
if (!agentCard.capabilities().pushNotifications()) {
242242
throw new PushNotificationNotSupportedError();
243243
}
244-
GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams(taskId, configId);
244+
GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams(taskId, configId, tenant);
245245
TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(params, context);
246246
return createSuccessResponse(200, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(config)));
247247
} catch (JSONRPCError e) {

transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public void testGetPushNotificationConfig() {
283283
Assertions.assertEquals(201, response.getStatusCode(), response.toString());
284284
Assertions.assertEquals("application/json", response.getContentType());
285285
// Now get it
286-
response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", callContext);
286+
response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", "", callContext);
287287
Assertions.assertEquals(200, response.getStatusCode(), response.toString());
288288
Assertions.assertEquals("application/json", response.getContentType());
289289
}

0 commit comments

Comments
 (0)