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

o.s.data.domain.Pageable - automatically add @ApiImplicitParams? #755

Closed
steve-oakey opened this issue May 26, 2015 · 64 comments
Closed

o.s.data.domain.Pageable - automatically add @ApiImplicitParams? #755

steve-oakey opened this issue May 26, 2015 · 64 comments

Comments

@steve-oakey
Copy link

steve-oakey commented May 26, 2015

Hi,

I have been looking for but haven't found a way to automatically the implicit parameters of the Pageable parameter. It would be great to have the page, size and sort parameters automatically added to the Swagger documentation. I have tried Docket#directModelSubstitute but this has not worked.

Any insight to this?

@tonisostrat
Copy link

I had the same problem in a Spring project and from what I could find there is no "automagical" way to do this. You have two options:

  • Add @ApiIgnore to the Pageable and then specify each element as @ApiImplicitParam
  • Change the method signature to accept each element of the Pageable as a separate argument

Support for this is debatable. Pageable is a Spring-specific construct and who's to say it will never change? Baking something like this into a general framework sounds counter-productive.

@steve-oakey
Copy link
Author

I agree, I am not suggesting that specific support for Pageable be written in. I was wondering if there was something in the Docket builder where I could configure my bean to say "if you see Pageable, use this class instead", where "this" class could be interpreted as request parameters (in the case of Pageable a class with page, size and sort fields).

@dilipkrish dilipkrish added this to the 2.1 milestone Jun 4, 2015
@dilipkrish
Copy link
Member

This should be fixed via #845

@steve-oakey
Copy link
Author

Thanks!

@zdila
Copy link

zdila commented Aug 26, 2015

It seems #845 only adds global parameters to every operation. I've found no way to specify that the parameter should be used only when Pageable has been found. Am I missing something or is it really not implemented?

@dilipkrish
Copy link
Member

@zdila This was to only added implicit parameters to all operations. In your case it doesn't seem like the right solution. In your case, I would simply use an alternate type rule for Pageable (see 10).

@zdila
Copy link

zdila commented Aug 26, 2015

OK. The description of this issue addresses exactly what I need but the provided solution can't be used.

I tried also the alternate type rule, but I can't use it to solve the problem.

What is needed is that for eg. org.springframework.data.domain.Pageable method parameter there should be created three springfox.documentation.service.Parameters - for page, offset and sort, all with paramType = "query".

@dilipkrish
Copy link
Member

Assuming that you're creating a concrete pageable object MyRequest, adding setters for your page/offset/sort etc. OR _jackson_ifying it a little should do the trick

class MyRequest extends AbstractPageRequest {
@JsonCreator
 MyRequest(@JsonProperty("page) int page, @JsonProperty("size") int size) { super(...)}

 //additional bean properties with getters and setters
}
``

@zdila
Copy link

zdila commented Aug 26, 2015

Pageable object is being created by Spring MVC.

@dilipkrish
Copy link
Member

In which case you'd create a Jackson mixin

@zdila
Copy link

zdila commented Aug 26, 2015

How can I tell the mixin properties that they should be represented as query paramerers?

Currently I have this on many places:

    @ApiOperation(value = "Find partners")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "page", dataType = "integer", paramType = "query",
                value = "Results page you want to retrieve (0..N)"),
        @ApiImplicitParam(name = "size", dataType = "integer", paramType = "query",
                value = "Number of records per page."),
        @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query",
                value = "Sorting criteria in the format: property(,asc|desc). " +
                        "Default sort order is ascending. " +
                        "Multiple sort criteria are supported.")
    })
    @RequestMapping(value = "/v1/partners", method = RequestMethod.GET)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public List<Partner> findAll(@ApiIgnore final Pageable pageable) {
            ...
    }

I want to get rid of @ApiImplicitParams.

@zdila
Copy link

zdila commented Aug 27, 2015

I think this issue should be reopened.

@dilipkrish
Copy link
Member

I think you're 💯% correct.

@dilipkrish dilipkrish reopened this Aug 27, 2015
@dilipkrish dilipkrish modified the milestones: 2.3.0, 2.2.0 Aug 27, 2015
@dilipkrish dilipkrish modified the milestones: 2.4.0, 2.3.0 Sep 10, 2015
@oembedler
Copy link

@dilipkrish
do you have any ETA on when this feature is implemented? I see target release 2.4.0 but not sure what time frame for this.

thanks

@dilipkrish
Copy link
Member

