springdoc / springdoc-openapi

Library for OpenAPI 3 with spring-boot
https://springdoc.org
Apache License 2.0
3.26k stars 493 forks source link

Mismatch between `org.springframework.data.domain.Page<ItemDto>` and the openapi spec #2630

Closed aconrad closed 3 months ago

aconrad commented 3 months ago

Describe the bug

When my controller returns Page<...>, the JSON response has the keys content (containing the paged items) and page (containing the pageable info) but the openapi spec shows a different schema with keys such as content (as expected) but also pageable, totalElements, totalPages, first, last, etc... (see full list below). This started happening when I upgraded org.springframework.boot from 3.2.6 to 3.3.0.

Why doesn't the openapi spec reflect the returned data?

To Reproduce Steps to reproduce the behavior:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.5.0</version>
        </dependency>

The openapi output (JSON, only the relevant part):

      "PageItemDto": {
        "type": "object",
        "properties": {
          "totalElements": {
            "type": "integer",
            "format": "int64"
          },
          "totalPages": {
            "type": "integer",
            "format": "int32"
          },
          "first": {
            "type": "boolean"
          },
          "last": {
            "type": "boolean"
          },
          "size": {
            "type": "integer",
            "format": "int32"
          },
          "content": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemDto"
            }
          },
          "number": {
            "type": "integer",
            "format": "int32"
          },
          "sort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SortObject"
            }
          },
          "numberOfElements": {
            "type": "integer",
            "format": "int32"
          },
          "pageable": {
            "$ref": "#/components/schemas/PageableObject"
          },
          "empty": {
            "type": "boolean"
          }
        }
      },
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;

    @GetMapping("/my-endpoint")
    public Page<ItemDto> getItems(
                @ParameterObject @PageableDefault(page = 0, size = 10) Pageable pageable) {

        // ...

        return new PageImpl<>(items, pageable, dbObjects.getTotalElements());
    }

The data returned by the controller:

{
    "content": [
        {
            // serialized ItemDto
        },
        // ...
    ],
    "page": {
        "size": 10,
        "number": 0,
        "totalElements": 48,
        "totalPages": 5
    }
}

Expected behavior

I expect the JSON output to describe PageItemDto with only 2 keys, content and page.

Something like this (handcrafted, sorry if typos):

      "PageItemDto": {
        "type": "object",
        "properties": {
          "content": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemDto"
            }
          },
          "page": {
            "$ref": "#/components/schemas/Page"
          }
        }
      },
      "Page": {
        "type": "object",
        "properties": {
          "totalElements": {
            "type": "integer",
            "format": "int64"
          },
          "totalPages": {
            "type": "integer",
            "format": "int32"
          },
          "size": {
            "type": "integer",
            "format": "int32"
          },
          "content": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemDto"
            }
          },
          "number": {
            "type": "integer",
            "format": "int32"
          },
      },

Screenshots If applicable, add screenshots to help explain your problem.

org.springframework.boot 3.3.0:

Screenshot 2024-06-18 at 6 56 28 PM

org.springframework.boot 3.2.6:

Screenshot 2024-06-18 at 6 58 18 PM

Additional context Add any other context about the problem here.

bnasslahsen commented 3 months ago

@aconrad,

Not reproducible. Feel free to provide a Minimal, Reproducible Example - with HelloController that reproduces the problem.

This ticket will be closed, but can be reopened if your provide the reproducible sample.

rszabolcs commented 3 months ago

We also experienced similar (probably buggy) behavior. After using the annotation @EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) the generated API doc does not represent the actual response structure.

The following issue might be related: https://github.com/springdoc/springdoc-openapi/issues/2625

naveenholla commented 1 month ago

@aconrad have you found the solution for the same in the latest 3.3.0 version of springboot and please share the same , i am still facing the issue in latest springboot 3.3.1 ,

          "sort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SortObject"
            }
          },