#Valid annotation is not working and giving NotReadablePropertyException - java

I have request class for a patch API in the below format
#Schema
#Data
#EqualsAndHashCode(callSuper = true)
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class PricingUsageTemplatePatchInput extends BaseRequest {
#Schema(description = "From node of the Template")
private JsonNullable<#Valid VertexNode> from;
#Schema(description = "To node of the Template")
private JsonNullable<#Valid VertexNode> to;
#NotNull
#Schema(description = "Current version of the template for which update is being made.")
private Long version;
}
VertexNode is as below
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class VertexNode {
#NotNull
#Valid
private Selectors selectors;
#NotNull
#Valid
private Cardinality cardinality;
}
And in the controller layer is as below
#PatchMapping("/{key}")
#Operation(description = "API to update the template")
public PricingUsageTemplateResponse update(#PathVariable #IsUUID final String key,
#Valid #RequestBody #NotNull #Parameter(description = "PricingUsageTemplatePatchInput", required = true)
PricingUsageTemplatePatchInput pricingUsagePatchInput) {
var request = PricingUsageTemplateUpdateRequest.builder()
.key(key)
.pricingUsageTemplatePatchInput(pricingUsageTemplatePatchInput)
.build();
return (PricingUsageTemplateResponse) actionRegistry.get(request).invoke(request);
}
When I am sending selector as null from the postman for the above api , the valid annotation is not able to send valid not null validation error instead I am getting 5xx error with below reason
org.springframework.beans.NotReadablePropertyException: Invalid property 'to.selectors' of bean class [domain.pricing_usage.dto.request.PricingUsageTemplatePatchInput]: Bean property 'to.selectors' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Can anyone help why #Valid is not working as expected

Related

Post request transform int field to string automatically

I'm doing a dummy app of a hostpital. The problem I'm having is that, I'm trying to verify that when a Patient is created, the fields passed are of the correct type, but whenever I POST an Int in a String field, it doesn't fail and just transform the Int to String. The field I'm trying to make fail is "surname", which by the definition of the Patient class, is a String.
If I do this (I pass a number to the "surname" field):
{
"name": "John",
"surname": 43,
"sickness": "headache"
}
It just transforms 43 into a String by the time its in the Controller method.
Here we have the Patient object:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
public class Patient implements Serializable {
private static final long serialVersionUID = 4518011202924886996L;
#Id
//TODO: posible cambiar luego la generationType
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private Long id;
#Column(name = "patient_name")
#JsonProperty(required = true)
private String name;
#Column(name = "patient_surname")
#JsonProperty(required = true)
private String surname;
#Column(name = "patient_sickness")
#JsonProperty(required = true)
private String sickness;
}
And this is the controller class:
#Controller
#Path("/patient")
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public class PatientController {
#POST
#Path("")
public ResponseEntity<Object> postPatient(final Patient patient) {
ResponseEntity<Object> createdPatient = patientBusiness.createPatient(patient);
return new ResponseEntity<Patient>(createdPatient.getBody(), createdPatient.getStatusCode());
}
EDIT 1:
Following the "clues" and closing the circle of attention, I tried modifying the ObjectMapper, but my configuration isn't applying. I'm still getting the error from above.
This is the config class:
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper getModifiedObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false);
mapper.coercionConfigFor(LogicalType.Integer).setCoercion(CoercionInputShape.String, CoercionAction.Fail);
return mapper;
}
}
Even added the property to the application.yml, but still nothing:
spring:
jackson:
mapper:
allow-coercion-of-scalars: false
Any help is appreciated. Thx.
In the end I referred to this post to do a deserializer and a module to just have it along all the program, not just the field I want not to be transformed.
Disable conversion of scalars to strings when deserializing with Jackson

Spring boot post api - org.postgresql.util.PSQLException: ERROR: null value in column "password" violates not-null constraint