milestones_-_springfox_springfox

@dilipkrish dilipkrish modified the milestones: 2.4.0, 2.5.0 Feb 9, 2016
lpellegr added a commit to ow2-proactive/workflow-catalog-old that referenced this issue Feb 16, 2016
The Spring pageable interface is not recognized by default with Swagger.

It is a known issue: springfox/springfox#755

The workaround is to use implicit parameters.
@marceloverdijk
Copy link

+1

@hartmut-pq
Copy link

Plans for 2.7.1 ? :-)

@dilipkrish
Copy link
Member

Did you try 2.7.0? I think this is covered for spring-data-rest

@hartmut-pq
Copy link

Hi @dilipkrish, finally had time to test with 2.7.0 - seems not to be working for me.
I'm not using spring-data-rest though - spring-data (spring-data-jpa) merely.

It's essentially a
org.springframework.data.domain.PageRequest (extends org.springframework.data.domain.AbstractPageRequest implements org.springframework.data.domain.Pageable)

and does not seem to resolve the properties page, size & sort. The OperationPageableParameterReader update @florianrusch provided works fine though...

@dilipkrish
Copy link
Member

dilipkrish commented Jun 12, 2017

@hartmut-pq Yeah thats the solution for non-spring-data-rest projects.

@kent2171
Copy link

kent2171 commented Jul 12, 2017

Hi folks, any news about this feature ? I also really need it, and I'm thinking about implement it, or wait for release

@dilipkrish
Copy link
Member

@kent2171 Its waiting for contributions. Would love it if you'd be able to help out by implementing it

@dilipkrish
Copy link
Member

Fixed via #2063

@Herzenkin
Copy link

@dilipkrish , still doesn't work with just plain "spring-data-jpa" on 2.8.0. Only with @florianrusch solution.

@dilipkrish
Copy link
Member

@Herzenkin spring-data-jpa isnt supported as-is. It would have to be spring-data-rest.

@rhanton
Copy link

rhanton commented May 11, 2018

@dilipkrish That's disappointing for us spring-data-jpa people.

@dilipkrish
Copy link
Member

@rhanton Not really sure, how that works. Is there a way to expose an JPA repository directly without having to write a controller or using spring-data-rest? Also its not that it will never be supported, there has just not been a demand for it.

@Herzenkin
Copy link

Herzenkin commented May 11, 2018

@dilipkrish , the question is exactly about writing custom controller with Pageable parameters which calls spring-data-jpa repository. It will be useful to "unbox" Pageable object in that case as it works with spring-data-rest repository.

@dilipkrish
Copy link
Member

Oh I see. That makes sense. That is supported via extensibility, you could either do something like how it is supported for spring data rest, or come up with a mixin class on your own and create an alternate type rule.

@cbornet
Copy link
Contributor

cbornet commented May 16, 2018

I think what @rhanton meant is that the fix doesn't work with basic spring-mvc (and spring-data-jpa for persistence). We still need to include the OperationBuilderPlugin of @florianrusch . It would be nice to have this class embedded in the lib. Either as a simple class, or as a @Component bean that can be imported.

@dilipkrish
Copy link
Member

@cbornet in case it wasn't obvious, you can create synthetic mixins as shown in the link above, modified snippet below...

private Type pageableMixin() {
    return new AlternateTypeBuilder()
        .fullyQualifiedClassName(
            String.format("%s.generated.%s",
                Pageable.class.getPackage().getName(),
                Pageable.class.getSimpleName()))
        .withProperties(newArrayList(
            property(Integer.class, "page"),
            property(Integer.class, "limit"),
            property(String.class, "sort")
        ))
        .build();
  }

Will create a rule that will replace Pageable with a mixin class with properties "page, limit, sort" hardcoded. Does that make sense?

@91wangmeng
Copy link

@dilipkrish i want size but i got pageSize when i use (@PageableDefault Pageable pageable)
snipaste_2018-08-01_18-19-59

@selvaebi
Copy link

selvaebi commented Aug 1, 2018

The default findAll has page,sort and size , but the endpoint doesnt have a effect for any of the provided values. it returns all the object without paging.

below is my pom,

            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
            <artifactId>springfox-data-rest</artifactId>
            <version>2.8.0</version>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>2.8.0</version>

my repository resource,

@RepositoryRestResource
public interface PhenotypeRepository extends CrudRepository<Phenotype, String> {
}

@dilipkrish
Copy link
Member

