Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 1 addition & 2 deletions common/src/main/java/io/a2a/common/MediaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@

public interface MediaType {

public static final String APPLICATION_JSON = "application/json";
public static final String APPLICATION_PROBLEM_JSON = "application/problem+json";
String APPLICATION_JSON = "application/json";
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.a2a.server.apps.quarkus;

import static io.a2a.common.MediaType.APPLICATION_PROBLEM_JSON;
import static io.a2a.server.ServerCallContext.TRANSPORT_KEY;
import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.HEADERS_KEY;
import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY;
Expand Down Expand Up @@ -248,7 +247,7 @@ public class A2AServerRoutes {
*
* <p><b>Processing Flow:</b>
* <ol>
* <li>Parse JSON-RPC request body using {@link JSONRPCUtils#parseRequestBody}</li>
* <li>Parse JSON-RPC request body using {@link JSONRPCUtils#parseRequestBody(String, String)}</li>
* <li>Create {@link ServerCallContext} from routing context</li>
* <li>Route to streaming or non-streaming handler</li>
* <li>Handle errors with appropriate JSON-RPC error codes</li>
Expand Down Expand Up @@ -297,7 +296,7 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) {
if (error != null) {
rc.response()
.setStatusCode(200)
.putHeader(CONTENT_TYPE, APPLICATION_PROBLEM_JSON)
.putHeader(CONTENT_TYPE, APPLICATION_JSON)
.end(serializeResponse(error));
} else if (streaming) {
final Multi<? extends A2AResponse<?>> finalStreamingResponse = streamingResponse;
Expand All @@ -313,7 +312,7 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) {
} else {
rc.response()
.setStatusCode(200)
.putHeader(CONTENT_TYPE, nonStreamingResponse.getError() != null ? APPLICATION_PROBLEM_JSON : APPLICATION_JSON)
.putHeader(CONTENT_TYPE, APPLICATION_JSON)
.end(serializeResponse(nonStreamingResponse));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.a2a.server.apps.quarkus;

import static io.a2a.common.MediaType.APPLICATION_PROBLEM_JSON;
import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD;
import static io.a2a.spec.A2AMethods.GET_TASK_METHOD;
import static io.a2a.spec.A2AMethods.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD;
Expand Down Expand Up @@ -718,7 +717,7 @@ public void testTenantExtraction_StreamingRequest() {
}

@Test
public void testJsonParseError_ContentTypeIsProblemJson() {
public void testJsonParseError_ContentTypeIsApplicationJson() {
// Arrange - invalid JSON
String invalidJson = "not valid json {{{";
when(mockRequestBody.asString()).thenReturn(invalidJson);
Expand All @@ -727,11 +726,11 @@ public void testJsonParseError_ContentTypeIsProblemJson() {
routes.invokeJSONRPCHandler(invalidJson, mockRoutingContext);

// Assert
verify(mockHttpResponse).putHeader(CONTENT_TYPE, APPLICATION_PROBLEM_JSON);
verify(mockHttpResponse).putHeader(CONTENT_TYPE, APPLICATION_JSON);
}

@Test
public void testMethodNotFound_ContentTypeIsProblemJson() {
public void testMethodNotFound_ContentTypeIsApplicationJson() {
// Arrange - unknown method
String jsonRpcRequest = """
{
Expand All @@ -746,7 +745,7 @@ public void testMethodNotFound_ContentTypeIsProblemJson() {
routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext);

// Assert
verify(mockHttpResponse).putHeader(CONTENT_TYPE, APPLICATION_PROBLEM_JSON);
verify(mockHttpResponse).putHeader(CONTENT_TYPE, APPLICATION_JSON);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.a2a.server.rest.quarkus;

import static io.a2a.common.MediaType.APPLICATION_JSON;
import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD;
import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD;
import static io.a2a.spec.A2AMethods.GET_TASK_METHOD;
Expand Down Expand Up @@ -82,7 +83,7 @@ public void setUp() {
when(mockRoutingContext.user()).thenReturn(null);
when(mockRequest.headers()).thenReturn(mockHeaders);
when(mockRequest.params()).thenReturn(mockParams);
when(mockRequest.getHeader(any(CharSequence.class))).thenReturn("application/json");
when(mockRequest.getHeader(any(CharSequence.class))).thenReturn(APPLICATION_JSON);
when(mockRoutingContext.body()).thenReturn(mockRequestBody);
when(mockRequestBody.asString()).thenReturn("{}");
when(mockResponse.setStatusCode(any(Integer.class))).thenReturn(mockResponse);
Expand All @@ -96,7 +97,7 @@ public void testSendMessage_MethodNameSetInContext() {
// Arrange
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.sendMessage(any(ServerCallContext.class), anyString(), anyString())).thenReturn(mockHttpResponse);

Expand All @@ -117,7 +118,7 @@ public void testSendMessageStreaming_MethodNameSetInContext() {
// Arrange
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.sendStreamingMessage(any(ServerCallContext.class), anyString(), anyString()))
.thenReturn(mockHttpResponse);
Expand All @@ -140,7 +141,7 @@ public void testGetTask_MethodNameSetInContext() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task123");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{test:value}");
when(mockRestHandler.getTask(any(ServerCallContext.class), anyString(), anyString(), any())).thenReturn(mockHttpResponse);

Expand All @@ -162,7 +163,7 @@ public void testCancelTask_MethodNameSetInContext() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task123");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.cancelTask(any(ServerCallContext.class), anyString(), anyString(), anyString())).thenReturn(mockHttpResponse);

Expand All @@ -184,7 +185,7 @@ public void testCancelTask_WithMetadata() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task456");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{\"id\":\"task456\",\"status\":\"cancelled\"}");

String requestBody = """
Expand Down Expand Up @@ -217,7 +218,7 @@ public void testCancelTask_WithEmptyMetadata() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task789");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{\"id\":\"task789\"}");

String requestBody = """
Expand Down Expand Up @@ -246,7 +247,7 @@ public void testCancelTask_WithNoMetadataField() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task999");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{\"id\":\"task999\"}");

String requestBody = "{}";
Expand All @@ -271,7 +272,7 @@ public void testCancelTask_WithNullBody() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task111");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{\"id\":\"task111\"}");

ArgumentCaptor<String> bodyCaptor = ArgumentCaptor.forClass(String.class);
Expand All @@ -293,7 +294,7 @@ public void testCancelTask_WithComplexMetadata() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task222");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{\"id\":\"task222\"}");

String requestBody = """
Expand Down Expand Up @@ -331,7 +332,7 @@ public void testSubscribeTask_MethodNameSetInContext() {
when(mockRoutingContext.pathParam("taskId")).thenReturn("task123");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.subscribeToTask(any(ServerCallContext.class), anyString(), anyString()))
.thenReturn(mockHttpResponse);
Expand All @@ -354,7 +355,7 @@ public void testCreateTaskPushNotificationConfiguration_MethodNameSetInContext()
when(mockRoutingContext.pathParam("taskId")).thenReturn("task123");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.createTaskPushNotificationConfiguration(any(ServerCallContext.class), anyString(), anyString(), anyString())).thenReturn(mockHttpResponse);

Expand All @@ -377,7 +378,7 @@ public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() {
when(mockRoutingContext.pathParam("configId")).thenReturn("config456");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.getTaskPushNotificationConfiguration(any(ServerCallContext.class), anyString(), anyString(), anyString())).thenReturn(mockHttpResponse);

Expand All @@ -400,7 +401,7 @@ public void testListTaskPushNotificationConfigurations_MethodNameSetInContext()
when(mockRoutingContext.pathParam("taskId")).thenReturn("task123");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.listTaskPushNotificationConfigurations(any(ServerCallContext.class), anyString(), anyString(), anyInt(), anyString()))
.thenReturn(mockHttpResponse);
Expand All @@ -424,7 +425,7 @@ public void testDeleteTaskPushNotificationConfiguration_MethodNameSetInContext()
when(mockRoutingContext.pathParam("configId")).thenReturn("config456");
HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class);
when(mockHttpResponse.getStatusCode()).thenReturn(200);
when(mockHttpResponse.getContentType()).thenReturn("application/json");
when(mockHttpResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockHttpResponse.getBody()).thenReturn("{}");
when(mockRestHandler.deleteTaskPushNotificationConfiguration(any(ServerCallContext.class), anyString(), anyString(), anyString())).thenReturn(mockHttpResponse);

Expand All @@ -446,7 +447,7 @@ public void testSendMessage_UnsupportedContentType_ReturnsContentTypeNotSupporte
// Arrange
HTTPRestResponse mockErrorResponse = mock(HTTPRestResponse.class);
when(mockErrorResponse.getStatusCode()).thenReturn(415);
when(mockErrorResponse.getContentType()).thenReturn("application/problem+json");
when(mockErrorResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockErrorResponse.getBody()).thenReturn("{\"error\":{\"code\":415,\"status\":\"INVALID_ARGUMENT\",\"message\":\"Incompatible content types\",\"details\":[{\"reason\":\"CONTENT_TYPE_NOT_SUPPORTED\",\"domain\":\"a2a-protocol.org\"}]}}");
when(mockRestHandler.createErrorResponse(any(ContentTypeNotSupportedError.class))).thenReturn(mockErrorResponse);
when(mockRequest.getHeader(any(CharSequence.class))).thenReturn("text/plain");
Expand All @@ -464,7 +465,7 @@ public void testSendMessageStreaming_UnsupportedContentType_ReturnsContentTypeNo
// Arrange
HTTPRestResponse mockErrorResponse = mock(HTTPRestResponse.class);
when(mockErrorResponse.getStatusCode()).thenReturn(415);
when(mockErrorResponse.getContentType()).thenReturn("application/problem+json");
when(mockErrorResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockErrorResponse.getBody()).thenReturn("{\"error\":{\"code\":415,\"status\":\"INVALID_ARGUMENT\",\"message\":\"Incompatible content types\",\"details\":[{\"reason\":\"CONTENT_TYPE_NOT_SUPPORTED\",\"domain\":\"a2a-protocol.org\"}]}}");
when(mockRestHandler.createErrorResponse(any(ContentTypeNotSupportedError.class))).thenReturn(mockErrorResponse);
when(mockRequest.getHeader(any(CharSequence.class))).thenReturn("text/plain");
Expand All @@ -482,9 +483,9 @@ public void testSendMessage_UnsupportedProtocolVersion_ReturnsVersionNotSupporte
// Arrange: content type is OK, but RestHandler returns a VersionNotSupportedError response
HTTPRestResponse mockErrorResponse = mock(HTTPRestResponse.class);
when(mockErrorResponse.getStatusCode()).thenReturn(400);
when(mockErrorResponse.getContentType()).thenReturn("application/problem+json");
when(mockErrorResponse.getContentType()).thenReturn(APPLICATION_JSON);
when(mockErrorResponse.getBody()).thenReturn("{\"error\":{\"code\":400,\"status\":\"UNIMPLEMENTED\",\"message\":\"Protocol version not supported\",\"details\":[{\"reason\":\"VERSION_NOT_SUPPORTED\",\"domain\":\"a2a-protocol.org\"}]}}");
when(mockRequest.getHeader(any(CharSequence.class))).thenReturn("application/json");
when(mockRequest.getHeader(any(CharSequence.class))).thenReturn(APPLICATION_JSON);
when(mockRestHandler.sendMessage(any(ServerCallContext.class), anyString(), anyString()))
.thenReturn(mockErrorResponse);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.a2a.transport.rest.handler;

import static io.a2a.common.MediaType.APPLICATION_JSON;
import static io.a2a.common.MediaType.APPLICATION_PROBLEM_JSON;
import static io.a2a.server.util.async.AsyncUtils.createTubeConfig;

import io.a2a.spec.A2AErrorCodes;
Expand Down Expand Up @@ -683,7 +682,7 @@ public HTTPRestResponse createErrorResponse(A2AError error) {

private HTTPRestResponse createErrorResponse(int statusCode, A2AError error) {
String jsonBody = new HTTPRestErrorResponse(error).toJson();
return new HTTPRestResponse(statusCode, APPLICATION_PROBLEM_JSON, jsonBody);
return new HTTPRestResponse(statusCode, APPLICATION_JSON, jsonBody);
}

private HTTPRestStreamingResponse createStreamingResponse(Flow.Publisher<StreamingEventKind> publisher) {
Expand Down
Loading
Loading