Swagger OpenApi sends empty value "" instead of null and this ends up giving a 400 Bad Request status - spring-boot

I'm doing a rest api in spring boot and I need to receive 3 normal values and a file, as you can see in the image below.
endpoint image in swagger
My problem is that it is not mandatory to send the file, so when I make the request without the file, instead of going null it goes "" which is considered a string and this ends up giving status 400, because it is waiting for a type MultipartFile.
My method in spring boot:
#Operation(tags = {"Contato"}, summary = "Atualizar contato", description = "Atualiza os dados de um contato ativo a partir do ID.")
#PutMapping(path = "/atualizarContato",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseDto atualizarContato(#ModelAttribute ContatoUpdateDto contatoDto) throws IOException {// fazer a logica}
My class, I even tried to put the default value in #Schema but it doesn't work.
package app.api.denuncia.Dto;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
public class ContatoUpdateDto {
#Schema(description = "O identificador (ID) do contato", required = true)
private int id;
#Schema(description = "O nome do contato", required = true)
private String nome;
#Schema(description = "O nĂºmero de telefone do contato")
private String telefone;
#Schema(description = "Imagem que representa o contato")
private MultipartFile logotipo;
public ContatoUpdateDto() {
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public MultipartFile getLogotipo() {
return logotipo;
}
public void setLogotipo(MultipartFile logotipo) {
this.logotipo = logotipo;
}
}
I just need that instead of sending "" it sends null.
The error:
image 1
image 2

Related

OpenApi Swagger not showing Exception as a response

I'm using OpenApi Swagger UI (v. 4.14) with SpringBoot. I'm getting all the info I need with the Swagger, except for the exception. Here's my code.
Garage class:
#Schema(description = "Details about the Car")
#Document("Garage")
public class Garage implements Serializable {
#Schema(description = "An ID of the car in the database", accessMode = Schema.AccessMode.READ_ONLY)
#Id
private String id;
#Schema(description = "The name of the car")
#Field("model")
protected String carModel;
#Schema(description = "Car's engine power output")
protected Integer hp;
#Schema(description = "Production year of the car")
#Field("Year")
protected Integer year;
#Schema(description = "The name of car's designer")
protected String designer;
// controllers, getters, setters, toString
Controller:
// some other code
#Operation(summary = "Deletes a car by its id")
#ApiResponses(value = {
#ApiResponse(responseCode = "200",
description = "A car is deleted from the Garage",
content = {#Content(
schema = #Schema(implementation = Garage.class),
mediaType = "application/json")}),
#ApiResponse(responseCode = "404",
description = "A car with this id is not in our garage",
content = #Content(
schema = #Schema(implementation = RestExceptionHandler.class),
mediaType = "application/json"))})
#DeleteMapping(path = "/deleteCar/{carId}")
public void deleteCarFromGarage(#PathVariable("carId") String id) {
garageService.deleteFromGarage(id);
}
// some other code
Exception handler:
#Schema(description = "Exception handling")
#RestControllerAdvice
public class RestExceptionHandler {
#Schema(description = "The ID is not valid")
#ExceptionHandler(value = {IllegalArgumentException.class})
public ResponseEntity<Object> resourceNotFoundException(IllegalArgumentException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getMessage());
}
}
I'm getting 200 responses every time, no matter do I delete a correct id, or an incorrect one.
EDIT: here's my deleteFromGarage method
public void deleteFromGarage(String id) {
garageRepository.deleteById(id);
}
I've edited my deleteFromGarage method, and that solved the issue
deleteFromGarage before:
public void deleteFromGarage(String id) {
garageRepository.deleteById(id);
}
deleteFromGarage now:
public void deleteFromGarage(String id) {
if (garageRepository.findById(id).isEmpty()) {
throw new IllegalArgumentException("The ID is not valid");
} else {
garageRepository.deleteById(id);
}
}

Printing Json data that is in array using rest template in SpringBoot

