Hidden parameter in springdoc-openapi doesn't work - spring-boot

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;
}

Related

How to add enum for attribute so that it comes as dropdown in swagger ui using spring webflux,openapi 3

I am writing a reactive application using webflux with springdoc-openapi(v 1.4.3). Below is the router class
#Configuration
public class AppRouter {
#RouterOperation(path = "/app", produces = {
MediaType.APPLICATION_JSON_VALUE},
beanClass = Apphandler.class, method = RequestMethod.GET, beanMethod = "getApp",
operation = #Operation(operationId = "getApp", responses = {
#ApiResponse(responseCode = "200", description = "successful operation",
content = #Content(schema = #Schema(implementation = AppResponse.class)))
},
parameters = {
#Parameter(in = ParameterIn.HEADER, name = "Authorization ",required = true),
#Parameter(in = ParameterIn.QUERY, name = "id",required = true),
#Parameter(in = ParameterIn.QUERY, name = "idType")
}
))
#Bean
public RouterFunction<ServerResponse> route(Apphandler handler) {
return RouterFunctions.route(GET("/app"), handler::getApp);
}
}
Corresponding to above api definition below swagger response is coming
pom contains the below dependency for openapi
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-core</artifactId>
<version>1.4.3</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>1.4.3</version>
</dependency>
Basically I my required swagger should look like below image. How to achieve it?
Below queries:
Q1. I have made id and authorization as required=true but even when I am running without providing them from swagger it is not giving any warning message. Ideally it should give some warning message. How to get that message
Q2. id value needs to follow some REGEX pattern like [a-zA-Z0-9]{5,15}.How to add this regex check. Also if I am providing id which does not follow this pattern should give some warning message? How to achieve it?
Q3. How to add enum for parameter?
If you want to generate swagger documentation indicating that idField is an enumerated, you can use the schema property of the #Parameter annotation:
#Parameter(in = ParameterIn.QUERY, name = "idType", schema = #Schema(implementation = EIndType.class))
Where EIndType is the enum where you define all the possible values:
public enum EIndType {
SITEGEOID("siteGeoId"),
SITERKST("siteRkst"),
CITYGEOID("cityGeoId"),
CITYRKST("cityRkst");
private final String value;
public String getValue() {
return value;
}
}
I got the answer
Q1 answer
#Parameter(in = ParameterIn.HEADER, name = "Authorization ",required = true,schema =#Schema(minLength = 1))
Q2 answer
#Parameter(in = ParameterIn.QUERY, name = "id",required = true,schema = #Schema(minLength = 5,maxLength = 15,pattern = "[a-zA-Z0-9]{5,15}"))

Unit Test JPA Specification's content

We have implemented filtering for a repository by using JPA's Specification as follows:
public Page<CustomerDTO> searchCustomers(SearchDto searchDto) {
var queryFilters = this.getQueryFitlers(searchDto);
var paginationAndSorting = this.getPaginationAndSorting(searchDto.getPageNumber(),
searchDto.getPageSize());
return customerRepository.findAll(queryFilters, paginationAndSorting)
.map(entity -> {
CustomerDTO dto = new CustomerDTO();
copyProperties(entity, dto);
return dto;
})
}
And here is the queryFilters() method which uses Specifications:
private Specification<Customer> getQueryFitlers(CustomerSearchSpecDTO filteringValues) {
Specification<Customer> specification = Specification.where(null);
if (isNotBlank(filteringValues.getLastname())) {
specification = specification.and(CustomerRepository.hasLastname(filteringValues.getLastname()));
}
if (isNotBlank(filteringValues.getTaxId())) {
specification = specification.and(CustomerRepository.hasTaxId(filteringValues.getTaxId()));
}
// several more fields with the same approach
return specification;
}
Since these query filters are optional depending upon if the searchField is empty or not, what we would like to do is verify that the specification contains the proper "filters".
For example, if the searchDto input contains only taxId not blank, then I want to check that the returned specification contains such a "filter / specification".
Note: grabbing a reference to the result of this.getQueryFilters() (which is the Specifications) is not a problem, we already achieved that.

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

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;
// [..]
}

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.

How to set the default namespace or how to define the #key values when using google-api and using Atom serialization/parser

