Quarkus: #Valid doesn’t work with PanacheMongo - java

I have a problem with validation of my bean.
I'm using Quarkus with MongoDB and when I try to run a create REST API, with #Valid annotation before request bean, I expect an exception if I want to create a document with null field (obviously I use #NotNull in entity), but document is created without field.
Here is my code:
Car.java:
#MongoEntity(collection="cars")
public class Car extends PanacheMongoEntityBase {
#BsonId
private long id;
#NotNull
private String carName;
#NotNull
#Size(min = 1, max = 3)
private String code;
// get and set
}
CarResource.java:
#Path("/cars")
#Consumes("application/json")
#Produces("application/json")
public class CarResource {
#GET
public List<Car> list() {
return Car.listAll();
}
#GET
#Path("/{id}")
public Car get(long id) {
return Car.findById(id);
}
#POST
public Response create(#Valid Car car) {
car.persist();
return Response.status(201).build();
}
I have same problem with #Size annotation, because I can create a code field with more characters than 3.
UPDATE
Validation works with quarkus-hibernate-validator.
Now, I have to find a solution for unique field.
And besides from the main question: is there an annotation like #Indexed(unique = true)? I want an unique field for my app.

You need to use #Validated annotation on CarResource Class as shown below.
#Path("/cars")
#Consumes("application/json")
#Produces("application/json")
#Validated
public class CarResource {
#GET
public List<Car> list() {
return Car.listAll();
}
#GET
#Path("/{id}")
public Car get(long id) {
return Car.findById(id);
}
#POST
public Response create(#Valid Car car) {
car.persist();
return Response.status(201).build();
}
}

Related

Getting an error with mapping when using MongoRepository findById

When I try to use the findById method from MongoRepository with a string passed in, I get an error with the mapper which leads to no results being returned, even though such exists.
Here is my project's structure and files:
Entity Post.java
#Document("posts")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Post {
#Id
private String Id;
private Integer userId;
private String content;
private Integer likes;
private Integer dislikes;
}
Repository PostRepository.java
public interface PostRepository extends MongoRepository<Post, String> {
}
Service PostService.java
#Service
public class PostService {
#Autowired
private PostRepository repository;
public List<Post> getPosts() {
return repository.findAll();
}
public Post getPostById(String id) {
return repository.findById(id).orElse(null);
}
public Post savePost(Post post) {
return repository.save(post);
}
public void deletePostById(String id) {
repository.deleteById(id);
}
}
Controller PostController.java
#RestController
#RequestMapping("/posts")
public class PostController {
#Autowired
private PostService service;
#Autowired
private StreamBridge streamBridge;
#GetMapping("")
public List<Post> getPosts() {
return service.getPosts();
}
#GetMapping("/{postId}")
public Post getPostById(#PathVariable String postId) {
return service.getPostById(postId);
}
#PostMapping("/createPost")
public Post createPost(#RequestBody Post post) {
streamBridge.send("postCreated-out-0", post.getUserId());
return service.savePost(post);
}
#DeleteMapping("/{postId}")
public void deletePostById(#PathVariable String postId) {
service.deletePostById(postId);
}
}
When I try running either a GET such as localhost:9192/posts/62a76719145e644e5b640327 or a DELETE localhost:9192/posts/62a76719145e644e5b640327, where 62a76719145e644e5b640327 is a correct id associated with a entry in the document in MongoDB I get this error in the console:
[nio-9192-exec-2] o.s.d.mongodb.core.convert.QueryMapper : Could not map 'Post.id'. Maybe a fragment in 'String' is considered a simple type. Mapper continues with id.
I also tried writing a custom query using the #Query annotation that overwrites the default findById as such:
#Override
#Query("{ 'id' : ?0}")
Optional<Post> findById(String s);
And I still get the same error. I am using spring-boot-starter-data-mongodb version 2.7.0 and I am running MongoDB locally.
EDIT: Forgot to mention that the same thing happens when using the deleteById method
i think the problem is in the identifier u named it with capital I
replace Id in posts document with id ?
give it a try... it will work

Ignore a property in Dto from RequestBody base on endpoint (use one Dto for two purpose)

