Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo


private String buildListTasksQueryString(ListTasksParams request) {
java.util.List<String> queryParts = new java.util.ArrayList<>();
List<String> queryParts = new java.util.ArrayList<>();
if (request.contextId() != null) {
queryParts.add("contextId=" + URLEncoder.encode(request.contextId(), StandardCharsets.UTF_8));
}
Expand All @@ -257,12 +257,27 @@ private String buildListTasksQueryString(ListTasksParams request) {
if (request.historyLength() != null) {
queryParts.add("historyLength=" + request.historyLength());
}
if (request.statusTimestampAfter() != null) {
queryParts.add("statusTimestampAfter=" + URLEncoder.encode(
request.statusTimestampAfter().toString(), StandardCharsets.UTF_8));
}
if (request.includeArtifacts() != null && request.includeArtifacts()) {
queryParts.add("includeArtifacts=true");
}
return String.join("&", queryParts);
}

private String buildListPushNotificationConfigsQueryString(ListTaskPushNotificationConfigsParams request) {
List<String> queryParts = new java.util.ArrayList<>();
if (request.pageSize() > 0) {
queryParts.add("pageSize=" + request.pageSize());
}
if (request.pageToken() != null && !request.pageToken().isEmpty()) {
queryParts.add("pageToken=" + URLEncoder.encode(request.pageToken(), StandardCharsets.UTF_8));
}
return String.join("&", queryParts);
}

@Override
public TaskPushNotificationConfig createTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException {
checkNotNullParam("request", request);
Expand Down Expand Up @@ -325,8 +340,13 @@ public ListTaskPushNotificationConfigsResult listTaskPushNotificationConfigurati
PayloadAndHeaders payloadAndHeaders = applyInterceptors(LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, builder,
agentCard, context);
try {
String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.id());
A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url);
StringBuilder urlBuilder = new StringBuilder(Utils.buildBaseUrl(agentInterface, request.tenant()));
urlBuilder.append(String.format("/tasks/%1s/pushNotificationConfigs", request.id()));
String queryString = buildListPushNotificationConfigsQueryString(request);
if (!queryString.isEmpty()) {
urlBuilder.append("?").append(queryString);
}
A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(urlBuilder.toString());
addStandardHeaders(getBuilder, payloadAndHeaders);
A2AHttpResponse response = getBuilder.get();
if (!response.success()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,20 @@ public class JsonRestMessages {
"id": "task-1234"
}
}""";

static final String LIST_TASKS_TEST_RESPONSE = """
{
"tasks": [
{
"id": "de38c76d-d54c-436c-8b9f-4c2703648d64",
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"status": {
"state": "TASK_STATE_COMPLETED"
},
"metadata": {}
}
],
"totalSize": 1,
"nextPageToken": ""
}""";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE;
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.GET_TASK_TEST_RESPONSE;
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE;
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.LIST_TASKS_TEST_RESPONSE;
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST;
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_RESPONSE;
import static org.a2aproject.sdk.client.transport.rest.JsonRestMessages.SEND_MESSAGE_TEST_REQUEST;
Expand All @@ -22,6 +23,7 @@
import static org.mockserver.model.HttpResponse.response;

import java.io.IOException;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
Expand All @@ -48,6 +50,7 @@
import org.a2aproject.sdk.spec.GetTaskPushNotificationConfigParams;
import org.a2aproject.sdk.spec.ListTaskPushNotificationConfigsParams;
import org.a2aproject.sdk.spec.ListTaskPushNotificationConfigsResult;
import org.a2aproject.sdk.spec.ListTasksParams;
import org.a2aproject.sdk.spec.Message;
import org.a2aproject.sdk.spec.MessageSendConfiguration;
import org.a2aproject.sdk.spec.MessageSendParams;
Expand Down Expand Up @@ -633,4 +636,65 @@ public void testVersionNotSupportedErrorUnmarshallingStreaming() throws Exceptio
assertTrue(versionError.getMessage().contains("2.0"));
assertTrue(versionError.getMessage().contains("1.0"));
}

/**
* Regression test for #1233: listTasks must include statusTimestampAfter in the query string.
* The mock server only responds when the parameter is present; without the fix the mock will
* not match and the client throws an exception, causing the assertion to fail.
*/
@Test
public void testListTasksIncludesStatusTimestampAfterQueryParam() throws Exception {
Instant cutoff = Instant.parse("2026-10-01T00:00:00Z");
this.server.when(
request()
.withMethod("GET")
.withPath("/tasks")
.withQueryStringParameter("statusTimestampAfter", "2026-10-01T00:00:00Z")
)
.respond(
response()
.withStatusCode(200)
.withBody(LIST_TASKS_TEST_RESPONSE)
);

RestTransport client = new RestTransport(CARD);
ListTasksParams params = ListTasksParams.builder()
.statusTimestampAfter(cutoff)
.build();

var result = client.listTasks(params, null);
assertEquals(1, result.tasks().size());
assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", result.tasks().get(0).id());
}

/**
* Regression test for #1233: listTaskPushNotificationConfigurations must include pageSize and
* pageToken in the query string. The mock server only responds when both parameters are present;
* without the fix the mock will not match and the client throws an exception.
*/
@Test
public void testListTaskPushNotificationConfigurationsIncludesPaginationQueryParams() throws Exception {
this.server.when(
request()
.withMethod("GET")
.withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs")
.withQueryStringParameter("pageSize", "10")
.withQueryStringParameter("pageToken", "next-page-token")
)
.respond(
response()
.withStatusCode(200)
.withBody(LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE)
);

RestTransport client = new RestTransport(CARD);
ListTaskPushNotificationConfigsParams params = ListTaskPushNotificationConfigsParams.builder()
.id("de38c76d-d54c-436c-8b9f-4c2703648d64")
.pageSize(10)
.pageToken("next-page-token")
.build();

ListTaskPushNotificationConfigsResult result = client.listTaskPushNotificationConfigurations(params, null);
assertEquals(2, result.configs().size());
}
}
Loading