Automatically adding #ImplicitParams with specific type of method argument of Spring Controller - spring

Previously, I had Spring controller as below.
#RequestMapping(method = GET)
public List<TheResponse> getResponses(
#RequestParam(defaultValue = "1") int offset,
#RequestParam(defaultValue = "10") int limit) {
Pagination pagination = new Pagination(offset, limit);
...
return someResponse;
}
Swagger was generating document of this method with correct parameters information.
Later I created PaginationArgResolver by implementing HandlerMethodArgumentResolver. After that, I have to update the method as below, and apply #ApiImplicitParams to make it work with Swagger.
#ApiImplicitParams({
#ApiImplicitParam(name = "offset", dataType = "int", defaultValue = "1"),
#ApiImplicitParam(name = "limit", dataType = "int", defaultValue = "10")
})
#RequestMapping(method = GET)
public List<TheResponse> getResponses(#ApiIgnore Pagination pagination) {
...
}
Is there anyway #ImplicitParams is applied automatically whenever Pagination type argument is found?
OR
If I expose #PaginationSupported annotation, can I process it to achieve same results?
I am currently using springfox v2.4.0.
PS. I can edit source of Pagination class, in case some swagger annotation is needed there.

Why adding #ApiIgnore springfox will resolve these attributes inside the class automatically. When you want to add default values and other stuff you can add the #ApiParam annotation to the class attributes as well.
class Pagination {
#ApiParam(defaultValue = "1")
private int offset;
// [..]
}

Related

InvalidPathException while sorting with org.springframework.data.domain.Pageable

I am trying to sort my table's content on the backend side, so I am sending org.springframework.data.domain.Pageable object to controller. It arrives correctly, but at the repository I am getting org.hibernate.hql.internal.ast.InvalidPathException. Somehow the field name I would use for sorting gets an org. package name infront of the filed name.
The Pageable object logged in the controller:
Page request [number: 0, size 10, sort: referenzNumber: DESC]
Exception in repository:
Invalid path: 'org.referenzNumber'","logger_name":"org.hibernate.hql.internal.ast.ErrorTracker","thread_name":"http-nio-8080-exec-2","level":"ERROR","level_value":40000,"stack_trace":"org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'org.referenzNumber'\n\tat org.hibernate.hql.internal.ast.util.LiteralProcessor.lookupConstant(LiteralProcessor.java:111)
My controller endpoint:
#GetMapping(value = "/get-orders", params = { "page", "size" }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<PagedModel<KryptoOrder>> getOrders(
#ApiParam(name = "searchrequest", required = true) #Validated final OrderSearchRequest orderSearchRequest,
#PageableDefault(size = 500) final Pageable pageable, final BindingResult bindingResult,
final PagedResourcesAssembler<OrderVo> pagedResourcesAssembler) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().build();
}
PagedModel<Order> orderPage = PagedModel.empty();
try {
var orderVoPage = orderPort.processOrderSearch(resourceMapper.toOrderSearchRequestVo(orderSearchRequest), pageable);
orderPage = pagedResourcesAssembler.toModel(orderVoPage, orderAssembler);
} catch (MissingRequiredField m) {
log.warn(RESPONSE_MISSING_REQUIRED_FIELD, m);
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(orderPage);
}
the repository:
#Repository
public interface OrderRepository extends JpaRepository<Order, UUID> {
static final String SEARCH_ORDER = "SELECT o" //
+ " FROM Order o " //
+ " WHERE (cast(:partnerernumber as org.hibernate.type.IntegerType) is null or o.tradeBasis.account.retailpartner.partnerbank.partnerernumber = :partnerernumber)"
+ " and (cast(:accountnumber as org.hibernate.type.BigDecimalType) is null or o.tradeBasis.account.accountnumber = :accountnumber)"
+ " and (cast(:orderReference as org.hibernate.type.LongType) is null or o.tradeBasis.referenceNumber = :orderReference)"
+ " and (cast(:orderReferenceExtern as org.hibernate.type.StringType) is null or o.tradeBasis.kundenreferenceExternesFrontend = :orderReferenceExtern)"
+ " and (cast(:dateFrom as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp > :dateFrom) "
+ " and (cast(:dateTo as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp < :dateTo) ";
#Query(SEARCH_ORDER)
Page<Order> searchOrder(#Param("partnerernumber") Integer partnerernumber,
#Param("accountnumber") BigDecimal accountnumber, #Param("orderReference") Long orderReference,
#Param("orderReferenceExtern") String orderReferenceExtern, #Param("dateFrom") LocalDateTime dateFrom,
#Param("dateTo") LocalDateTime dateTo, Pageable pageable);
}
Update:
I removed the parameters from the sql query, and put them back one by one to see where it goes sideways. It seems as soon as the dates are involved the wierd "org." appears too.
Update 2:
If I change cast(:dateTo as org.hibernate.type.DateType) to cast(:dateFrom as date) then it appends the filed name with date. instead of org..
Thanks in advance for the help
My guess is, Spring Data is confused by the query you are using and can't properly append the order by clause to it. I would recommend you to use a Specification instead for your various filters. That will not only improve the performance of your queries because the database can better optimize queries, but will also make use of the JPA Criteria API behind the scenes, which requires no work from Spring Data to apply an order by specification.
Since your entity Order is named as the order by clause of HQL/SQL, my guess is that Spring Data tries to do something stupid with the string to determine the alias of the root entity.

Hidden parameter in springdoc-openapi doesn't work

I have Spring Boot application with version 2.3.0.
and springdoc-openapi-webflux-ui in version 1.4.1.
I have annotated parameter in operation like this.
parameters = {
#Parameter(
hidden = true, schema = #Schema(implementation = Boolean.class),
in = ParameterIn.QUERY, name = DO_NOT_FORWARD
)
With hidden = true I expected that this parameter will not be visible in swagger-ui. But it is.
Did I misunderstood this parameter or is it not doing what it was supposed to do?
I want this parameter to be in api-docs, to have generated client able to use this parameter, but I want it invisible in swagger-ui
Try
#Parameter(name = "paramName", hidden = true)
#GetMapping("/example")
public Object example(String paramName) {
return null;
}
instead of
#Operation(
parameters = {
#Parameter(name = "paramName", hidden = true)
}
)
#GetMapping("/example")
public Object example(String paramName) {
return null;
}
You just make sure that the name of in the #Parameter annotation, is the exact name of the operation parameter you want to hide.
You can have a look at the documentation as well:
https://springdoc.org/faq.html#how-can-i-hide-a-parameter-from-the-documentation-
If you are still having coniguration issue, you can add the code of sample HelloController that reproduces your problem, or you can add the link to a minimal, reproducible sample in github.
As per the doc
#GetMapping("/example")
fun someCall: Response (
#Parameter(hidden = true) String paramName
) {
return response;
}

