I would like the OpenAPI Generator (https://github.com/OpenAPITools/openapi-generator) to be able to generate Pageable parameter in API according to the implementation in Spring Boot Data. I've been trying to find a suitable, out of the box solution, but couldn't find one.
Ideally, this Pageable parameter should be added only to GET methods in a following manner:
default ResponseEntity<User> getUser(#ApiParam(value = "value",required=true) #PathVariable("id") Long id, **Pageable pageable**)
So after implementing this interface in my Controller I would need to override it and having this aforementioned Pageable parameter. I don't want to have separate parameters for size or page, only this Pageable here.
Thanks for any tips and help!
Unfortunately this is no final solution but it is half way. Maybe it is of help anyway.
By defining the pageable parameters (size, page etc.) as an object query parameter it is possible to tell the generator to use the Spring object instead of generating a Pageable class from the api. This is done by an import mapping.
in gradle:
openApiGenerate {
....
importMappings = [
'Pageable': 'org.springframework.data.domain.Pageable'
]
}
which tells the generator to use the Spring class instead of the one defined in the api:
openapi: 3.0.2
info:
title: Spring Page/Pageable API
version: 1.0.0
paths:
/page:
get:
parameters:
- in: query
name: pageable
required: false
schema:
$ref: '#/components/schemas/Pageable'
responses:
...
components:
schemas:
Pageable:
description: minimal Pageable query parameters
type: object
properties:
page:
type: integer
size:
type: integer
The issue with the mapping is that the generator still adds a #RequestParam() annotation and that breaks it again. It only works if it is NOT annotated.
If you are a bit adventurous you could try openapi-processor-spring (i'm the author). It it does handle the example above. But it may have other limitations you don't like.
Related
We are using Swagger to model our API with Spring annotations:
#Operation(summary = "Creates a post for given user.")
#PostMapping("/post")
open fun createPost(
#RequestParam("userId") user: User,
)
The issue we are having is that Swagger does not know there is a logic behind and we are only passing userId: Long for which the user is loaded by Hibernate.
The model of User contains several #OneToOne, #ManyToOne, #OneToMany relations to other entities and Swagger builds the model of User with all of them. This causes the model to be huge and some of our Swagger docs wouldn't even load in the browser as the model is in the size of megabytes.
Is there a way to tell Swagger:
to ignore specific entity/entities
to enforce different type (in this case Long)
Ideally something like:
#Operation(summary = "Creates a post for given user.")
#PostMapping("/post")
open fun createPost(
#SwaggerType(Long::class)
#RequestParam("userId")
user: User,
)
The cleanest way is to use Springfox with an alternate type rule. See no 10 in the examples given here:
https://springfox.github.io/springfox/docs/current/#springfox-spring-mvc-and-spring-boot
This enables you to completely replace your User class by any other (fake) class that you want to show to the Swagger user, without polluting your model with workarounds - but still staying transparent in your code.
There are a few options that can be tried:
Using inheritance with User Model, you can just define a SuperClass-childClass mapping with User class only containing the userId, and the child class that inherits from it will be holding the other attributes for you. In this way, the input will only be the userId with minimal effort.
Using JsonIgnore, but this works really well while returning the response.
With OpenAPI, Swagger has introduced the capability to use specific properties from the request class. More can be read from
https://swagger.io/docs/specification/describing-request-body/
you can use two different class,a basic class and a senior class,and the senior extends the basic,use basic class in API;
or you can use #JsonIgnore if you don't want to show the field ,like:
#JsonIgnore
private String name;
Beacuse Swagger use jackson to json, if you shield the field by jackson,it will not appear.
In swagger
you can use #ApiModelProperty(hidden = true),This is the perfect way
I use OpenAPI spec to generate Java POJOs. What do I need to specify in Open API yaml to generate the equivalent of below POJO ?
...
#JsonIgnore
public String ignoredProperty;
...
I have the yaml spec as below
openapi: 3.0.0
info:
title: Cool API
description: A Cool API spec
version: 0.0.1
servers:
- url: http://api.cool.com/v1
description: Cool server for testing
paths:
/
...
components:
schemas:
MyPojo:
type: object
properties:
id:
type: integer
name:
type: string
# I want the below attribute to be ignored as a part of JSON
ignoreProperty:
type: string
the openapi generator supports vendor extensions. Specifically, for the Java generator, it supports the following extensions as of the time of writing. However, an up-to-date list can be found here.
Extension name
Description
Applicable for
Default value
x-discriminator-value
Used with model inheritance to specify value for discriminator that identifies current model
MODEL
x-implements
Ability to specify interfaces that model must implements
MODEL
empty array
x-setter-extra-annotation
Custom annotation that can be specified over java setter for specific field
FIELD
When field is array & uniqueItems, then this extension is used to add #JsonDeserialize(as = LinkedHashSet.class) over setter, otherwise no value
x-tags
Specify multiple swagger tags for operation
OPERATION
null
x-accepts
Specify custom value for 'Accept' header for operation
OPERATION
null
x-content-type
Specify custom value for 'Content-Type' header for operation
OPERATION
null
x-class-extra-annotation
List of custom annotations to be added to model
MODEL
null
x-field-extra-annotation
List of custom annotations to be added to property
FIELD
null
x-webclient-blocking
Specifies if method for specific operation should be blocking or non-blocking(ex: return Mono<T>/Flux<T> or return T/List<T>/Set<T> & execute .block() inside generated method)
OPERATION
false
You can use the x-field-extra-annotation vendor extension listed above to add annotations to any field. So, for your example, you can add the following:
openapi: 3.0.0
info:
title: Cool API
description: A Cool API spec
version: 0.0.1
servers:
- url: http://api.cool.com/v1
description: Cool server for testing
paths:
/
...
components:
schemas:
MyPojo:
type: object
properties:
id:
type: integer
name:
type: string
# I want the below attribute to be ignored as a part of JSON
ignoreProperty:
type: string
x-field-extra-annotation: "#com.fasterxml.jackson.annotation.JsonIgnore"
In my Spring API designed with Swagger 2.0, I'm trying to create Inheritance using swagger. I want to create a base object, which will have common properties for both Request and Response objects. I tried to do it like the example below:
CategoryResponse:
allOf:
- $ref: '#/definitions/Category'
- type: object
properties:
id:
type: integer
example: '1'
CategoryRequest:
type: object
allOf:
- $ref: '#/definitions/Category'
Category:
discriminator: nameCategory
type: object
properties:
nameCategory:
type: string
example: Games
The problem is that I get a Bad Request error when trying to POST or PUT a new CategoryRequest object. It doesn't even gets to the API Controller, so I guess the problem might in the model definition above. I tried many variations, but none of them worked. However, when I try to GET the list of categories, or one category by id, i'm able to do so (My CategoryResponse is working and extending Category fine).
Does anybody knows the correct way of creating this structure using inheritance of a common base model, both for Request and Response objects?
Thanks in advance!
id looks like an auto-generated and read-only property. In that case you don't need inheritance - you can use a single Category schema and mark id as readOnly: true.
Category:
type: object
properties:
nameCategory:
type: string
example: Games
id:
type: integer
readOnly: true # <-----
example: 1
From the OpenAPI Specification:
readOnly
Declares the property as "read only". This means that it MAY be sent as part of a response but MUST NOT be sent as part of the request.
I like to use mapstruct, but i can't find out: is there a function to transform a Sort in Pageable for the mapped dto pageable to the entity pagable?
Links:
Pagable: https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Pageable.html
Sort: https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Sort.html
Story:
There is a request:
#GetMapping("find")
public List<DTO> findAll(final Pageable pageable) {
return mapper.map(repository.findAll(pagable));
}
And the Repository:
public interface Repository extends JpaRepository<Entity, Long> {
Page<Entity> findAll(Pageable pageable);
}
If I don't want to expose the entity column name so i map the column.
Eg:
#Mapper
public interface DTOMapper {
#Mappings({
#Mapping(source = "make", target = "manufacturer"),
})
DTO toDto(Entity entity);
}
now the API/Returnvalue has manufacturer. The usage of manufacturer for the sort-request (included in pageable) instead of make is the possible: sort=manufacturer;asc.
But must it not be sort=make;asc, or can the JpaRepository handle it?
So is there a simple way to transform the pageable from the request to the right pageable (with the right sort)?
Thx for answers.
Well - answering to your question "is there a simple way to transform pageable from the request to the right pageable".
If you want to convert retrieved pageable content from db entities with prepared mapper you could use the following approach:
repository.findAll(pageable).map(myMapper::toDto)
The pageable abstraction has the map method:
Returns a new Page with the content of the current one mapped by the given Function.
I understand that in your case you would like to modify also the Sort parameters from/to request according to prepared mapping (modify parameters naming when request is executed and inverse names when response is returned in order to correctly execute it by JPA repository).
Unfortunately in this situation I affraid you have to prepare you own cusom map method. What I suggest you could create very similar method but you could extends the logic and modify/create new Sort according to provided #Mappings annotation(s) to your mapper which will base on the mapstruct mappings definitions. It requires some kind of Java reflection knowledge but with this approach you could create very flexible and universal mapper (it is a good idea to create extension for MapStruct if you are interested).
Hope this help.
I'm using swagger-spring-mvc 0.9.5 and have fields like this in my response data:
#ApiModelProperty("Some description")
private List<Account> accounts;
Short version of the question: how can I get from this annotated Java to e.g. Objective C via swagger-codegen?
The swagger JSON that gets generated by that is:
accounts: {
description: "Some description",
items: {
type: "Account"
},
required: false,
type: "List"
}
My colleague is feeding this into swagger-codegen to generate Objective C classes, and it's producing code that doesn't compile.
#property (nonatomic, strong) NSArray<Optional, NSArray> *accounts;
because NSArray (inside the < >) isn't a protocol.
The swagger template files (mustache) create a protocol for each model. When that protocol is specified on an array, it is picked up by JSONModel to generate the correct models from the data inside the list / array. So in this case the expected output is
#property (nonatomic, strong) NSArray<Optional, MAAccount> *accounts;
This will create an NSArray of MAAccount's (Account being the object type and MA being a prefix that swagger already has).
If we hand-edit the swagger JSON to change List to array (as suggested in various similar cases), the output is correct, but we want to avoid this manual step.
So I tried to get swagger-spring-mvc to use "array":
#ApiModelProperty(value = "Some description", dataType = "array")
private List<Account> accounts;
But then discovered that dataType is ignored in swagger-spring-mvc 0.9.5, and by the looks of it, in springfox 2.0 it is ignored unless it's a fully-qualified Java class name.
Is there a way to achieve this, either by getting swagger-spring-mvc/springfox to use "array" or by any other means?
For the most part the swagger annotations are only an aid to the springfox engine to infer additional information about the types like description/hidden/readonly etc that are not otherwise available from the type system. It can also used as a crutch to represent types that are not easily inferred. Data types can be overriden, but just for type safety as it was pointed out in the comment.
Specifically, I read that dataType will be ignored unless it's a fully-qualified class name.
Like #CupawnTae suggested, version 2.x of springfox supports an option to render generic types with code-generation friendly and language agnostic representations of generic types.
When creating/configuring your docket you will need to specify that the rendered swagger service description needs to be code-generation friendly using the forCodeGeneration option
#Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
...
.forCodeGeneration(true)
...;
}
This will cause springfox to render generic types like List<String>
as ListOfString when forCodeGeneration is set to true
as ListĀ«StringĀ» when forCodeGeneration is set to false
You can try notation below. Dont't forget to use package info of you class
#ApiModelProperty(dataType = "[Lyour.package.Account;")
private List<Account> accounts;