I have a Coupon Table in the database so there are CouponEntity and CouponDto in the application. My question is about handling two endpoints with one Dto. for example, I have create and update endpoints, both of them use CouponDto. There is a business role that I can't update code property of the coupon entity. How to ignore it if the client passes this property in the request?
If I use #JsonIgnore, the property will be ignored in both the endpoints that I need to get it in create API. I used groups for validations and separate validations from each other, but #JsonIgnore can not be set sometimes.
For create a coupon:
#PostMapping("/create")
public ResponseEntity<?> createCoupon(#RequestBody CouponCodeDTO couponCodeDTO) {
return ResponseEntity.ok(couponService.create(couponCodeDTO));
}
For update a coupon:
#PutMapping("/update/{id}")
public ResponseEntity<?> updateCoupon(
#PathVariable Long id, #RequestBody CouponCodeDTO couponCodeDTO
) {
return ResponseEntity.ok(couponService.update(id, couponCodeDTO));
}
And CouponDto:
public class CouponCodeDto extends BaseDto<Integer> {
#NotBlank
private String code;
private Integer availableCount;
#NotNull
private LocalDate startDate;
...
}
You can use #JsonView to specify the field you want to serialize/deserialize per view and specify view on the endpoint.
public class Views {
interface Update {}
interface Create extends Update {}
}
public class CouponCodeDto extends BaseDto<Integer> {
#NotBlank
#JsonView(Views.Create.class)
private String code;
#JsonView(Views.Update.class)
private Integer availableCount;
#NotNull
#JsonView(Views.Update.class)
private LocalDate startDate;
...
}
And use on request body of the endpoint
#PutMapping("/update/{id}")
public ResponseEntity<?> updateCoupon(#PathVariable Long id,
#JsonView(Views.Update.class) #RequestBody CouponCodeDTO couponCodeDTO) {
#PostMapping("/create")
public ResponseEntity<?> createCoupon(
#JsonView(Views.Create.class) #RequestBody CouponCodeDTO couponCodeDTO) {
Here you find details
https://www.baeldung.com/jackson-json-view-annotation
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

#valid annotation sends exception in response to rest service

I am using custom validation in my rest web services.
#PUT
#Path("/{accountId}")
#Consumes({MediaType.APPLICATION_JSON})
public Response update(
#NotNull #ValidUUID #PathParam("accountId") UUID accUUID,
#NotNull #Valid Account changedAcc) {
synchronized (LOCK_MANAGER.getLock(accUUID)) {
return accHelper.update(this.getCurrentUser(), accUUID, changedAcc);
}
}
here is a glimpse at my Account class
#Table(keyspace = "Randomss", name = "accounts")
public class Account {
#PartitionKey
#Column(name = "id")
#JsonIgnore
private UUID id;
#Column(name = "acc_type")
#NotNull
#ValidString
#JsonIgnore
private String accType;
Now I send JSON data to this web service to update account,
but if I send some wrong json data
(e.g acc_type is expected as string and I send numeric data)
then it throws an exception.
How do I get it to send an error message instead of throwing an exception
(specifically, I want to send the error message)?
You need to write a javax.ws.rs.ext.Provider that implements an javax.ws.rs.ext.ExceptionMapper.
For example a generic ValidationExceptionMapper might look like:
#Provider
public class ValidationExceptionMapper
implements ExceptionMapper<ValidationException> {
public Response toResponse(ValidationException e) {
return Response.status(BAD_REQUEST).build();
}
}
You can choose a more appropriate response to return.

Any hint to simplify this POST? (Java)

I implemented this POST operation in Jax-RS and it is working fine, however I am sure I can send the body in other way somehow. Any hint to simplify it? (By receiving an object for instance?)
Thanks!
#POST
#Path("updateUser/{name}/{surname}/{address}")
#Produces(MediaType.TEXT_PLAIN)
public Response updateUser(#FormParam("user") String name,
#FormParam("surname") String surname,
#FormParam("address") String address) throws UnknownHostException {
User user;
user = new CorporateUser(name, surname, address);
usersService.updateUser(user);
return Response.ok(user).build();
}
You can pass json string of object by using #consumes annotaion.
#POST
#Path("/updateUser")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public Response updateUser(User bean) {
usersService.updateUser(user);
return Response.ok(user).build();
}
Add a JSON provider like Jackson to your project.
#POST
#Path("updateUser")
#Consumes(value = { MediaType.APPLICATION_JSON })
#Produces(value = { MediaType.APPLICATION_JSON })
public Response updateUser(NewCorporateUserRequest req) throws UnknownHostException {
User user;
user = new CorporateUser(req.getName(), req.getSurname(), req.getAddress());
usersService.updateUser(user);
return Response.ok().entity(user).type(MediaType.APPLICATION_JSON).build();
}
public class NewCorporateUserRequest implements java.io.Serializable {
private String name;
private String surname;
private String address;
... Getters and Setters.
}

Bean Validation and requested parameter in spring mvc

Is it possible to use validators for validation request parameters from javax.validation.constraints package in any way? I.e. like the following:
#Controller
public class test {
#RequestMapping("/test.htm")
public String test(#RequestParam("name") #NotNull String name)
{
return "index";
}
}
Use this way:
public class Comment{
#NotEmpty
#Length(max = 140)
private String text;
//Methods are omitted.
}
Now use #Valid in controller
#Controller
public class CommentController {
#RequestMapping(value = "/api/comment", method = RequestMethod.POST)
#ResponseBody
public Comment add(#Valid #RequestBody Comment comment) {
return comment;
}
}
When you are applying #Valid for Comment object in your cotroller,it will apply the validation mentioned in Comment class and its attribute like
#NotEmpty
#Length(max = 140)
private String text;
You can also check this out for little alternate way of doing:
http://techblogs4u.blogspot.in/2012/09/method-parameter-validation-in-spring-3.html
you can try this
#Controller
public class test {
#RequestMapping("/test.htm")
public String test(#RequestParam(value="name",required=true) String name)
{
return "index";
}
}

Categories