Skip to content

Commit 4e98c62

Browse files
authored
fix(grpc): avoid NPE when listTasks is called without a tenant (#1240)
* fix(grpc): avoid NPE when listTasks is called without a tenant Related to #1207 * fix(grpc): map tenant in ListTasksParams fromProto Convert an empty protobuf tenant back to null so that a null tenant survives a toProto/fromProto round trip.
1 parent 7acb539 commit 4e98c62

3 files changed

Lines changed: 107 additions & 0 deletions

File tree

‎client/transport/jsonrpc/src/test/java/org/a2aproject/sdk/client/transport/jsonrpc/JSONRPCTransportTest.java‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
import java.util.List;
3838
import java.util.Map;
3939

40+
import com.google.gson.JsonObject;
41+
import com.google.gson.JsonParser;
42+
43+
import org.a2aproject.sdk.jsonrpc.common.wrappers.ListTasksResult;
4044
import org.a2aproject.sdk.spec.A2AClientException;
4145
import org.a2aproject.sdk.spec.A2AClientHTTPError;
4246
import org.a2aproject.sdk.spec.AgentCard;
@@ -54,6 +58,7 @@
5458
import org.a2aproject.sdk.spec.FileWithUri;
5559
import org.a2aproject.sdk.spec.GetExtendedAgentCardParams;
5660
import org.a2aproject.sdk.spec.GetTaskPushNotificationConfigParams;
61+
import org.a2aproject.sdk.spec.ListTasksParams;
5762
import org.a2aproject.sdk.spec.Message;
5863
import org.a2aproject.sdk.spec.MessageSendConfiguration;
5964
import org.a2aproject.sdk.spec.MessageSendParams;
@@ -88,6 +93,50 @@ public void tearDown() {
8893
server.stop();
8994
}
9095

96+
@Test
97+
public void testListTasksWithDefaultBuilder() throws Exception {
98+
JsonObject params = listTasksAndGetRequestParams(ListTasksParams.builder().build());
99+
100+
assertEquals("", params.get("tenant").getAsString());
101+
}
102+
103+
@Test
104+
public void testListTasksWithDefaultConstructor() throws Exception {
105+
JsonObject params = listTasksAndGetRequestParams(new ListTasksParams());
106+
107+
assertEquals("", params.get("tenant").getAsString());
108+
}
109+
110+
@Test
111+
public void testListTasksWithTenant() throws Exception {
112+
JsonObject params = listTasksAndGetRequestParams(ListTasksParams.builder().tenant("tenant-123").build());
113+
114+
assertEquals("tenant-123", params.get("tenant").getAsString());
115+
}
116+
117+
private JsonObject listTasksAndGetRequestParams(ListTasksParams params) throws Exception {
118+
String body = """
119+
{
120+
"jsonrpc": "2.0",
121+
"id": "list-tasks",
122+
"result": {"tasks": [], "totalSize": 0, "pageSize": 0}
123+
}
124+
""";
125+
server.when(request().withMethod("POST").withPath("/"))
126+
.respond(response().withStatusCode(200).withBody(body));
127+
128+
ListTasksResult result = new JSONRPCTransport("http://localhost:4001").listTasks(params, null);
129+
130+
assertTrue(result.tasks().isEmpty());
131+
assertEquals(0, result.totalSize());
132+
assertEquals(0, result.pageSize());
133+
var requests = server.retrieveRecordedRequests(request().withMethod("POST").withPath("/"));
134+
assertEquals(1, requests.length);
135+
JsonObject json = JsonParser.parseString(requests[0].getBodyAsString()).getAsJsonObject();
136+
assertEquals("ListTasks", json.get("method").getAsString());
137+
return json.getAsJsonObject("params");
138+
}
139+
91140
@Test
92141
public void testA2AClientSendMessage() throws Exception {
93142
this.server.when(

‎spec-grpc/src/main/java/org/a2aproject/sdk/grpc/mapper/ListTasksParamsMapper.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public interface ListTasksParamsMapper {
2828
@Mapping(target = "historyLength", source = "historyLength", conditionExpression = "java(params.historyLength() != null)")
2929
@Mapping(target = "statusTimestampAfter", source = "statusTimestampAfter", qualifiedByName = "instantToProtoTimestamp")
3030
@Mapping(target = "includeArtifacts", source = "includeArtifacts", conditionExpression = "java(params.includeArtifacts() != null)")
31+
@Mapping(target = "tenant", source = "tenant", conditionExpression = "java(params.tenant() != null)")
3132
ListTasksRequest toProto(org.a2aproject.sdk.spec.ListTasksParams params);
3233

3334
/**
@@ -45,5 +46,6 @@ public interface ListTasksParamsMapper {
4546
@Mapping(target = "historyLength", expression = "java(request.hasHistoryLength() ? request.getHistoryLength() : null)")
4647
@Mapping(target = "statusTimestampAfter", source = "statusTimestampAfter", qualifiedByName = "protoTimestampToInstant")
4748
@Mapping(target = "includeArtifacts", source = "includeArtifacts", qualifiedByName = "falseToNull")
49+
@Mapping(target = "tenant", source = "tenant", qualifiedByName = "emptyToNull")
4850
org.a2aproject.sdk.spec.ListTasksParams fromProto(ListTasksRequest request);
4951
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.a2aproject.sdk.grpc.mapper;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
6+
import org.a2aproject.sdk.grpc.ListTasksRequest;
7+
import org.a2aproject.sdk.spec.ListTasksParams;
8+
import org.junit.jupiter.api.Test;
9+
10+
public class ListTasksParamsMapperTest {
11+
12+
@Test
13+
void toProtoWithDefaultBuilder() {
14+
ListTasksRequest request = ListTasksParamsMapper.INSTANCE.toProto(ListTasksParams.builder().build());
15+
16+
assertEquals("", request.getTenant());
17+
}
18+
19+
@Test
20+
void toProtoWithDefaultConstructor() {
21+
ListTasksRequest request = ListTasksParamsMapper.INSTANCE.toProto(new ListTasksParams());
22+
23+
assertEquals("", request.getTenant());
24+
}
25+
26+
@Test
27+
void toProtoWithTenant() {
28+
ListTasksRequest request = ListTasksParamsMapper.INSTANCE.toProto(
29+
ListTasksParams.builder().tenant("tenant-123").build());
30+
31+
assertEquals("tenant-123", request.getTenant());
32+
}
33+
34+
@Test
35+
void fromProtoWithoutTenant() {
36+
ListTasksParams params = ListTasksParamsMapper.INSTANCE.fromProto(ListTasksRequest.newBuilder().build());
37+
38+
assertNull(params.tenant());
39+
}
40+
41+
@Test
42+
void fromProtoWithTenant() {
43+
ListTasksParams params = ListTasksParamsMapper.INSTANCE.fromProto(
44+
ListTasksRequest.newBuilder().setTenant("tenant-123").build());
45+
46+
assertEquals("tenant-123", params.tenant());
47+
}
48+
49+
@Test
50+
void nullTenantRoundTrip() {
51+
ListTasksRequest request = ListTasksParamsMapper.INSTANCE.toProto(ListTasksParams.builder().build());
52+
ListTasksParams params = ListTasksParamsMapper.INSTANCE.fromProto(request);
53+
54+
assertNull(params.tenant());
55+
}
56+
}

0 commit comments

Comments
 (0)