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

Serialization to openapi of org.springframework.data.domain.Sort is wrong for Spring Boot >2.x #2725

Closed
derXear opened this issue Sep 25, 2024 · 2 comments
Labels
incomplete incomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController

Comments

@derXear
Copy link

derXear commented Sep 25, 2024

The serialization of org.springframework.data.domain.Sort seems to be wrong with v2.6.0

Minimal reproducable example

With the following versions

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.2.10</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <dependency>
	    <groupId>org.springdoc</groupId>
	    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
	    <version>2.6.0</version>
    </dependency>
    ...
</dependencies>

and a rest controller like this

@RestController
@RequestMapping(value = {"/api/test"})
public class TestController {

    @GetMapping
    public Page<String> getTestItemsPaged(
      @RequestParam(defaultValue = "0") int page,
      @RequestParam(defaultValue = "5") int size
    ) {
        String[] items = new String[20];
        for (int i = 0; i < 20; i++) {
            items[i] = String.valueOf(i);
        }
        int offset = page * size;
        int limit = Math.min(size, items.length - offset);
        List<String> result = Arrays.asList(items).subList(offset, offset + limit);
        return new PageImpl<>(result, PageRequest.of(page, size), items.length);
    }
}

when starting the application and checking the endpoint: http://localhost:8080/v3/api-docs

we get this

{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenAPI definition",
    "version": "v0"
  },
  "servers": [
    {
      "url": "http://localhost:8080",
      "description": "Generated server url"
    }
  ],
  "paths": {
    "/api/test": {
      "get": {
        "tags": [
          "test-controller"
        ],
        "operationId": "getTestItemsPaged",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/PageString"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PageString": {
        "type": "object",
        "properties": {
          "totalPages": {
            "type": "integer",
            "format": "int32"
          },
          "totalElements": {
            "type": "integer",
            "format": "int64"
          },
          "pageable": {
            "$ref": "#/components/schemas/PageableObject"
          },
          "first": {
            "type": "boolean"
          },
          "last": {
            "type": "boolean"
          },
          "size": {
            "type": "integer",
            "format": "int32"
          },
          "content": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "number": {
            "type": "integer",
            "format": "int32"
          },
          "sort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SortObject"
            }
          },
          "numberOfElements": {
            "type": "integer",
            "format": "int32"
          },
          "empty": {
            "type": "boolean"
          }
        }
      },
      "PageableObject": {
        "type": "object",
        "properties": {
          "paged": {
            "type": "boolean"
          },
          "pageNumber": {
            "type": "integer",
            "format": "int32"
          },
          "pageSize": {
            "type": "integer",
            "format": "int32"
          },
          "offset": {
            "type": "integer",
            "format": "int64"
          },
          "sort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SortObject"
            }
          },
          "unpaged": {
            "type": "boolean"
          }
        }
      },
      "SortObject": {
        "type": "object",
        "properties": {
          "direction": {
            "type": "string"
          },
          "nullHandling": {
            "type": "string"
          },
          "ascending": {
            "type": "boolean"
          },
          "property": {
            "type": "string"
          },
          "ignoreCase": {
            "type": "boolean"
          }
        }
      }
    }
  }
}

but calling the Rest endpoint

curl --request GET \
  --url 'http://localhost:8080/api/test?size=10'

results in this response

{
  "content": [
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9"
  ],
  "pageable": {
    "pageNumber": 0,
    "pageSize": 10,
    "sort": {
      "sorted": false,
      "empty": true,
      "unsorted": true
    },
    "offset": 0,
    "paged": true,
    "unpaged": false
  },
  "last": false,
  "totalPages": 2,
  "totalElements": 20,
  "first": true,
  "size": 10,
  "number": 0,
  "sort": {
    "sorted": false,
    "empty": true,
    "unsorted": true
  },
  "numberOfElements": 10,
  "empty": false
}

There is a mismatch for the representation of the sort object in the generated swagger json. Since Spring Boot 2.x the response for sort is an object and not an array anymore.

I guess the issue was introduced with a fix for #2447 which will only work for Spring Boot 1.x but not for Spring Boot 2.x anymore. See the changes in more detail here:
https://www.wimdeblauwe.com/blog/2018/2018-06-10-pageimpl-json-serialization-with-spring-boot-2/

@bnasslahsen
Copy link
Contributor

@derXear,

Not sure what your problem is.

Feel free to provide a Minimal, Reproducible Example - with HelloController that reproduces the problem. Try showing your problem with failing test case.

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

@bnasslahsen bnasslahsen added the incomplete incomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController label Sep 25, 2024
@derXear
Copy link
Author

derXear commented Sep 26, 2024

@bnasslahsen thanks for your reply, I updated my original comment and added a minimal example with all steps to reproduce the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
incomplete incomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController
Projects
None yet
Development

No branches or pull requests

2 participants