How to set Additional Properties to boolean - java

I am trying to set Additional Properties element into the Open API Schema 3.X but unfortunatel I was not able to find anything in the documentation that help me on it.
I have a Application in Spring boot and it is using Spring doc OAS that relies on Swagger OAS as transitive dependency.
Let me pick some code snippet here:
#GetMapping("/{accountId}")
#Operation(summary = "Get account by account id", tags = TAG)
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Return a specific account queried by path",
content = { #Content(mediaType = "application/json",
schema = #Schema(implementation = AccountDetailsDTO.class)) }),
#ApiResponse(responseCode = "404", description = "No accounts found",
content = #Content) })
public ResponseEntity<AccountDetailsDTO> getAccountDetailsByClientId(#PathVariable("accountId") Integer accountId) { }
This attribute is default to true and What I would like to see is as false like that below:

If you want explicitly set the attribute to false you can a TransformationFilter (annoted #Component for Spring) to set additionalProperties to false for each component of you specification if you are using Springfox.
If you are using Springdoc, you can add a OpenApiCustomiser bean, see examples
Example with Springdoc OpenAPI
#Bean
public OpenApiCustomiser openApiCustomiser() {
return openApi -> openApi.getComponents().getSchemas().values().forEach( s -> s.setAdditionalProperties(false));
}
Example with Springfox framework
#Component
#Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class OpenApiTransformationFilter implements WebMvcOpenApiTransformationFilter
{
public boolean supports(#NotNull DocumentationType delimiter)
{
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
#Override
public OpenAPI transform(OpenApiTransformationContext<HttpServletRequest> context)
{
OpenAPI openApi = context.getSpecification();
openApi.getComponents().getSchemas().values().forEach(schema -> schema.setAdditionalProperties(false));
return openApi;
}
}

One workaround might be define a dummy class that contains the type information, then use that as the #Schema#implementation class in your #APIResponse.
static class YourTypeMap extends java.util.HashMap<String, YourType> {};
Then:
#APIResponse(
responseCode = "200",
content = #Content(
mediaType = "application/json",
schema = #Schema(implementation = YourTypeMap.class)))
Credits: MikeEdgar

Related

Springboot + Jersey 3 + Swagger-core - Nested #Beanparam not rendering correctly

I'm in a journey to upgrade swagger-jaxrs2-jakarta 1.6.8 to 2.2.7, I got almost everything working except some object parameters that should be exploded as inputs in the Swagger-ui and them still be interpreted as JSON input... and not as 2 distinct inputs
Java: 17 Springboot: 3.0.0
Jersey: 3.1.0
swagger-jaxrs2-jakarta: 2.2.7
Resource Interface
#Tag(name = "MyResource", description = "MyResource enpoint")
#Path("/")
#RequestMapping
public interface MyResourceAPI {
#GET
#Path("/get/{name}/something")
#Produces(MediaType.APPLICATION_JSON)
#Operation(summary = "MyResource")
#GetMapping(value = "/get/{name}/something")
#ApiResponses(value = {
#ApiResponse(responseCode = "404", description = "Not found"),
#ApiResponse(responseCode = "400", description = "Bad request"),
#ApiResponse(responseCode = "200", description = "Sucesso", content = #Content(schema = #Schema(implementation = MyResourcehResponse.class)))
})
Response search(#Context HttpServletRequest servletRequest, #BeanParam MyCustomRequest myRequest);
}
Resource Impl
#Component
public class MyResourceAPIImpl extends implements MyResourceAPI {
#Override
public Response search(HttpServletRequest servletRequest, MyCustomRequest myRequest) {
#hidden logic
return Response.ok().entity(myResponse).build();
}
}
Request's classes
public class MyCustomRequest extends Request {
}
public class Request {
#BeanParam
#Builder.Default
private Pagination pagination = new Pagination();
}
public class Pagination {
#QueryParam("limit")
#DefaultValue("200")
private Integer limit = 200;
#QueryParam("offset")
#DefaultValue("0")
private Integer offset = 0;
}
Using the version: 1.6.8, swagger-ui shows
After upgrade:
If I remove #QueryParam from Pagination items, they disappear, and if I remove #BeanParam from Pagination declaration, it works as a single JSON input.
I'm without any clue to fix this... anyone already got this issue or something similar and can help me?

java springdoc #ApiResponses how to define a List as return object using

I'm using SpringBoot with the following dependency
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>
The controller class (#RestController) has one entry point (#GetMapping), and this entry point should return a List of the object : MyClass.java. I added Swagger annotations above the method in order to create API documentation via a swagger UI page.
The swagger documentation should indicate that the return object is of type
List< MyClass>
But how should I do that ? If I do
"#Schema(implementation = List< MyClass >.class)"
there is a compile error.
Swagger annotations:
#Operation(....)
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "successful operation",
content = { #Content(mediaType = "application/json",
schema = #Schema(implementation = ????)) }),
#ApiResponse(...),
#ApiResponse(...)
#GetMapping(value = "/aaa", produces = MediaType.APPLICATION_JSON_VALUE)
public List<MyClass> getAaa(...)
{
return ...
}
You need to use an ArraySchema annotation for this and assign it to the array attribute instead of the schema attribute of the #Content annotation.
You don't need to specify List.class only its type parameter MyClass.class.
#Operation(
summary = "Get a list of users",
description = "Get a list of users registered in the system",
responses = {#ApiResponse(
responseCode = "200",
description = "The response for the user request",
content = {
#Content(
mediaType = "application/json",
array = #ArraySchema(schema = #Schema(implementation = User.class))
)
})
}
)
#GET
#SecurityRequirement(name = "JWT")
#Path("/user")
public List<User> getUsers() {
return null;
}

WebFlux Swagger (Open API) integraton - Post Request sample

I have integrated Swagger (OpenAPI) with Spring Webflux as mentioned here: https://springdoc.org/#spring-weblfuxwebmvcfn-with-functional-endpoints using RouterOperation. The integration works fine and is accessible at /swagger-ui.html
However, for POST APIs, I am not seeing the "Request" sample when I click on "Try it out" button. My Post API accepts a Json as Request Body.
How do I configure this ? Can that be done via Annotations along with RouterOperation or something else ?
Edit: Below is my Router class code:
#Configuration
public class MyRouter {
#RouterOperations({
#RouterOperation(path = "/data", beanClass = MyHandler.class, beanMethod = "getData"),
#RouterOperation(path = "/allData", beanClass = MyHandler.class, beanMethod = "getAllData") })
#Bean
public RouterFunction<ServerResponse> route(MyHandler MyHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/data").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), MyHandler::getData)
.andRoute(RequestPredicates.GET("/allData").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), MyHandler::getAllData);
}
}
Upon adding RouterOperations annotation I can see the swagger-ui showing both the GET and POST APIs correctly but not the request schema sample.
I also came across yaml / json file to describe this. But I am not getting where to put this file in my application so that swagger-ui uses it.
Finally found it
Using #Operation and #Schema, can define the class that is required as input in request body. This will be shown as sample json structure in Swagger-ui. No other configuration required.
#RouterOperations({
#RouterOperation(
path = "/data", beanClass = MyHandler.class, beanMethod = "getData",
operation = #Operation(
operationId = "opGetData",
requestBody = #RequestBody(required = true, description = "Enter Request body as Json Object",
content = #Content(
schema = #Schema(implementation = ApiRequestBody.class))))),
#RouterOperation(path = "/allData", beanClass = MyHandler.class, beanMethod = "getAllData")})
#Bean
public RouterFunction<ServerResponse> route(MyHandler myHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/data").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), myHandler::getData)
.andRoute(RequestPredicates.GET("/allData").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), myHandler::getAllData);
}

Implementation of Swagger core v3 in java

I'm writing some API and I want to integrate Documentation while writing code, so I found Swagger as a good way to to this.
I used Swagger core v3 notations, so my classes are something like:
#RestController
#RequestMapping("/api/v1/bundles")
#OpenAPIDefinition(
info = #Info(
title = "Lifecycle Management RESTful API.",
version = "1",
description = "TODO",
license = #License(name = "Apache 2.0", url = "xxx"),
contact = #Contact(url = "xxx", name = "xx", email = "xxx#xxx.fr")
))
public class RestBundle {
#GetMapping(value = "/{nodeId}",
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
#Operation(summary = "Get all bundles",
description = "Get all available bundles status from a specific node")
public Something(..) {
//Something ...
}
}
And I create a configuration class:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("ANF Orchestrator")
.description("REST API for ANF Orchestrator")
.license("Apache 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.termsOfServiceUrl("")
.version("1.0.0")
.contact(new Contact("Amine","xxx", "aalaouie#laas.fr"))
.build();
}
}
ANd I want to enable the UI of Swagger to get the documentation, but when I enter to:
.../swagger-ui.html
I get:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: "2.0" and those that match openapi: 3.0.n(for example, openapi: 3.0.0).
Try to extend your SwaggerConfig class with WebMvcConfigurationSupport and override its method called addResourceHandlers with implementation like this:
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
On top of class, can you try removing this annotation, as the SwaggerConfig already contains the Docket info.
#OpenAPIDefinition(
info = #Info(
title = "Lifecycle Management RESTful API.",
version = "1",
description = "TODO",
license = #License(name = "Apache 2.0", url = "xxx"),
contact = #Contact(url = "xxx", name = "xx", email = "xxx#xxx.fr")
))

