Bean validation in springboot - java

public class test{
private String id;
private String status;
private Integer alertsCount
}
I have a class test, when a rest api implemented using springboot with post request gets triggered input json looks like
{
"id": "1",
"status": "Cancelled",
"alertCount": 10
}
at my model class i need to add restriction to prevent status to be one of the below values
"Successfull", "Cancelled", "In Progress", "On Hold"
How can i achieve this.

#Pattern(regexp = "^(Successfull)|(Cancelled)|(In Progress)|(On Hold)$"
private String status;
DON'T FORGET TO USE #Valid AT CONTROLLER

Add Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
then add #Valid to your Request Handler Method parameter
#PostMapping("/test/")
ResponseEntity<String> addTest(#Valid #RequestBody Test test) {
return ResponseEntity.ok();
}
and add bean-validation annotations to your Json Object, for exmaple:
public class Test{
#NotNull
private String id;
#NotNull
#Pattern(regexp="(Successfull)|(Cancelled)|(In Progress)|(On Hold)")
private String status;
#NotNull
private Integer alertsCount
}
Some other example: https://stackoverflow.com/a/9994590/280244 that also contains the handling of validation errors, via BindingResult
BTW: An other idea is to use an Enum instead of an String (that causes other Exception when the string does not match!) but then the whitespace must been replaced)

Since status field has to have value from predefined set of constants, it would be better to provide an enum containing all available status strings.
public class TestModel{
private String id;
private StatusEnum status;
private Integer alertsCount;
}
public enum StatusEnum {
SUCCESSFUL("Successful"),
CANCELLED("Cancelled"),
IN_PROGRESS("In progress"),
ON_HOLD("On hold");
private String statusTag;
StatusEnum(String status){
this.statusTag = status;
}
}
Enums are designed for exactly such situations, according to Java documentation (https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html):
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.

What about using Enums? This decision is clean and simple.
public class test{
private String id;
private STATUS status;
private Integer alertsCount
}
public enum STATUS {
public Successfull;
public Cancelled;
public InProgress;
public OnHold
}
Write your custom ConstraintValidator.

Related

How do i validate or restrict enum type to accept only specific values

In spring boot & hibernate, i'm making an API to change order status of a product, in orderStatus class i have two enum type fields, fromStatus and toStatus
My orderStatus class looks like this:
#Entity
public class OrderStatus {
#Id
private Long orderProductId;
#Enumerated(value = EnumType.STRING)
private Status fromStatus;
#Enumerated(value = EnumType.STRING)
private Status toStatus;
enum:
public enum Status{
ORDER_PLACED, CANCELLED, ORDER_CONFIRMED, ORDER_REJECTED, REFUND_INITIATED, ORDER_SHIPPED, DELIVERED, CLOSED,
RETURN_REQUESTED, RETURN_REJECTED, RETURN_APPROVED, PICK_UP_INITIATED, PICK_UP_COMPLETED, REFUND_COMPLETED
}
What i want to restrict is that when fromStatus is in ORDER_PLACED status, toStatus can only be
ORDER_CONFIRMED, ORDER_REJECTED or CANCELLED.
Mapping chart for possible values
Currently i don't have any validations so when someone uses changeOrderStatus API they can change both fields to anything they want,
for eg: from_status: ORDER_PLACED and to_status: REFUND_INITIATED and i don't want that to happen.
Allowed toStatus values should depend on values in fromStatus
I could make a method to do so where i check each possible value with if conditions, i want to know if there's any smarter approach to this.
Thank you
An easy way to do this is to include a validate(Status status) in your Enum
like:
enum Status {
ORDER_PLACED {
#Override
public boolean validate(Status status) {
return status== CANCELLED || status== ORDER_CONFIRMED || status== ORDER_REJECTED;
}
},
CANCELLED{
#Override
public boolean validate(Status status) {
return ...;
}
},
ORDER_CONFIRMED {
...
}, ...
public abstract boolean validate(Status status)
}
A better way (but more complex) is to use a StateMachine like Spring Statemachine
If you need to be sure that no one can save the OrderStatus with the wrong enum values, you have to build some business logic upon that.
I would suggest to move fromStatus and toStatus to the embedded object and implement the validation in this class.
#Entity
public class OrderStatus {
#Id
private Long orderProductId;
#Embedded
private ComplexStatus status;
}
#Embeddable
class ComplexStatus {
#Enumerated(value = EnumType.STRING)
private Status fromStatus;
#Enumerated(value = EnumType.STRING)
private Status toStatus;
// getters, setters, constructors
}

Mapping all request params into an object in Spring Controller [duplicate]

This question already has answers here:
In Spring-mvc the attribute names in view have to always match the property names in model?
(3 answers)
How to customize parameter names when binding Spring MVC command objects?
(10 answers)
Closed 3 years ago.
So, url requested looks like
localhost:8080/contacts?id=22&name=John&eventId=11
and also I got an object to map request into
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
}
I use a controller method like passing my request params into an object
#GetMapping("/contacts")
public ContactDTO contacts(ContactDTO contact) {
// everything is awesome! contact maps clearly
return contact;
}
The question is how to map like this but have different name
localhost:8080/contacts?id=22&name=John&event_id=11
Setting #JsonAttribute doesn't works because Jackson mapper works only in requestbody.
Maybe I should write custom HandlerMethodArgumentResolver or something like that?
P.S.
I've got a dirty hack (objectMapper is injected, so I can use #JsonAttributes),
But this case fails on array mapping, same mapping with requestbody works fine
#GetMapping("/contacts")
public ContactsDTO contacts(#RequestParam Map<String,String> params) {
ContactDTO contactDTO = objectMapper.convertValue(params,ContactDTO.class);
return contactDTO;
}
Since it is an API design requirement, it should be clearly reflected in the corresponding DTO's and endpoints.
Usually, this kind of requirement stems from a parallel change and implies that the old type queries will be disabled during the contract phase.
You could approach the requirement by adding the required mapping "query-parameter-name-to-property-name" by adding it to the ContactDTO. The simplest way would be just to add an additional setter like below
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
public void setEvent_id(Long eventId) {
this.eventId = eventId;
}
}
If you prefer immutable DTO's, then providing a proper constructor should work as well
#Value
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
public ContactDTO(Long id, String name, String eventId, String event_id) {
this.id = id;
this.name = name;
this.eventId = eventId != null ? eventId : event_id;
}
}
Use something like
#RequestParam(name="event_id", required = true) long eventId
in the parameter list to change the parameter name.
Use #RequestBody insteaf of #requestparam.