#Component
public class JsonData {
#JsonProperty("id")
private Integer id;
#JsonProperty("createdAt")
private Date cratedAt;
#JsonProperty("name")
private String name;
#JsonProperty("email")
private String email;
#JsonProperty("imageUrl")
private String url;
public JsonData() {
}
public JsonData(Integer id, Date cratedAt, String name, String email, String url) {
this.id = id;
this.cratedAt = cratedAt;
this.name = name;
this.email = email;
this.url = url;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getCratedAt() {
return cratedAt;
}
public void setCratedAt(Date cratedAt) {
this.cratedAt = cratedAt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Controller
#RestController
public class JsonDataController {
#RequestMapping(value = "/template/products")
public void getAllData() {
RestTemplate template = new RestTemplate();
String url = "https://5ef99e4bbc5f8f0016c66d42.mockapi.io/testing/data";
ResponseEntity < JsonData[] > response = template.exchange(url, JsonData[].class);
for (JsonData jsonData: response.getBody()) {
System.out.println(jsonData.getName());
System.out.println(jsonData.getEmail());
}
}
}
I am trying to print json data that is array using rest template but I am getting error in this line "ResponseEntity < JsonData[] > response = template.exchange(url, JsonData[].class);" my error is "cannot resolve method" Can anyone tell me correct way of doing this .I am new to spring I do not have proper understanding it would be helpful if some one can give their suggeestion in this code
RestTemplate does not have any method with signature exchange(String, Class<T>).
That is why you are getting "cannot resolve method" error for template.exchange(url, JsonData[].class);.
Here is an example of correct usage of one of the methods from RestTemplate.exchange API:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<JsonData[]> response = restTemplate.exchange(url, HttpMethod.GET, null, JsonData[].class);
RestTemplate also has another method - getForEntity that makes a GET call with the given URL and expected return type. (without need for passing null for not required fields)
RestTemplate template = new RestTemplate();
String url = "https://5ef99e4bbc5f8f0016c66d42.mockapi.io/testing/data";
ResponseEntity <JsonData[]> response = template.getForEntity(url, JsonData[].class);

Getting empty answer Mongodb

Hi there im having some issues with MongoDb i have a CRUD and this is the code im using
First the POJO:
#Data
#Document(collection = "Informacion")
public class Informacion {
//Declaration code
public Informacion(Pais pais, Date fechaCreacion, String nombre, Boolean sexo,
Date fechaNacimiento, List<Preferencias> preferencias, String numTelefono, String usuario,
List<RedesSociales> redes, String contraseniaTW, String contraseniaFB, String contraseniaIG,
Grupo grupo, String paqChip, String email, String contraseniaMail, String fuente,
Date fechaRecarga) {
this.pais = pais;
this.fechaCreacion = fechaCreacion;
this.nombre = nombre;
this.sexo = sexo;
this.fechaNacimiento = fechaNacimiento;
this.preferencias = preferencias;
this.numTelefono = numTelefono;
this.usuario = usuario;
this.redes = redes;
this.contraseniaTW = contraseniaTW;
this.contraseniaFB = contraseniaFB;
this.contraseniaIG = contraseniaIG;
this.grupo = grupo;
this.paqChip = paqChip;
this.email = email;
this.contraseniaMail = contraseniaMail;
this.fuente = fuente;
this.fechaRecarga = fechaRecarga;
}
}
Now the DAO:
#Repository
public interface informacionRepo extends MongoRepository<Informacion,String> {
Informacion findByIdInformacion(String id);
}
And the controller:
#RestController
#RequestMapping("/Informacion")
public class InformacionControlador {
#Autowired
private informacionRepo informacioRepo;
public InformacionControlador(informacionRepo informacioRepo) {
this.informacioRepo = informacioRepo;
}
#GetMapping("/Listar")
public List<Informacion> getAll(){
List<Informacion> info = this.informacioRepo.findAll();
System.out.println(info.isEmpty());
return info;
}
#PutMapping
public void insert(#RequestBody Informacion informacion){
this.informacioRepo.insert(informacion);
}
public void update(#RequestBody Informacion informacion){
this.informacioRepo.save(informacion);
}
#DeleteMapping("/Listar/{id}")
public void delete(#PathVariable("id") String id){
this.informacioRepo.deleteById(id);
}
#GetMapping("/Listar/{id}")
public Informacion getById(#PathVariable("id") String id){
Informacion info = this.informacioRepo.findByIdInformacion(id);
return info;
}
}
Im using POSTMAN to test the methods above but im getting empty answers, the data is already set on the Database, im using a method call seeder that fills the data also y check it with robo mongo and the data is there but still getting empty answers also when i try the insert method i get 403 error.
Thanks for your help
This is the answer seen from the web browser
The problem was that im using the annotation #Data from lombok but i didnt enable annotation processing in the IDE just enable it and works :D

HV000030: No validator could be found for constraint (Hibernate Validator)

I was following this tutorial for create a custom validation with multiple parameters, but I get the following exception when executing the #PostMapping method:
HV000030: No validator could be found for constraint 'com.crimsonlogic.anotaciones.TimeRangeConstraints' validating type 'com.crimsonlogic.model.NuevoEvento'. Check configuration for ''
it catches my attention that at the "check configuration for ''" part, does not tell me any kind of information.
NuevoEvento class:
#TimeRangeConstraints.List({
#TimeRangeConstraints(
fechaEvento="fechaEvento",
horaInicio="horaInicio",
horaCulminacion="horaCulminacion"
)
})
public class NuevoEvento {
#NotNull(message="Como se llamara el evento?")
#Size(max=40, message="Titulo invalido")
private String titulo;
#NotNull(message="Seleccione un tipo.")
private String tipoEvento;
private String url;
#NotNull(message="Seleccione la fecha del evento")
private String fechaEvento;
#NotNull(message="A que hora inicia el evento?")
private String horaInicio;
#NotBlank(message="A que hora termina el evento?")
private String horaCulminacion;
#NotNull(message="Seleccione un salon.")
private int salonId;
public NuevoEvento() {}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getTipoEvento() {
return tipoEvento;
}
public void setTipoEvento(String tipoEvento) {
this.tipoEvento = tipoEvento;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getFechaEvento() {
return fechaEvento;
}
public void setFechaEvento(String fechaEvento) {
this.fechaEvento = fechaEvento;
}
public String getHoraInicio() {
return horaInicio;
}
public void setHoraInicio(String horaInicio) {
this.horaInicio = horaInicio;
}
public String getHoraCulminacion() {
return horaCulminacion;
}
public void setHoraCulminacion(String horaCulminacion) {
this.horaCulminacion = horaCulminacion;
}
public int getSalonId() {
return salonId;
}
public void setSalon(int salon) {
this.salonId = salon;
}
}
TimeRangeConstraint annotation:
#Documented
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy= TimeRangeValidator.class)
public #interface TimeRangeConstraints {
String fechaEvento();
String horaInicio();
String horaCulminacion();
String message() default "El rango de tiempo establecido no es valido o esta ocupado.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#interface List {
TimeRangeConstraints[] value();
}
}
Anyone knows causes of this problem?
I found the problem.
In my TimeRangeValidator class I had the code as follows:
//HERE WAS THE PROBLEM
public class TimeRangeValidator implements ConstraintValidator<TimeRangeConstraints,String> {
//--------------------------------------------------------------------------------->
private String fechaEvento;
private String horaInicial;
private String horaFinal;
#Autowired
private UsuarioSalonRepository usuarioSalon;
#Override
public void initialize(TimeRangeConstraints constraintAnnotation) {
this.fechaEvento = constraintAnnotation.fechaEvento();
this.horaInicial = constraintAnnotation.horaInicio();
this.horaFinal = constraintAnnotation.horaCulminacion();
}
//// MORE AND MOREEE CODE....////
I had to replace the String with Object
public class TimeRangeValidator implements ConstraintValidator<TimeRangeConstraints,Object>
and the problem disappeared.
Deeper explanation for those who did not understand what happened
TimeRangeValidator takes 3 fields from the form to perform the validation logic. The value that was changed previously prevented me from taking the 3 fields of the form due to the following reason:
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
Object dt = new BeanWrapperImpl(value).getPropertyValue(fechaEvento);
Object hInit = new BeanWrapperImpl(value).getPropertyValue(horaInicial);
Object hFin = new BeanWrapperImpl(value).getPropertyValue(horaFinal);
SimpleDateFormat form = new SimpleDateFormat("yyyy-MM-dd");
try {
Date dia = form.parse(dt.toString().replaceAll("/","-"));
return TimeUtils.detectOverlappingEvents(usuarioSalon.buscarEvento(dia),
hInit.toString().replaceAll("\\s","")+":00",
hFin.toString().replaceAll("\\s","")+":00");
} catch (ParseException e) {
e.printStackTrace();
return false;
}
With the type object I can have the ability (with the help of BeanWrapperImpl) to obtain multiple values of the form to validate them.
Normally, type String (or Integer, whatever) is used to validate a single value of the form.

Error Spring React REST Controller Using Custom Class Response (WebFlux)

I'm trying to build a Spring WebFlux project and realize the follow business logic:
1 - Call an external REST Api using WebClient and parse the Json results using the Models below. It is working OK
2 - To show the Mono results Mono<DeviceList> devices, I'm using the ResponseApi class and returning it, but it is NOT working
I'm getting the follow error:
Response status 406 with reason "Could not find acceptable representation"
Thanks
# Json Result
{
"data": [
{
"id": "5bc3c0efe833d93f401bafa8",
"name": "XXXXX",
"group": "5b8fd1fa0499f54cfa7febb8",
"description": "Geolocalizacao gps",
"payloadType": "None",
"contract": "5bc08be5e833d93f40e1f936",
"keepAlive": 0
}
]
}
# Controller
public class DeviceController{
...
...
#RequestMapping(value = V1 + BASE_URL + "/devices/types", method = GET, produces = APPLICATION_JSON)
public Mono<ServerResponse> getDeviceTypes(){
Mono<DeviceList> devices = deviceService.findDeviceTypes();
ResponseApi r = new ResponseApi();
r.setMessage("Test");
r.setCode("200");
r.setStatus(200);
r.setData(devices);
return ok().body(Mono.just(r), ResponseApi.class);
}
}
# Repository
public Mono<DeviceList> findDeviceTypes() {
return webClient.get()
.uri(DEVICE_TYPES_URL)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(DeviceList.class);
}
# Model
public class DeviceList{
#JsonProperty("data")
private List<Device> data;
public List<Device> getData() {
return data;
}
public void setData(List<Device> data) {
this.data = data;
}
}
public class Device{
#JsonProperty("id")
private String id;
#JsonProperty("name")
private String name;
#JsonProperty("group")
private String group;
#JsonProperty("description")
private String description;
#JsonProperty("keepAlive")
private Integer keepAlive;
#JsonProperty("payloadType")
private String payloadType;
#JsonProperty("contract")
private String contract;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getKeepAlive() {
return keepAlive;
}
public void setKeepAlive(Integer keepAlive) {
this.keepAlive = keepAlive;
}
public String getPayloadType() {
return payloadType;
}
public void setPayloadType(String payloadType) {
this.payloadType = payloadType;
}
public String getContract() {
return contract;
}
public void setContract(String contract) {
this.contract = contract;
}
}
#JsonRootName("data")
public class ResponseApi{
#JsonProperty("status")
private Integer status;
#JsonProperty("code")
private String code;
#JsonProperty("message")
private String message;
#JsonProperty("data")
private Object data;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
You can get devices, then in non blocking way, map them to the ResponseApi like that:
#RequestMapping(value = V1 + BASE_URL + "/devices/types", method = GET, produces = APPLICATION_JSON)
public Mono<ServerResponse> getDeviceTypes(){
return deviceService.findDeviceTypes()
.flatMap(devices -> {
ResponseApi r = new ResponseApi();
r.setMessage("Test");
r.setCode("200");
r.setStatus(200);
r.setData(devices);
return ok().body(Mono.just(r), ResponseApi.class);
});
}

Resources