Skip to content

Commit

Permalink
The annotation @RestStreamElementType should override @produces at class
Browse files Browse the repository at this point in the history
According to [the documentation](https://quarkus.io/guides/resteasy-reactive#server-sent-event-sse-support), when using the annotation @RestStreamElementType and there is no @produces annotation set at method level, then RR is automatically using the media type `SERVER_SENT_EVENTS`.

However, when there is no @produces annotation set at method level, but it exists at class level, then the class level @produces annotation will be used which is wrong.

These changes address the mentioned issue above, so if there is no @produces annotation set at method level, then RR will always use the media type `SERVER_SENT_EVENTS` regardless if the annotation @produces is present at class level. 

Fix quarkusio#32012
  • Loading branch information
Sgitario committed Mar 22, 2023
1 parent 7d7bc87 commit 0888449
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -156,6 +175,29 @@ public Multi<Dto> 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<Dto> 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<Dto> getJson();
}

public static class Dto {
public String name;
public String value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -662,16 +662,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])) {
Expand Down

0 comments on commit 0888449

Please sign in to comment.