I have created a spring boot application. And in the application I'm working with API's. I have an POST API for creating an user, but every time I use the api, i will get the following error
Spring boot post api - org.postgresql.util.PSQLException: ERROR: null value in column "password" violates not-null constraint
Im using hibernate and PostgreSQL as database.
Even if I give de data values, i will get this error. I searched over the internet, but I cant find a solution for this problem
This is the code that i user:
Users.java:
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder(toBuilder = true)
#ToString
#Table
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int user_id;
#Column(unique = true, nullable = false)
private String username;
#Column(nullable = false)
private String password;
}
UsersRepository.java
#Repository
public interface UsersRepository extends JpaRepository<Users, Integer> {}
UsersService
#Service
#RequiredArgsConstructor
#Transactional
public class UsersService {
private final UsersRepository usersRepository;
public Users createUser(Users newUsers) {
return this.usersRepository.save(newUsers);
}
}
UsersDto
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class UsersDto {
private int user_id;
private String username;
private String password;
}
UsersMapper
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface UsersMapper {
UsersDto toDto(Users users);
Users toModel(UsersDto usersDto);
}
UsersController
#RestController
#CrossOrigin(origins = "http://localhost:4200")
#RequiredArgsConstructor
#RequestMapping("/users")
#Validated
public class UsersController {
private final UsersService usersService;
private final UsersMapper usersMapper;
#PostMapping()
#ApiOperation(value = "Create new user", httpMethod = "POST", code = 201)
#ResponseBody
public ResponseEntity<UsersDto> createUser(#RequestBody UsersDto usersDto) throws Exception {
Users users = this.usersMapper.toModel(usersDto);
Users createUsers = this.usersService.createUser(users);
return ResponseEntity.ok(this.usersMapper.toDto(createUsers));
}
}
I hope someone can help me with this problem
The error says that the password is null, what is not allowed. So it's probably not provided on the request send to your API. You could add validation constraints to the endpoint, like
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class UsersDto {
private int user_id;
#NotNull
private String username;
#NotNull
private String password;
}
One more note, storing password as plain text is not advised , it's a major security issue. You should create and use a hash instead, check out Spring Security for this.
PS - check out bootify.io - here you can create your REST API together with the validation constraints.

javax.validation not working when noargsconstructor is used

I am trying to validate a request object, whose class is as follows:
#Value
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
#AllArgsConstructor
public class SomeCreateRequest {
#NotNull(message = "Name can not be null")
#NotBlank(message = "Name can not be empty")
String name;
}
Without NoArgsConstructor, validation works but POST operation fails with HttpMessageNotReadableException; with NoArgsConstructor, POST operation succeeds but validation doesn't work.
What am I doing wrong?
P.S. using Java 8, Spring 2.3.4.RELEASE, javax.validation 2.0.1.Final and my controller looks like below:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api")
public class MyController {
private final MyService myService;
#PostMapping()
public MyResponse submit(#Valid #RequestBody SomeCreateRequest request){
return myService.create(request);
}
}

Deserialize JSON to polymorphic types Spring boot