I'm having trouble with Atom parsing/serializing - clearly something related to the namespace and the default alias - but I can;t figure out what I'm doing wrong.
I have two methods - one that I'm trying to do a GET and see if an album is defined and what that tries to do a POST to create the album (if it does not exist).
The GET I managed to get working - although there too I'm pretty sure I am doing something wrong because it is different from the PicasaAndroidSample. Specifically, if I define:
public class EDAlbum {
#Key("atom:title")
public String title;
#Key("atom:summary")
public String summary;
#Key("atom:gphoto:access")
public String access;
#Key("atom:category")
public EDCategory category = EDCategory.newKind("album");
}
Then the following code does indeed get all the albums:
PicasaUrl url = PicasaUrl.relativeToRoot("feed/api/user/default");
HttpRequest request = EDApplication.getRequest(url);
HttpResponse res = request.execute();
EDAlbumFeed feed = res.parseAs(EDAlbumFeed.class);
boolean hasEDAlbum = false;
for (EDAlbum album : feed.items) {
if (album.title.equals(EDApplication.ED_ALBUM_NAME)) {
hasEDAlbum = true;
break;
}
}
But - if instead I have:
public class EDAlbum {
#Key("title")
public String title;
#Key("summary")
public String summary;
#Key("gphoto:access")
public String access;
#Key("category")
public EDCategory category = EDCategory.newKind("album");
}
Then the feed has an empty collection - i.e. the parser does not know that this is Atom (my guess).
I can live with the android:title in my classes - I don;t get it, but it works.
The problem is that I can't get the POST to wok (to create the album). This code is:
EDAlbum a = new EDAlbum();
a.access = "public";
a.title = EDApplication.ED_ALBUM_NAME;
a.summary = c.getString(R.string.ed_album_summary);
AtomContent content = new AtomContent();
content.entry = a;
content.namespaceDictionary = EDApplication.getNamespaceDictionary();
PicasaUrl url = PicasaUrl.relativeToRoot("feed/api/user/default");
HttpRequest request = EDApplication.postRequest(url, content);
HttpResponse res = request.execute();
The transport and namespace are:
private static final HttpTransport transport = new ApacheHttpTransport(); // my libraries don;t include GoogleTransport.
private static HttpRequestFactory createRequestFactory(final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
AtomParser parser = new AtomParser();
parser.namespaceDictionary = getNamespaceDictionary();
request.addParser(parser);
}
});
}
public static XmlNamespaceDictionary getNamespaceDictionary() {
if (nsDictionary == null) {
nsDictionary = new XmlNamespaceDictionary();
nsDictionary.set("", "http://www.w3.org/2005/Atom");
nsDictionary.set("atom", "http://www.w3.org/2005/Atom");
nsDictionary.set("exif", "http://schemas.google.com/photos/exif/2007");
nsDictionary.set("gd", "http://schemas.google.com/g/2005");
nsDictionary.set("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#");
nsDictionary.set("georss", "http://www.georss.org/georss");
nsDictionary.set("gml", "http://www.opengis.net/gml");
nsDictionary.set("gphoto", "http://schemas.google.com/photos/2007");
nsDictionary.set("media", "http://search.yahoo.com/mrss/");
nsDictionary.set("openSearch", "http://a9.com/-/spec/opensearch/1.1/");
nsDictionary.set("xml", "http://www.w3.org/XML/1998/namespace");
}
return nsDictionary;
}
If I use
#Key("title")
public String title;
then I get an exception that it does not have a default namespace:
W/System.err( 1957): java.lang.IllegalArgumentException: unrecognized alias: (default)
W/System.err( 1957): at com.google.common.base.Preconditions.checkArgument(Preconditions.java:115)
W/System.err( 1957): at com.google.api.client.xml.XmlNamespaceDictionary.getNamespaceUriForAliasHandlingUnknown(XmlNamespaceDictionary.java:288)
W/System.err( 1957): at com.google.api.client.xml.XmlNamespaceDictionary.startDoc(XmlNamespaceDictionary.java:224)
and if I use
#Key("atom:title")
public String title;
then it does serialize but each element has the atom: prefix and the call fails - when I to a tcpdump on it I see something like
.`....<? xml vers
ion='1.0 ' encodi
ng='UTF- 8' ?><at
om:entry xmlns:a
tom="htt p://www.
w3.org/2 005/Atom
"><atom: category
scheme= "http://
schemas. google.c
om/g/200 5#kind"
term="ht tp://sch
emas.goo gle.com/
photos/2 007#albu
m" /><at om:gphot
o:access >public<
/atom:gp hoto:acc
....
What do I need to do different in order to use
#Key("title")
public String title;
and have both the GET and the POST manage the namespace?
It looks you are adding either duplicate dictonary keys or keys that are not understood by the serializer.
Use the following instead.
static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary()
.set("", "http://www.w3.org/2005/Atom")
.set("activity", "http://activitystrea.ms/spec/1.0/")
.set("georss", "http://www.georss.org/georss")
.set("media", "http://search.yahoo.com/mrss/")
.set("thr", "http://purl.org/syndication/thread/1.0");
Removing the explicit set for the "atom" item namespace solved this issue for me.

Resources