Why is object query parameter null?

I describe a path in openapi file in my spring boot application.
Openapi generates by the file api class which handles http requests.
Also I use swagger which hepls construct a valid url, where I can put query parameters as well.
I'm wondering, why having all this generated staff I receive null object instead of expected.
part of api.yaml
/films:
get:
summary: Отфильтрованные фильмы
operationId: findFilms
tags: [selections]
parameters:
- in: query
name: filter
schema:
type: object
properties:
genreId:
type: integer
year:
type: integer
countryId:
type: integer
style: deepObject
explode: false
responses:
200:
description: successfull response
content:
application/json:
schema:
$ref: 'list-schemas.yaml#/components/schemas/SelectionTo'
generated java class
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
#Validated
#Api(value = "Selections")
public interface SelectionsApi {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
#ApiOperation(value = "Отфильтрованные фильмы", nickname = "findFilms", , response = SelectionTo.class, tags={ "selections", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "successful response", response = SelectionTo.class) })
#RequestMapping(value = "/films",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<SelectionTo> _findFilms(#ApiParam() #Valid #RequestParam(value = "filter", required = false) Filter filter) {
return findFilms(filter);
}
// Override this method
default ResponseEntity<SelectionTo> findFilms(Filter filter) {
getRequest().ifPresent(request -> {
...
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
generated query parameter class
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public class Filter {
#JsonProperty("genreId")
private Integer genreId = null;
#JsonProperty("year")
private Integer year = null;
#JsonProperty("countryId")
private Integer countryId = null;
public Filter genreId(Integer genreId) {
this.genreId = genreId;
return this;
}
implementing interface
#Override
public ResponseEntity<SelectionTo> findFilms(Filter filterType) {
//here filter is null !
return ResponseEntity.ok(transformer.transform(service.getItemsInfo()));
}
request
http://localhost/films?filter[genreId]=13&filter[year]=2021
How openapi file could be improved? Because this is the only thing I've defined. Or what else could the reason?
As far as I can see, Spring MVC does not support decoding nested object query parameters in the OpenAPI deepObject style, like filter[genreId]=13, at least out of the box.
Try to remove #RequestParam() from filter object.
Like this:
default ResponseEntity<SelectionTo> _findFilms(#ApiParam() #Valid Filter filter) {
return findFilms(filter);
}
Also the request should be http://localhost/films?genreId=13&year=2021

Categories