Flattening RequestParam Object in OpenApi 3 - spring-boot

I am migrating from swagger 2 to OpenApi 3.
Swagger 2 Sample Code
#ApiOperation(value = "", nickname = "")
#GetMapping
public List<Employee> findEmployees(#Valid Dto dto) {
return employeeService.findEmployees(dto);
}
OpenApi 3 Code
#Operation(summary = "")
#GetMapping
public List<Employee> findEmployees(#Valid Dto dto) {
return employeeService.findEmployees(dto);
}
DTO Class
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Dto {
private String status;
private String name;
private String destination;
}
There is a significant difference in generation of swagger-ui in both cases.
Swagger 2 shows the DTO object as flattened into individual query params :
Image Flattening of object as individual query parameters happens in Swagger 2 ui
while OpenApi 3 creates a JSON object:
Image Object doesnot flattens but creates a json object
I want to have the flattening behavior in OpenApi 3 like the way it used to be in Swagger 2.
Is there any way to achieve the same in OPENAPI 3.

After some research i found that a new version of openapiui dependency has been released on 12th of april 2020 and it solves my issue in hand. From version 1.3.2 its available.
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.3.2</version>
</dependency>
The use of Annotation #ParameterObject before the query parameter object solves it.
#Operation(summary = "")
#GetMapping
public List<Employee> findEmployees(**#ParameterObject** #Valid Dto dto) {
return employeeService.findEmployees(dto);
}

Related

Spring boot REST API best way to choose in client side which field to load