Jackson - DTO int to String conversion

Working on a REST client that calls another server which returns the following object:
public class ObjectOriginal {
private int id;
private String name;
// constructor/getters/setters
}
I need to obfuscate the id. To do so I'm using an already existing service that transforms the id into a unique generated String so that the person calling my service doesn't know the REAL id but can still request info about it with the unique string.
So I'm basically trying to return to the caller this object:
public class ObjectNew {
private String id;
private String name;
// constructor/getters/setters
}
Do I need to have a copy of ObjectOriginalDTO + create a ObjectNew DTO + create a mapper to go from one to the other.
Or can I configure Jackson to deserialize the id field as a String and not an int?
You can do this using your own Serializer/Deserializer.
You have to implement your Serializer/Deserializer that will extends respectively BeanSerializerModifier/BeanDeserializerModifier and configuring your Module with them for instance Or use the annotation base solution as explained in this tutorial, there are plenty of references on the web for such a thing. then you'll have more controlle over the way to map your id.
If you don't want to have custom deserializer you can have:
public class ObjectNewDto {
private String id;
private String name;
// constructor/getters/setters
}
and another object:
public class ObjectOriginal {
private int id;
private String name;
// construxtor/getters/settes
}
Now after validating ObjectNewDto you can map it via your obfuscator service into ObjectOriginal , then validate this Object original and so on...

Creating json using jackson and ignoring object in some condition

i want to apply #JsonIgnore in some condition only .
for example in one case i may only need test object not all the questions in test.
but in other case i may required to have all the question as well by using #JsonManagedReference
class Test{
private string testName;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="abc")
#JsonIgnore
private Set<question> question;
}
Try out #JsonView. It allows you to conditionally include/suppress properties based on a view (a marker class) that is provided during serialization.
class Test {
private String testName;
#JsonView(SomeCondition.class)
private Set<Question> questions;
}
#GET
#Path("/testWithCondition")
#JsonView(SomeCondition.class)
public Response getTestWithCondition() {
Test test = testService.lookupTest();
return Response.ok(test).build();
}
Note: You may have to disable MapperFeature.DEFAULT_VIEW_INCLUSION on your ObjectMapper.

Jackson: Ignore Json configuration value

I have the following json file:
{
"segments": {
"externalId": 123,
"name": "Tomas Zulberti",
"shouldInform": true,
"id": 4
}
}
But the java model is as follows:
public class Segment {
private String id;
private String name;
private boolean shouldInform;
// getter and setters here...
}
When Jackson is parsing it raises an exception becuase there is no getter or setter for the field "externalId". It there a decorator that can be used to ignore a json field?
You can use annotation #JsonIgnoreProperties; if it's just one value you want to skip, something like:
#JsonIgnoreProperties({"externalId"})
or to ignore anything that can't be used:
#JsonIgnoreProperties(ignoreUnknown=true)
There are other ways to do it too, for rest check out FasterXML Jackson wiki.
Also we can use mapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
instead #JsonIgnoreProperties(ignoreUnknown=true)
but for particular property we can use
#JsonIgnoreProperties({"externalId"})
public class Segment {
private String id;
private String name;
private boolean shouldInform;
// getter and setters here...
}

Categories

Resources