How to give a swagger parameter default value?

I want somes params on swagger have its own default value. Is there anyway to give a default value ?
like this
this note has to be filled automatically with "stackoverflow"
You have to annotate your note parameter with defaultValue="stackoverflow".
You method signature should look like this:
public ResponseEntity<?> yourMethod(#ApiParam(value = "Default value for note", required = true, defaultValue = "stackoverflow") #Valid #RequestParam final String note) { }

Using #RequestParam annotated method with swagger ui

I am using Springfox libraries to generate documentation for REST service and display it in Swagger UI. I followed the directions in Springfox documentation.
I have one controller, which uses parameters from query string and the method is mapped as following:
#ApiOperation(value = "")
#RequestMapping(method = GET, value = "/customcollection/{id}/data")
public Iterable<CustomeType> getData(#ApiParam(value = "The identifier of the time series.")
#PathVariable String id,
#ApiParam(name = "startDate", value = "start date", defaultValue = "")
#RequestParam("startDate") String startDate,
#ApiParam(name = "endDate", value = "end date", defaultValue = "")
#RequestParam("endDate") String endDate)
The resulting mapper in swagger-ui then displayed as:
GET /customcollection/{id}/data{?startDate,endDate}
Parameters are displayed correctly in the UI:
But when I click on Try it Out, the request URL is misformed:
http://localhost:8080/customcollection/1/data{?startDate,endDate}?startDate=1&endDate=2
How can it be fixed?
This was caused by the line
enableUrlTemplating(true)
in Docket configuration which I copied from example and forgot to remove.
After removing this line everything is working as expected.

Spring Matrix Variables require at least one template variable to work?

I've been trying to get a simple REST API to list contents of a collection and I'm using matrix variables to control the pagination.
My controller has the following method to list contents of a collection:
#RequestMapping(
value = "articles",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ArticlePageRestApiResponse listArticles(
#MatrixVariable(required = true, defaultValue = 100, value = "resultsPerPage") int resultsPerPage,
#MatrixVariable(required = true, defaultValue = 0, value = "pageNumber") int pageNumber) {
// some logic to return the collection
}
If I then do a GET http://example.com/articles;resultsPerPage=22;pageNumber=33 it fails to find a request mapping. I have enabled matrix variables support by adding the following:
#Configuration
public class EnableUriMatrixVariableSupport extends WebMvcConfigurationSupport {
#Override
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping hm = super.requestMappingHandlerMapping();
hm.setRemoveSemicolonContent(false);
return hm;
}
}
What I've found is that if the matrix variables are prefixed with at least one template variable then the matrix variables are correctly assigned. The following works but is ugly where I've had to make part of the URI path a template variable that is always going to be "articles" to trick the Request Mapping Handler into thinking there is at least one URI template variable :
#RequestMapping(
value = "{articles}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ArticlePageRestApiResponse listArticles(
#PathVariable("articles") String ignore,
#MatrixVariable(required = true, defaultValue = 100, value = "resultsPerPage") int resultsPerPage,
#MatrixVariable(required = true, defaultValue = 0, value = "pageNumber") int pageNumber) {
// some logic to return the collection
}
Have I found a bug or am I mis-understanding matrix variables?
According to Spring documentation
If a URL is expected to contain matrix variables, the request mapping
pattern must represent them with a URI template. This ensures the
request can be matched correctly regardless of whether matrix
variables are present or not and in what order they are provided.
In your first example you use no templates (like {articles}) in URL mapping, so Spring is unable to detect matrix parameters.
I'd rather call it not a bug, but an implemnetation side effect. We have it just because #MatrixVariable support is build on the top of the old #PathVariable parsing mechanism.

Resources