Hi I have implemented a mock solution to my problem and I'm pretty sure something better already exist.
Here's that I want to achieve :
I have created a point to load categories with or without subCategories
/api/categories/1?fields=subCategories
returns
{
"id":"1",
"name":"test",
"subCategories":[{
"id":"1",
"name":"test123"
}]
}
/api/categories/1
returns
{
"id":"1",
"name":"test"
}
My entities
#Entity
class Category{
#Id
private String id;
private String name;
private Set<SubCategory> subCategories;
}
#Entity
class SubCategory{
#Id
private String id;
private String name;
}
I have removed services since this is not the point.
I've created CategoryDTO and SubCategoryDTO classes with the same fields as Category and SubCategory
The converter
class CategoryDTOConverter{
CategoryDTO convert(Category category,String fields){
CategoryDTO dto=new CategoryDTO();
dto.setName(category.getName());
if(StringUtils.isNotBlank(fields) && fields.contains("subCategories"){
category.getSubCategories().forEach(s->{
dto.getSubcategories().add(SubCategoryDTOConverter.convert(s));
}
}
}
}
I used com.cosium.spring.data.jpa.entity.graph.repository to create an EntityGraph from a list of attribute path
#Repository
interface CategoryRepository extends EntityGraphJpaRepository<Category, String>{
Optional<T> findById(String id,EntityGraph entityGraph);
}
Controller
#RestController
#CrossOrigin
#RequestMapping("/categories")
public class CategoryController {
#GetMapping(value = "/{id}")
public ResponseEntity<CategoryDTO> get(#PathVariable("id") String id, #RequestParam(value="fields",required=false) String fields ) throws Exception {
Optional<Category> categOpt=repository.findById(id,fields!=null?EntityGraphUtils.fromAttributePaths(fields):null);
if(categOpt.isEmpty())
throws new NotFoundException();
return ResponseEntity.ok(categoryDTOConverter.convert(categOpt.get(),fields);
}
}
This is a simple example to illustrate what I need to do
I don't want to load fields that clients doesn't want to use
How could I do this in a better way ?
Take a look at GraphQL since it is a perfect match for your use case. With GraphQL it is the client that decides which attributes it wants to receive by providing in the POST request body exactly which attributes are needed to be included in the response. This is way more manageable than trying to handle all this on your own.
Spring Boot recently added its own Spring GraphQL library, so it is quite simple to integrate it in your Spring Boot app.

javax-validation annotation is not working for member variables that is declared as another ObjectType

I have added annotations in the parent class.
It is working fine.
But it is not working in the member variables that is declared as another Object type. It is validating:
orderId from base class
referenceNumber from MarchantApplicationRequest
#NotEmpty annotation at customerRequests field in MerchantApplicationRequest.
But it is not validating customerRoleType in CustomerRequest.
Also, I would like to add #NotBlank annotation in customerRequests. But it is not taking this, though it is taking #NotEmpty annotation.
Class MerchantApplicationRequest
#JsonIngnoreProperties(ignoreUnknown=false)
public class MerchantApplicationRequest extends IomBaseDTO {
#NotEmpty(message="customerRequests is mandatory")
private List<CustomerRequest> customerRequests;
#NotBlank(message="referenceNumber is mandatory")
private String referenceNumber ;
}
Class CustomerRequest
public class CustomerRequest {
#NotBlank(message="customerRoleType is mandatory")
private String customerRoleType ;
}
Controller class
Method where to apply validation:
#PostMapping("/orderDetail")
public void orderDetail(#Valid #RequestBody MerchantApplicationRequest request) {
try {
iOrderService.updateProductDetail(request);
} catch (Exception e) {
// ...
}
}
Here is my JSON payload:
{
"orderId" : 101,
"referenceNumber" : "123",
"customerRequests" : [ {
"customerRoleType" : null
}]
}
I am using in pom.xml of Spring Boot application:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
If you want to cascade the validation you have to add the #Valid annotation:
#Valid
#NotEmpty(message="customerRequests is mandatory")
private List<CustomerRequest> customerRequests;
Please read more about cascading in the Hibernate Validation documentation: Example 2.11: Cascaded validation
Using bean-validation (javax.validation), you can add validation to elements of collections.
Using Bean-Validation 1.0
#JsonIngnoreProperties(ignoreUnknown=false)
public class MerchantApplicationRequest extends IomBaseDTO {
#NotEmpty(message="customerRequests is mandatory")
#Valid
private List<CustomerRequest> customerRequests;
#NotBlank(message="referenceNumber is mandatory")
private String referenceNumber ;
}
See also:
JSR 303: How to Validate a Collection of annotated objects?
LogicBig Tutorial: Collection Validation
Alternative since Bean-Validation 2.0
In Java 8 generic types can also be validated by prepending the annotation before the type inside the diamond-operator, e.g. <#Valid CustomerRequest>. This is a more concise way of defining per-element validation. It has the same effect like the traditional way, validates every given element as defined in the class (CustomerRequest).
See also:
java/beans validation - collection/map does not contain nulls
Baeldung Tutorial: Validating Container Elements with Bean Validation 2.0

I want to render the data format of my choice dynamically in REST api

I have to render the data format of my choice at run time.
E.x.
public class Message {
#Id
private String version;
private String title;
private String content;
private String sender;
private String url;
}
now if i hit the url http://localhost:8080/messages/12345/xml it should return xml format
and if i hit the url http://localhost:8080/messages/12345/json then i should return json format
but the end point should be same
http://localhost:8080/messages/12345
I am very new to Spring boot and REST ,it would be a great help.
Based on your question, for json you don't need a converter or libraries spring boot have jackson library so it will automatically converts your pojo into json. For xml response add jackson-dataformat-xml dependency to your project
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.8</version>
</dependency>
and while returning response convert pojo to xml response like this
Message message = new Message();
message.setVersion();
// set all the values if you want otherwise take the message object and pass
XmlMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(message);
This will return xml response

Why I receive 404 error use Spring MVC framework?

When I send request http://localhost:8080/pets My server response 404!
The code on github: https://github.com/Teemitze/petstore
I build war file. Version spring 2.2.6.RELEASE
#Controller
#RequestMapping("/pets")
public class PetsController {
#Autowired
PetRepository petRepository;
#PostMapping("/addPet")
public void addPet(Pet pet) {
petRepository.save(pet);
}
#GetMapping
#ModelAttribute
public String pets(Model model) {
List<Pet> petList = new ArrayList<>();
petList.add(getPet());
petList.add(getPet());
petList.add(getPet());
model.addAttribute("pets", petList);
return "allPets";
}
public Pet getPet() {
Pet pet = new Pet();
pet.setId(1L);
pet.setName("Мурзик");
pet.setPrice(100);
pet.setBirthday(Date.valueOf("2019-12-12"));
pet.setSex("М");
return pet;
}
}
I checked out your code and found a few issues.
1) Package structure
Move controller, dto, repo packages to the main package (com.petstore)
Since the main application is inside the (com.petstore) package and the controller is outside the package, so it fails to scan the class.
2) Use annotation #Entity for the Pet entity class with #Id for the id property
3) Remove #ModelAttribute from pets() method since you are not binding any method parameter.
After this, I see the /pets
SpringBoot project requires define some configuration conventions that need to be follow in order to start a minimum application.
Some points you have to consider when you want to start a spring boot application.
For example:
Your SpringBootApplication(PetstoreApplication) class should be in the directory level above your other packages so that it can scan all classes.
If you want to use SpringData JPA you have to manage your model class
#Data
#Entity
public class Pet {
#Id
private long id;
private String name;
private String sex;
private Date birthday;
private byte[] photo;
private int price;
}
because it is handled by respository
public interface PetRepository extends CrudRepository<Pet, Long>
Need minimum configuration for Thymeleaf https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
You are making a GET request for a resource "/pets" so no need #ModelAttribute in get mapping method
#GetMapping()
public String allPets(Model model) {
Make sure your html files is under resources/templates directory.
Check out the reference docs
spring mvc
spring data jpa

Ignore xml tags while serializing pojo fields to xml

I am using jackson library to map POJO to XML.
compile ('com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.0')
While serializing I need to ignore some of the fields. This is my POJO class. For example, the field lineNumber should be ignored.
#NoArgsConstructor
#AllArgsConstructor
#Getter
#XmlAccessorType(XmlAccessType.FIELD)
public class InvoiceLineItem {
#JacksonXmlProperty(localName = "LineNumber")
#XmlTransient
private Integer lineNumber;
#JacksonXmlProperty(localName = "ProductCode")
#XmlTransient
private String productCode;
#JacksonXmlProperty(localName = "ProductDescription")
#XmlTransient
private String productDescription;
}
I am using #XmlTransient with XmlAccessorType to ignore the fields. But the lineNumber field annotated with XmlTransient is not ignored while serializing.
Try adding the #JsonProperty(access = Access.WRITE_ONLY)
annotation to the lineNumber field.
Even thought it looks like a JSON thing,
the Jackson XmlMapper identifies the annotation and reacts accordingly.
Edit
The conclusion XmlMapper should support JSON serizlization is an example of the following, incorrect attempt at reasoning:
All men are mortal.
Socrates was mortal.
Therefore, all men are Socrates.
The XmlMapper is not a wrapper class around ObjectMapper.
It came after ObjectMapper and appears to share many features,
like the handling of some JSON annotation.

Resources