quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.54k stars 2.61k forks source link

@JsonView annotation ignored for sub-resources #37145

Open jonjanisch opened 9 months ago

jonjanisch commented 9 months ago

code-with-quarkus.zip

Describe the bug

We want to use @JsonView to to selectively hide/show fields for different scenarios.

It works for the simple scenario of adding it to a top-level Resource. But the same code fails if the endpoint is in a sub-resource.

Example

Basic User class where we want to hide the password field in the JSON response except for Private.

public class User {

    private String name;

    // hide the password except for Private view
    @JsonView(Views.Private.class)
    private String password;

    // getters/setters
}

The Views are identical to https://quarkus.io/guides/resteasy-reactive#jsonview-support

public class Views {
    public static class Public {}
    public static class Private extends Public {}
}

Create a simple endpoint at the top-level and it works fine.

@GET
@JsonView(Views.Public.class)
public User hello() {
    User user = new User();
    user.setName("John");
    user.setPassword("password");
    return user;
}
http://localhost:8080/hello
{"name":"John"}

Bug Scenario - using @JsonView in Sub-Resource

Now create another class BugResource with the same code and in GreetingResource add a method to return the sub-resource:

@Path("/bug")
public BugResource getBugResource() {
    return resourceContext.getResource(BugResource.class);
}
@ApplicationScoped
@Unremovable
public class BugResource {

    @GET
    @JsonView(Views.Public.class)
    public User hello() {
        User user = new User();
        user.setName("John");
        user.setPassword("password");
        return user;
    }
}
http://localhost:8080/hello/bug
{"name":"John","password":"password"}

Temporary workaround

Our current workaround is to inject ObjectMapper and manually serialize to a String but it gets pretty ugly as we add @JsonView to more endpoints:

@Inject
ObjectMapper objectMapper;

// endpoint must now return a String instead of User
public String bug() throws JsonProcessingException {
        User user = new User();
        user.setName("John");
        user.setPassword("password");
        return objectMapper.writerWithView(Views.Public.class)
                .writeValueAsString(user);
}

Expected behavior

@JsonView annotation on endpoints should work regardless if endpoint is top-level resource or sub-resource.

Actual behavior

@JsonView annotation does not work in sub-resources.

How to Reproduce?

  1. Generate code-with-quarkus project using Quarkus CLI
  2. Add quarkus-resteasy-reactive-jackson dependency to pom.xml
  3. Modify GreetingResource to return a sub-resource:
@Path("/bug")
public BugResource getBugResource() {
    return resourceContext.getResource(BugResource.class);
}
  1. In the sub-resource, add a simple @GET endpoint with a @JsonView annotation.
    @GET
    @JsonView(Views.Public.class)
    public User hello() {
        User user = new User();
        user.setName("John");
        user.setPassword("password");
        return user;
    }
  1. Add User.java and Views.java

    public class User {
    
    private String name;
    @JsonView(Views.Private.class)
    private String password;
    
    // getters and setters
    }

Same as documentation:

public class Views {
    public static class Public {}
    public static class Private extends Public {}
}

Open browser: http://localhost:8080/hello/bug

Output of uname -a or ver

No response

Output of java -version

No response

Quarkus version or git rev

3.5.1

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

Bug has existed since at least 2.16.6.Final. I was hoping the upgrade to 3.5.1 would fix it.

gsmet commented 9 months ago

Could you provide a small Maven reproducer? That will be easier for us to reproduce. Thanks!

quarkus-bot[bot] commented 9 months ago

/cc @FroMage (resteasy-reactive), @geoand (resteasy-reactive), @stuartwdouglas (resteasy-reactive)

jonjanisch commented 9 months ago

I've attached the maven project to the original description above. Let me know if you need anything else! Thanks. P.S. Ignore the weird indentation in GreetingResource, I removed indentation to copy the code into github! 🙂

geoand commented 9 months ago

Interesting use case for sure. I don't have a lot of time these days, but I'll try to address it at some point.

jonjanisch commented 9 months ago

@geoand Thank you! Of course we're not using it to hide password that's just example code.

Or did you mean usage of sub-resource? If it's the latter I'm curious. I would have imagined most non-trivial use cases are using sub-resources. We have around 100 resource classes currently with only a single root resource that delegates to smaller resources as we're primarily using Quarkus to build an app and not for microservices.

geoand commented 9 months ago

I meant the sub-resources