Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Despite having the JsonView specified, the openAPI response is resulting with all the objects of the entity #1724

Closed
VarunReddy1111 opened this issue Jan 24, 2024 · 8 comments · Fixed by #1746

Comments

@VarunReddy1111
Copy link

Having specified with different view classes in JsonView annotation while doing a Rest API call, the openAPI response is containing all the objects of the entity, instead of just the provided JsonViews leading to a nested object structure and resulting a big json file. Also, causing the swagger page to freeze and crash when expanding the APIs.

openapi.txt

@MikeEdgar
Copy link
Member

Hi @VarunReddy1111 , if you can provide a simple project that demonstrates the issue, that will help us find the problem.

@VarunReddy1111
Copy link
Author

VarunReddy1111 commented Jan 25, 2024

Thank you for the response @MikeEdgar , below is code that demonstrates the issue

@Path("/car")
@Tag(name = "Car Endpoints")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface CarInterface extends BaseSearchableInterface<CarEntity>,  BaseCrudUpsertInterface<CarEntity>, BaseCrudIdInterface<CarEntity>, BaseCrudStringInterface<CarEntity>  {
}

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface BaseCrudIdInterface<E extends BaseEntity> {

	@POST
	@Path("/")
	@JsonView({ View.CreateView.class })
	public ObjectResponce<E> create(E entity);

	@GET
	@Path("/{id}")
	@JsonView({ View.ReadView.class })
	public ObjectResponce<E> read(@PathParam("id") Long id);

	@PUT
	@Path("/")
	@JsonView({ View.UpdateView.class })
	public ObjectResponce<E> update(E entity);

	@DELETE
	@Path("/{id}")
	@JsonView({ View.DeleteView.class })
	public ObjectResponce<E> delete(@PathParam("id") Long id);
	
}


@RequestScoped
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CarController extends BaseSearchController<CarEntity> implements CarInterface {
}



@Data
@Schema(name = "ObjectResponce", description = "POJO that represents the ObjectResponce")
public class ObjectResponce<E extends BaseEntity> {
	@JsonView(View.ReadView.class)
	private E entity;
}


@Data @EqualsAndHashCode(callSuper = true)
@Schema(name = "CarEntity", description = "POJO that represents the CarEntity")
public class CarEntity extends BaseSearchableEntity {
	@JsonView(View.ReadView.class)
	private String hood;

	@JsonView(View.CreateView.class)
	public String name;

	@JsonView(View.UpdateView.class)
	public String model;
}

@Data @EqualsAndHashCode(callSuper = true)
public class BaseSearchableEntity extends BaseEntity {
	private String bodyType;
}

I am using quarkus 3.5.0. When I go to swagger page, I see the endpoints of different methods, whenever I expand the endpoint, I see a Example value Schema section, where I am getting all the objects of the class and all of its attributes in super class instead of only retrieving the attributes which were specified with View class in JsonView annotation.

In the above code, I have CarEntity with 3 attributes, each having view class specified in JsonView annotation and in the BaseIdCrudInterface, the JsonView specifies only ReadView class. But the response schema is having all the attributes of the class as in the below image.

The project I am working consists of a lot of nested classes, where the example value schema section is resulting in large json, leading to freeze and crashing of the page.

Below is the image illustrating the issue.

image

Sample project displaying this behaviour: https://gitlab.com/nilosedge/inheritance/-/tree/Swagger-Issue?ref_type=heads

OpenAPI Yaml file:


openapi: 3.0.3
info:
  title: This is to Test Interface inheritance
  description: This is to Test Interface inheritance
  version: 1.0 Alpha
security:
- api_token: []
tags:
- name: Car Endpoints
- name: Truck Endpoints
paths:
  /api/car:
    put:
      tags:
      - Car Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CarEntity'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce1'
    post:
      tags:
      - Car Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CarEntity'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce4'
  /api/car/search:
    post:
      tags:
      - Car Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchResponce1'
  /api/car/upsert:
    post:
      tags:
      - Car Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CarEntity'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce4'
  /api/car/{id}:
    get:
      tags:
      - Car Endpoints
      parameters:
      - name: id
        in: path
        required: true
        schema:
          format: int64
          type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce2'
    delete:
      tags:
      - Car Endpoints
      parameters:
      - name: id
        in: path
        required: true
        schema:
          format: int64
          type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce3'
  /api/car/{string}:
    get:
      tags:
      - Car Endpoints
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce2'
    delete:
      tags:
      - Car Endpoints
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce3'
  /api/truck:
    put:
      tags:
      - Truck Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TruckEntity'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce6'
    post:
      tags:
      - Truck Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TruckEntity'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce5'
  /api/truck/upsert:
    post:
      tags:
      - Truck Endpoints
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TruckEntity'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce5'
  /api/truck/{id}:
    get:
      tags:
      - Truck Endpoints
      parameters:
      - name: id
        in: path
        required: true
        schema:
          format: int64
          type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce7'
    delete:
      tags:
      - Truck Endpoints
      parameters:
      - name: id
        in: path
        required: true
        schema:
          format: int64
          type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectResponce8'