@91wangmeng you need to create a rule for the Pageable parameter with an alternate type like described here

@dilipkrish
Copy link
Member

Please upgrade to 2.9.2 @selvaebi

@vlsergeyatsbt
Copy link

vlsergeyatsbt commented Nov 20, 2018

Cleanup the existing parameters due to merge. Sadly, reflection is required.

import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.spi.schema.contexts.ModelContext.inputParam;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Function;
import com.google.common.collect.Sets;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.OperationBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelReference;
import springfox.documentation.schema.ResolvedTypes;
import springfox.documentation.schema.TypeNameExtractor;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.ParameterContext;

@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class OperationPageableParameterReader implements OperationBuilderPlugin {
    private final TypeNameExtractor nameExtractor;
    private final ResolvedType pageableType;
    private final TypeResolver resolver;

    @Autowired
    public OperationPageableParameterReader(TypeNameExtractor nameExtractor, TypeResolver resolver) {
        this.nameExtractor = nameExtractor;
        this.resolver = resolver;
        this.pageableType = resolver.resolve(Pageable.class);
    }

    @Override
    @SneakyThrows
    public void apply(OperationContext context) {
        List<ResolvedMethodParameter> methodParameters = context.getParameters();
        List<Parameter> parameters = newArrayList();

        for (ResolvedMethodParameter methodParameter : methodParameters) {
            ResolvedType resolvedType = methodParameter.getParameterType();
            if (!pageableType.equals(resolvedType)) {
                continue;
            }

            ParameterContext parameterContext = new ParameterContext(methodParameter, new ParameterBuilder(),
                    context.getDocumentationContext(), context.getGenericsNamingStrategy(), context);
            Function<ResolvedType, ? extends ModelReference> factory = createModelRefFactory(parameterContext);

            ModelReference intModel = factory.apply(resolver.resolve(Integer.TYPE));
            ModelReference stringModel = factory.apply(resolver.resolve(List.class, String.class));

            parameters.add(new ParameterBuilder().parameterType("query")
                    .name("page")
                    .modelRef(intModel)
                    .description("Results page you want to retrieve (0..N)")
                    .build());
            parameters.add(new ParameterBuilder().parameterType("query")
                    .name("size")
                    .modelRef(intModel)
                    .description("Number of records per page")
                    .build());
            parameters.add(new ParameterBuilder().parameterType("query")
                    .name("sort")
                    .modelRef(stringModel)
                    .allowMultiple(true)
                    .description("Sorting criteria in the format: property(,asc|desc). "
                            + "Default sort order is ascending. " + "Multiple sort criteria are supported.")
                    .build());

            final OperationBuilder operationBuilder = context.operationBuilder();
            operationBuilder.parameters(parameters);

            Set<String> toDelete = Sets.newLinkedHashSet(Arrays
                    .asList("offset", "pageNumber", "pageSize", "paged", "sort.sorted", "sort.unsorted", "unpaged"));
            final Field field = operationBuilder.getClass().getDeclaredField("parameters");
            field.setAccessible(true);
            @SuppressWarnings("unchecked")
            final List<Parameter> list = (List<Parameter>) field.get(operationBuilder);
            field.set(operationBuilder,
                    list.stream().filter(p -> !toDelete.contains(p.getName())).collect(Collectors.toList()));
        }
    }

    private Function<ResolvedType, ? extends ModelReference> createModelRefFactory(ParameterContext context) {
        ModelContext modelContext = inputParam(context.getGroupName(),
                context.resolvedMethodParameter().getParameterType(),
                context.getDocumentationType(),
                context.getAlternateTypeProvider(),
                context.getGenericNamingStrategy(),
                context.getIgnorableParameterTypes());
        return ResolvedTypes.modelRefFactory(modelContext, nameExtractor);
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}

@dilipkrish
Copy link
Member

@vlsergeyatsbt this is already supported in 2.9.2. Is it not working?

@selvaebi
Copy link

Please upgrade to 2.9.2 @selvaebi

sorry i used pagingAndSortingRepository , it worked in 2.8.0 , sorry for late reply.

@cbornet
Copy link
Contributor

cbornet commented Dec 18, 2018

Hi @dilipkrish , I tested the AlternateTypeRule which works fine except that I can't set the parameter description. Is there a way to set it ?

@prisin
Copy link

prisin commented Jan 24, 2019

@cbornet i am facing the same problem with AlternateTypeRule, is there any way to set description for pageable parameters ?

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

No branches or pull requests