I am working on an e-policy project where i need to save different types of policies. For simplicity i am considering only two types "LifeInsurance" and "AutoInsurance". What i want to achieve is if the JSON request to create policy contains "type":"AUTO_INSURANCE" then the request should be mapped to AutoInsurance.class likewise for LifeInsurance but currently in spring boot app the request is getting mapped to parent class Policy eliminating the specific request fields for auto/Life insurance. The domain model i have created is as below.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#NoArgsConstructor
#Getter
#Setter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include =
JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({ #Type(value = AutoInsurance.class, name = "AUTO_INSURANCE"),
#Type(value = LifeInsurance.class) })
public class Policy {
#Id
#GeneratedValue
private Long id;
private String policyNumber;
#Enumerated(EnumType.STRING)
private PolicyType policyType;
private String name;
}
My AutoInsurance class is below.
#Entity
#NoArgsConstructor
#Getter
#Setter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonTypeName(value = "AUTO_INSURANCE")
public class AutoInsurance extends Policy {
#Id
#GeneratedValue
private Long id;
private String vehicleNumber;
private String model;
private String vehicleType;
private String vehicleName;
}
Below is LifeInsurance type child class
#Entity
#NoArgsConstructor
#Getter
#Setter
#JsonTypeName(value = "LIFE_INSURANCE")
public class LifeInsurance extends Policy {
#OneToMany(mappedBy = "policy")
private List<Dependents> dependents;
private String medicalIssues;
private String medication;
private String treatments;
}
To save the policy details, I am sending JSON request from UI with a "type" property indicating the type of insurance in the request.
When i run the below test method, JSON request gets mapped to the correct child class as required.
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper map = new ObjectMapper();
String s = "{\"id\": 1,\"policyNumber\": \"Aut-123\",\"type\": \"AUTO_INSURANCE\",\"policyType\": \"AUTO_INSURANCE\",\"name\": null,\"address\": null,\"contact\": null,\"agentNumber\": null,\"agentName\": null,\"issuedOn\": null,\"startDate\": null,\"endDate\": null,\"vehicleNumber\": \"HR\",\"model\": null,\"vehicleType\": \"SUV\",\"vehicleName\": null}";
Policy p = map.readValue(s, Policy.class);
System.out.println(p.getClass());
//SpringApplication.run(EPolicyApplication.class, args);
}
But when i run the same in Spring boot in a RESTController postmapping, I am getting a PArent class object instead of the child class object.
#RestController
#RequestMapping("/policy")
public class PolicyController {
#PostMapping
public void savePolicy(Policy policy) {
System.out.println(policy.getClass());
}
}
I can get the JSON as string, autowire objectmapper and parse manually but i want to understand if its a known issue and if anyone else has faced the same with Spring boot. I have searched for solutions on this but i got was solution to deserializing to polymorphic classes but nothing related to issue with Spring boot.
In your method you haven't annotated the Policy method argument with #RequestBody. Which leads to Spring creating just an instance of Policy instead of using Jackson to convert the request body.
#PostMapping
public void savePolicy(#RequestBody Policy policy) {
System.out.println(policy.getClass());
}
Adding the #RequestBody will make that Spring uses Jackson to deserialize the request body and with that your annotations/configuration will be effective.

Javax validation on nested objects - not working

In my Spring Boot project I have two DTO's which I'm trying to validate, LocationDto and BuildingDto. The LocationDto has a nested object of type BuildingDto.
These are my DTO's:
LocationDto
public class LocationDto {
#NotNull(groups = { Existing.class })
#Null(groups = { New.class })
#Getter
#Setter
private Integer id;
#NotNull(groups = { New.class, Existing.class })
#Getter
#Setter
private String name;
#NotNull(groups = { New.class, Existing.class, LocationGroup.class })
#Getter
#Setter
private BuildingDto building;
#NotNull(groups = { Existing.class })
#Getter
#Setter
private Integer lockVersion;
}
BuildingDto
public class BuildingDto {
#NotNull(groups = { Existing.class, LocationGroup.class })
#Null(groups = { New.class })
#Getter
#Setter
private Integer id;
#NotNull(groups = { New.class, Existing.class })
#Getter
#Setter
private String name;
#NotNull(groups = { Existing.class })
#Getter
#Setter
private List<LocationDto> locations;
#NotNull(groups = { Existing.class })
#Getter
#Setter
private Integer lockVersion;
}
Currently, I can validate in my LocationDto that the properties name and building are not null, but I can't validate the presence of the property id which is inside building.
If I use the #Valid annotation on the building property, it would validate all of its fields, but for this case I only want to validate its id.
How could that be done using javax validation?
This is my controller:
#PostMapping
public LocationDto createLocation(#Validated({ New.class, LocationGroup.class }) #RequestBody LocationDto location) {
// save entity here...
}
This is a correct request body: (should not throw validation errors)
{
"name": "Room 44",
"building": {
"id": 1
}
}
This is an incorrect request body: (must throw validation errors because the building id is missing)
{
"name": "Room 44",
"building": { }
}
Just try adding #valid to collection. it would be working as per reference hibernate
#Getter
#Setter
#Valid
#NotNull(groups = { Existing.class })
private List<LocationDto> locations;
#Valid annotation must be added to cascade class attributes.
LocationDTO.class
public class LocationDto {
#Valid
private BuildingDto building;
.........
}
Use #ConvertGroup from Bean Validation 1.1 (JSR-349).
Introduce a new validation group say Pk.class. Add it to groups of BuildingDto:
public class BuildingDto {
#NotNull(groups = {Pk.class, Existing.class, LocationGroup.class})
// Other constraints
private Integer id;
//
}
And then in LocationDto cascade like following:
#Valid
#ConvertGroup.List( {
#ConvertGroup(from=New.class, to=Pk.class),
#ConvertGroup(from=LocationGroup.class, to=Pk.class)
} )
// Other constraints
private BuildingDto building;
Further Reading:
5.5. Group conversion from Hibernate Validator reference.

Categories