components:
  schemas:
    BaseEntity:
      type: object
    CarEntity:
      description: POJO that represents the CarEntity
      type: object
      properties:
        bodyType:
          type: string
        hood:
          type: string
        name:
          type: string
        model:
          type: string
    CarEntity_CreateView_FieldsOnly:
      description: POJO that represents the CarEntity
      type: object
      properties:
        bodyType:
          type: string
        name:
          type: string
        hood:
          type: string
        model:
          type: string
    CarEntity_DeleteView_FieldsOnly:
      description: POJO that represents the CarEntity
      type: object
      properties:
        bodyType:
          type: string
        hood:
          type: string
        name:
          type: string
        model:
          type: string
    CarEntity_FieldsOnly:
      description: POJO that represents the CarEntity
      type: object
      properties:
        bodyType:
          type: string
        hood:
          type: string
        name:
          type: string
        model:
          type: string
    CarEntity_ReadView_FieldsOnly:
      description: POJO that represents the CarEntity
      type: object
      properties:
        bodyType:
          type: string
        hood:
          type: string
        name:
          type: string
        model:
          type: string
    CarEntity_UpdateView_FieldsOnly:
      description: POJO that represents the CarEntity
      type: object
      properties:
        bodyType:
          type: string
        model:
          type: string
        hood:
          type: string
        name:
          type: string
    ObjectResponce:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/BaseEntity'
    ObjectResponce1:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_UpdateView_FieldsOnly'
    ObjectResponce2:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_ReadView_FieldsOnly'
    ObjectResponce3:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_DeleteView_FieldsOnly'
    ObjectResponce4:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_CreateView_FieldsOnly'
    ObjectResponce5:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/TruckEntity_CreateView_FieldsOnly'
    ObjectResponce6:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/TruckEntity_UpdateView_FieldsOnly'
    ObjectResponce7:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/TruckEntity_ReadView_FieldsOnly'
    ObjectResponce8:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/TruckEntity_DeleteView_FieldsOnly'
    ObjectResponce_CreateView_FieldsOnly:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_CreateView_FieldsOnly'
    ObjectResponce_DeleteView_FieldsOnly:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_DeleteView_FieldsOnly'
    ObjectResponce_ReadView_FieldsOnly:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_ReadView_FieldsOnly'
    ObjectResponce_UpdateView_FieldsOnly:
      description: POJO that represents the ObjectResponce
      type: object
      properties:
        entity:
          $ref: '#/components/schemas/CarEntity_UpdateView_FieldsOnly'
    SearchRequest:
      type: object
    SearchResponce:
      description: POJO that represents the SearchResponce
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/BaseEntity'
    SearchResponce1:
      description: POJO that represents the SearchResponce
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/CarEntity_FieldsOnly'
    SearchResponce_FieldsOnly:
      description: POJO that represents the SearchResponce
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/CarEntity_FieldsOnly'
    TruckEntity:
      description: POJO that represents the TruckEntity
      type: object
      properties:
        bodyType:
          type: string
        bedType:
          type: string
    TruckEntity_CreateView_FieldsOnly:
      description: POJO that represents the TruckEntity
      type: object
      properties:
        bodyType:
          type: string
        bedType:
          type: string
    TruckEntity_DeleteView_FieldsOnly:
      description: POJO that represents the TruckEntity
      type: object
      properties:
        bodyType:
          type: string
        bedType:
          type: string
    TruckEntity_ReadView_FieldsOnly:
      description: POJO that represents the TruckEntity
      type: object
      properties:
        bodyType:
          type: string
        bedType:
          type: string
    TruckEntity_UpdateView_FieldsOnly:
      description: POJO that represents the TruckEntity
      type: object
      properties:
        bodyType:
          type: string
        bedType:
          type: string
  securitySchemes:
    api_token:
      type: http
      description: API Token
      scheme: bearer

@MikeEdgar
Copy link
Member

The response object has a @JsonView on the entity. The example is missing the View classes so I can't see how they relate to each other, but I think this annotation is interfering with whatever you have annotated the REST methods with.

@Data
@Schema(name = "ObjectResponce", description = "POJO that represents the ObjectResponce")
public class ObjectResponce<E extends BaseEntity> {
	@JsonView(View.ReadView.class)
	private E entity;
}

@VarunReddy1111
Copy link
Author

The below is the View class for above example:

public class View {

	public static class FieldsOnly { }
	
	public static class CreateView extends FieldsOnly { }
	public static class ReadView extends FieldsOnly { }
	public static class UpdateView extends FieldsOnly { }
	public static class DeleteView extends FieldsOnly { }
}

I have also tried removing/changing the annotations(@JsonView and @Schema) of ObjectResponce class, but issue persists.

@VarunReddy1111
Copy link
Author

VarunReddy1111 commented Feb 7, 2024

This is a sample project displaying this behaviour: https://gitlab.com/nilosedge/inheritance/-/tree/Swagger-Issue?ref_type=heads . Please let me know if you need any other inputs.

@MikeEdgar
Copy link
Member

@VarunReddy1111 , the problem is that the annotation scanner is using the Lombok generated un-annotated methods rather than the field. You can work around it like with something like this until it's fixed:

@Getter(onMethod=@__({@JsonView(View.ReadView.class)}))
@Setter(onMethod=@__({@JsonView(View.ReadView.class)}))

@VarunReddy1111
Copy link
Author

Thanks for above resolution @MikeEdgar . Currently, we have another issue, which is crashing of swagger page due to large response bodies, do you have any solutions for such kind of issues like having a save/download button or limiting the size or truncating the response?

@MikeEdgar
Copy link
Member

@VarunReddy1111 , I'm not sure about that. You might find a solution in the swagger-ui project though.

https://github.com/swagger-api/swagger-ui

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants