OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.42k stars 6.48k forks source link

[REQ] [Java][Spring] Return 'Page<Model>' instead of 'List<Model>' when using 'x-spring-paginated' #14062

Open tofi86 opened 1 year ago

tofi86 commented 1 year ago

Is your feature request related to a problem? Please describe.

When using Spring Data Pageable in classes generated with the Spring generator and x-spring-paginated: true I would expect the return type to be Page<Model> instead of List<Model>.

Describe the solution you'd like

The Spring generator should return Page<Model> instead of List<Model> when using x-spring-paginated: true.

This has also been requested by @amelnikoff at https://github.com/OpenAPITools/openapi-generator/issues/8315#issuecomment-797010332

miberen commented 1 year ago

This would be amazing, please.

remcocats commented 10 months ago

Is there any progress on this? What is the workaround for this?

miberen commented 10 months ago

Is there any progress on this? What is the workaround for this?

The only workaround i found was to enable custom templates in the generator, and then modify the ones they provide, to add in the correct code. I basically added in some checks to see if that vendor extension is present, and then insert the Pageable in the correct place instead of the default. Of course you will be missing the pageable parameter in the input spec, but we just use the generated openapi file instead, as this will include the actual parameters.

remcocats commented 10 months ago

The pageable is not the problem for me the problem is the return type of Page. For pageable you can use the importMapping and map Pageable (inside openapi contract) to org.springframework.data.domain.Pageable that works fine. But Page is a complex interface.

miberen commented 10 months ago

You can also edit the return type of the generated classes using custom mustache templates.

amelnikoff commented 10 months ago

@miberen whole idea of extension x-spring-paginated is to use out of the box Spring's pagination mechanism without extra efforts.

miberen commented 10 months ago

@amelnikoff Yes I agree. But the discussion here is how to work around the fact that currently, you cannot do this in a proper manner.

daniel-shuy commented 10 months ago

The workaround I have been using is to create a schema that has the same properties as Page, then creating a schema that $refs the page schema, and using MapStruct to map a Page to the generated model class.

I've created common components for Spring pagination in SwaggerHub: https://app.swaggerhub.com/apis/spring/pagination

See https://github.com/daniel-shuy/swaggerhub-spring-pagination for usage instructions.

Example OpenAPI specification:

paths:
  /pet:
    get:
      tags:
        - pet
      operationId: findPets
      x-spring-paginated: true
      parameters:
        - $ref: 'https://api.swaggerhub.com/apis/spring/pagination/1.0.0#/components/parameters/pageParam'
        - $ref: 'https://api.swaggerhub.com/apis/spring/pagination/1.0.0#/components/parameters/sizeParam'
        - $ref: 'https://api.swaggerhub.com/apis/spring/pagination/1.0.0#/components/parameters/sortParam'
      responses:
        '200':
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PetPage'

components:
  schemas:
    Pet:
      # ...
    PetPage:
      type: object
      allOf:
        - $ref: 'https://api.swaggerhub.com/apis/spring/pagination/1.0.0#/components/schemas/PaginationResponse'
        - type: object
          properties:
            content:
              type: array
              items:
                $ref: '#/components/schemas/Pet'
              default: []

Example Controller implementation:

@RestController
public class PetController implements PetApi {
  private final PetMapper petMapper;

  // ...

  @Override
  public ResponseEntity<PetPage> findPets(Integer page, Integer size, List<String> sort, Pageable pageable) {
    Page<Pet> pets = ...
    PetPage petPage = petMapper.mapToDto(pets);
    return ResponseEntity.ok(petPage);
  }
}

@Mapper
public interface PetMapper {
  PetPage mapToDto(Page<Pet> pets);
}
remcocats commented 10 months ago

@daniel-shuy thanks for the examples I will try it out.

amelnikoff commented 10 months ago

@amelnikoff Yes I agree. But the discussion here is how to work around the fact that currently, you cannot do this in a proper manner.

In my case I have openapi file generated long time agio with SpringFox it provided return types for Spring pages: PageOfEntityDto1, PageOfEntityDto2, I simply launching sed with replace regex PageOf(.*) to Page<$1> in build script.