diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/MultiSseTest.java b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/MultiSseTest.java index cecc8a6be8f97..df9f9b662d31b 100644 --- a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/MultiSseTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/MultiSseTest.java @@ -16,6 +16,8 @@ import jakarta.ws.rs.core.MediaType; import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.jboss.resteasy.reactive.RestStreamElementType; import org.jboss.resteasy.reactive.server.jackson.JacksonBasicMessageBodyReader; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -93,6 +95,23 @@ void shouldSendPayloadAndConsumeAsParametrizedType() { Map.of("name", "foo", "value", "test"))); } + /** + * Test to reproduce the issue: https://github.com/quarkusio/quarkus/issues/32012. + */ + @Test + void shouldRestStreamElementTypeOverwriteProducesAtClassLevel() { + var resultList = new CopyOnWriteArrayList<>(); + RestClientBuilder.newBuilder().baseUri(uri) + .build(SeeWithRestStreamElementTypeClient.class) + .getJson() + .subscribe() + .with(resultList::add); + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted( + () -> assertThat(resultList) + .containsExactly(new Dto("foo", "bar"), new Dto("chocolate", "bar"))); + } + private SseClient createClient() { return RestClientBuilder.newBuilder().baseUri(uri).register(new JacksonBasicMessageBodyReader(new ObjectMapper())) .build(SseClient.class); @@ -156,6 +175,29 @@ public Multi postAndReadAsMap(String entity) { } } + @Path("/sse-rest-stream-element-type") + // The following annotation should be ignored because we're using `@RestStreamElementType(MediaType.APPLICATION_JSON)`. + @Produces(MediaType.APPLICATION_JSON) + public static class SseWithRestStreamElementTypeResource { + @GET + @RestStreamElementType(MediaType.APPLICATION_JSON) + @Path("/json") + public Multi getJson() { + return Multi.createFrom().items(new Dto("foo", "bar"), new Dto("chocolate", "bar")); + } + } + + @RegisterRestClient + @Path("/sse-rest-stream-element-type") + // The following annotation should be ignored because we're using `@RestStreamElementType(MediaType.APPLICATION_JSON)`. + @Produces(MediaType.APPLICATION_JSON) + public interface SeeWithRestStreamElementTypeClient { + @GET + @Path("/json") + @RestStreamElementType(MediaType.APPLICATION_JSON) + Multi getJson(); + } + public static class Dto { public String name; public String value; diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index b60c6e611eb18..3ab31b3282411 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -684,16 +684,17 @@ private ResourceMethod createResourceMethod(ClassInfo currentClassInfo, ClassInf streamElementType = streamElementTypeInMethod; } - String[] produces = extractProducesConsumesValues(getAnnotationStore().getAnnotation(currentMethodInfo, PRODUCES), - basicResourceClassInfo.getProduces()); + String[] produces = extractProducesConsumesValues(getAnnotationStore().getAnnotation(currentMethodInfo, PRODUCES)); if (((produces == null) || (produces.length == 0)) && (streamElementType != null)) { // when @RestStreamElementType is used, we automatically determine SSE as the @Produces MediaType - produces = applyDefaultProducesAndAddCharsets(httpMethod, nonAsyncReturnType, - new String[] { MediaType.SERVER_SENT_EVENTS }); - } else { - produces = applyDefaultProducesAndAddCharsets(httpMethod, nonAsyncReturnType, produces); + produces = new String[] { MediaType.SERVER_SENT_EVENTS }; + } else if (produces == null || produces.length == 0) { + // use the @Produces annotation at class level if exists + produces = basicResourceClassInfo.getProduces(); } + produces = applyDefaultProducesAndAddCharsets(httpMethod, nonAsyncReturnType, produces); + boolean returnsMultipart = false; if (produces != null && produces.length == 1) { if (streamElementType == null && MediaType.SERVER_SENT_EVENTS.equals(produces[0])) {