Intercept request to create RequestBody - java

I have a base CreateRequest class which I inherit in a couple of children, each responsible for some custom validations:
Base:
public class CreateEventRequest {
#NotEmpty
private String name;
#NotNull
#JsonProperty
private Boolean isPrivate;
}
Child:
public class CreateRegularEventRequest extends CreateEventRequest {
#NotNull
#Future
private LocalDateTime startTime;
#NotNull
#Future
private LocalDateTime endTime;
public LocalDateTime getStartTime() {
return startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
}
In order to take advantage of the validations, I've tried this:
#PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<CreateEventResponse> createEvent(HttpEntity<String> httpEntity,
#AuthenticationPrincipal SecuredUser user) {
try {
CreateEventRequest eventRequest = eventRequestFactory.getEventRequestString(httpEntity.getBody());
Set<ConstraintViolation<CreateEventRequest>> violations = validator.validate(eventRequest);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
return new ResponseEntity<>(this.service.createEvent(eventRequest, user), HttpStatus.CREATED);
} catch (ConstraintViolationException e) {
throw e;
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
My factory is:
public CreateEventRequest getEventRequestString(String json) {
try {
String eventTypeRaw = new ObjectMapper().readTree(json)
.get("event_type").asText();
if (!StringUtils.isEmpty(eventTypeRaw)) {
EventType eventType = EventType.valueOf(eventTypeRaw);
if (EventType.REGULAR.equals(eventType)) {
return objectMapper.readValue(json, CreateRegularEventRequest.class);
} else if (EventType.RECURRING.equals(eventType)) {
return objectMapper.readValue(json, CreateRecurringEventRequest.class);
}
}
return null;
} catch (JsonProcessingException e) {
return null;
}
}
This seems really hacky to me and do not scale for future Parent-Child relation, my question is, is there a way to intercept this request to create this child classes and pass it to the controller or some built-in validations to handle this scenario?
Thanks!

Did yo try:
public ResponseEntity<CreateEventResponse> createEvent( #Valid #RequestBody CreateRegularEventRequest reqBody ...) {
..
all the mapping and validation are done automatically

Related

How to handling bad request in REST service

I have a rest service that receives an object like this:
public class Item implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String ID;
private String name;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date insertDate;
//getter setter
}
My controller has a saveItem method of this type:
#RequestMapping(value = "/item", method = RequestMethod.POST, produces = "application/json")
public Object saveItem(#RequestBody(required = false) Item item) {
if (item == null) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
//code..
return new ResponseEntity<>(HttpStatus.OK);
}
when an incorrect date format arrives, an error is generated that is not handled by me because it does not enter the if. Why does this happen? How should it be managed?
The problem occurred even without the #JsonFormat annotation
I suppose you are using spring boot. You can use ControllerAdvice in order to define response in case of exceptions. You can define multiple handlers as multiple methods annotated with ExceptionHandler.
#Slf4j
#ControllerAdvice
public class RestControllerAdvice {
#ExceptionHandler(InvalidArgumentException.class)
public ResponseEntity<ResponseDto<Object>> handleException(Exception exception) {
log.error(exception.getMessage(), exception);
return new ResponseEntity<>(<someBody>, HttpStatus. BAD_REQUEST);
}
#ExceptionHandler(Exception.class)
public ResponseEntity<ResponseDto<Object>> handleException2(Exception exception) {
log.error(exception.getMessage(), exception);
return new ResponseEntity<>(<someBody>, HttpStatus. INTERNAL_SERVER_ERROR);
}
}

How to do following operation in Dao implementation?

I have got an array response from the following code.
I am able to return the array result as above but how do i return a json object using one of the values from that array? I am very much new to Java, springboot and hibernate. Any help would be appretiated!
GoalPlanController
#RequestMapping(method = RequestMethod.GET, path="/calculateRecurringAmount")
public ResponseEntity<Object> getCalculateRecurringAmount( String accountID) {
try {
logger.info("get recurring amount by accountid:->", accountID);
AccountsDTO[] goalPlan = goalPlanService.getCalculateRecurringAmount(accountID);
return new ResponseEntity<>(goalPlan, HttpStatus.OK);
}catch(Exception ex) {
logger.error("Exception raised retriving recurring amount using accountId:->" + ex);
ErrorDTO errors = new ErrorDTO();
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
return new ResponseEntity<>(errors, HttpStatus.SERVICE_UNAVAILABLE);
}
}
This is GoalPlanDaoImplementation
#Autowired
private GoalPlanRepository goalPlanRepository;
#Override
public List<Accounts> getCalculateRecurringAmount(String accountID) {
// TODO Auto-generated method stub
List<Accounts> goalPlan = null;
goalPlan = goalPlanRepository.getCalculateRecurringAmount(accountID);
return goalPlan.subList(0, 1);
}
GoalPlanRepository ->
public interface GoalPlanRepository extends JpaRepository<GoalPlan, String>{
#Query("select ac from Accounts ac where ac.userId = :accountID")
public List<Accounts> getCalculateRecurringAmount(#Param("accountID") String accountID);
}
I get a array result as follows
{
"accountID": "acc12345",
"accountName": "hellooee",
"accountType": "goalPlanner",
"userId": "abcd",
"bankName": null,
"bankId": null,
"debiitCardNumber": null,
"availableBalance": null,
}
]```
Now using accountID I need to return a json object like this
{
"calculatedGoalAmount": [
{
"goalFrequency": "Monthly",
"goalAmount": 0.4166666666666667,
"fromAccount": "acc12345"
},
{
"goalFrequency": "Quarterly",
"goalAmount": 1.25,
"fromAccount": "acc12345"
}
]
}
My AccountsDTO has folllowing
public class AccountsDTO {
private String accountID;
private String accountName;
private String accountType;
private String userId;
private String bankName;
private String bankId;
private String debitCardNumber;
//getters and setters
}
And initilAmount, goalTimePeriod, goalAmount are the values entered by user.
then i need to calculate
monthly = (goalAmount-initialAmount)/(12*goalTimePeriod)
quarterly = (goalAmount-initialAmount)/(4*goalTimePeriod)
accountId = (got from the response array above)
First You need to create two classes.
CustomResponse Class
public class CustomResponse {
private List<CalculatedGoalAmount> calculatedGoalAmount;
//getters and setters
}
CalculatedGoalAmount Class
public class CalculatedGoalAmount {
private String goalFrequency;
private double goalAmount;
private String fromAccount;
//getters and setters
}
Then inside your getCalculateRecurringAmount method write below code. Note that I have no idea about your AccountsDTO class.
#RequestMapping(method = RequestMethod.GET, path="/calculateRecurringAmount")
public ResponseEntity<Object> getCalculateRecurringAmount( String accountID) {
CalculatedGoalAmount calculatedGoalAmount = null;
CustomResponse customResponse = null;
try {
customResponse = new CustomResponse();
AccountsDTO[] goalPlan = goalPlanService.getCalculateRecurringAmount(accountID);
for (AccountsDTO accountsDTO : goalPlan) {
calculatedGoalAmount = new CalculatedGoalAmount();
calculatedGoalAmount.setFromAccount(accountsDTO.getFromAccount());
calculatedGoalAmount.setGoalAmount(accountsDTO.getGoalAmount());
calculatedGoalAmount.setGoalFrequency(accountsDTO.getFrequency());
customResponse.getCalculatedGoalAmount().add(calculatedGoalAmount);
}
return new ResponseEntity<>(customResponse, HttpStatus.OK);
}catch(Exception ex) {
logger.error("Exception raised retriving recurring amount using accountId:->" + ex);
ErrorDTO errors = new ErrorDTO();
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
return new ResponseEntity<>(errors, HttpStatus.SERVICE_UNAVAILABLE);
}
}

How to get rid of ifs in this case?

I have the building process for Criteria SQL requests but this repeating if() extremely horrible. I want to get rid of theirs. May by Reflection API help me but my struggle is failing. Maybe some who help me with a small example.
#AllArgsConstructor
class PhoneSpecification implements Specification<Phone> {
private final #NonNull PhoneFilter filter;
#Override
public Predicate toPredicate(Root<Phone> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.conjunction();
if (nonNull(filter.getId())) {
predicate.getExpressions().add(cb.equal(root.get("id"), filter.getId()));
}
if (nonNull(filter.getNote())) {
predicate.getExpressions().add(cb.like(root.get("note"), toLike(filter.getNote(), ANY)));
}
if (nonNull(filter.getNumber())) {
predicate.getExpressions().add(cb.like(root.get("number"), toLike(filter.getNumber(), ANY)));
}
if (nonNull(filter.getStatus())) {
predicate.getExpressions().add(cb.like(root.get("status"), toLike(filter.getStatus(), ANY)));
}
if (nonNull(filter.getOpName())) {
predicate.getExpressions().add(cb.like(root.get("opName"), toLike(filter.getOpName(), ANY)));
}
if (nonNull(filter.getOpLogin())) {
predicate.getExpressions().add(cb.like(root.get("opAccLogin"), toLike(filter.getOpLogin(), ANY)));
}
if (nonNull(filter.getOpPassword())) {
predicate.getExpressions().add(cb.like(root.get("opPassword"), toLike(filter.getOpPassword(), ANY)));
}
if (nonNull(filter.getRegFrom()) && nonNull(filter.getRegTo())) {
predicate.getExpressions().add(cb.between(root.get("regDate"), filter.getRegFrom(), filter.getRegTo()));
}
return predicate;
}
}
I thying something like this:
Lists.newArrayList(Phone.class.getDeclaredFields()).forEach(field -> {
field.setAccessible(true);
try {
field.get(filter)
//...
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
But I confused can I get value and type, and name of field...
Maybe reflection isn't a good way and you know better? Design pattern or something else?
This filter DTO:
#Data
class PhoneFilter {
private Pageable pageable;
private Integer id;
private Timestamp regFrom;
private Timestamp regTo;
private String number;
private String opLogin;
private String opPassword;
private String opName;
private String status;
private String note;
}

JPA not updating column with Converter class

I'm using a Converter class to store a complex class as JSON text in mySQL. When I add a new entity, the Converter class works as intended. However, when I update the entity, the data in the complex class is not updated in the database but it's updated in memory. Other attributes such as Lat and Long are updated. The breakpoint I placed at the convertToDatabaseColumn method and it did not trigger on update.
Object Class
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String city;
private String state;
private String country;
#Enumerated(EnumType.STRING)
private StatusType status;
private String street;
private double latitude;
private double longitude;
#Convert(converter=ProjectPropertyConverter.class)
private ProjectProperty property;
}
public class ProjectProperty {
private String description;
private List<String> projectImgs;
private Boolean hasImages;
}
Property Converter Class
#Converter (autoApply=true)
public class ProjectPropertyConverter implements AttributeConverter<ProjectProperty, String> {
#Override
public String convertToDatabaseColumn(ProjectProperty prop) {
try {
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(prop);
return jsonString;
} catch (Exception e) {
System.out.print(e.toString());
return null;
}
}
#Override
public ProjectProperty convertToEntityAttribute(String jsonValue) {
try {
ObjectMapper mapper = new ObjectMapper();
ProjectProperty p = mapper.readValue(jsonValue, ProjectProperty.class);
if(p.getProjectImgs().isEmpty())
{
p.setHasImages(Boolean.FALSE);
}
else
{
p.setHasImages(Boolean.TRUE);
}
return p;
} catch (Exception e) {
System.out.print(e.toString());
return null;
}
}
}
Method to Update Database
public void modifyEntity(Object entity, String query, HashMap params) {
try {
tx.begin();
em.flush();
tx.commit();
} catch (Exception e) {
e.toString();
}
}
I came here looking for same answers. Turns out the problem is JPA doesn't know that your object is dirty. This was solved by implementing equals()/hashcode() methods on this complex objects. In your example, implement equals and hashcode for ProjectProperty
Once that is done, JPA is able to identify via these methods that the underlying object is dirty and converts and persists.

JsonMappingException: Unexpected token (START_OBJECT)

I'm having problem with the user of Codehaus Jackson. I have an object with the next attributes and mapper declaration:
public class AuthenticatedPrincipal implements Serializable, Principal {
#JsonIgnore
private final static ObjectMapper mapper = new ObjectMapper().enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY).enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)
.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL).setVisibility(JsonMethod.FIELD, JsonAutoDetect.Visibility.ANY);
private String name;
private Collection<String> roles;
private Collection<String> groups;
private boolean adminPrincipal;
...
#JsonIgnore
public String serialize() {
try {
return mapper.writeValueAsString(this);
} catch (IOException e) {
throw new RuntimeException("Unable to serialize Principal:" + toString(), e);
}
}
#JsonIgnore
public static AuthenticatedPrincipal deserialize(String json) {
try {
return mapper.readValue(json, AuthenticatedPrincipal.class);
} catch (IOException e) {
throw new RuntimeException("Unable to serialize Principal:" + json, e);
}
}
}
That is used from another class:
public class AuthRequest {
#Transient
private AuthenticatedPrincipal principal;
#PreUpdate
#PrePersist
public void encodePrincipal() {
if (principal != null) {
this.encodedPrincipal = principal.serialize();
}
}
#PostLoad
#PostPersist
#PostUpdate
public void decodePrincipal() {
if (StringUtils.isNotBlank(encodedPrincipal)) {
this.principal = AuthenticatedPrincipal.deserialize(encodedPrincipal);
}
}
}
When I execute the funtionality that generate a String like this:
Principal:{"adminPrincipal":false,"displayName":"sdfas","groupAware":false,"name":"sdfas"}
When the method AuthenticatedPrincipal.deserialize(encodedPrincipal); is called parsing a Json parameter but that method fail with this error:
org.codehaus.jackson.map.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class com.trent.app.lib.principal.AuthenticatedPrincipal
at [Source: java.io.StringReader#40fa255; line: 1, column: 1]
Can anyone help me?
Principal:{"adminPrincipal":false,"displayName":"sdfas","groupAware":false,"name":"sdfas"}
Is not valid JSON. It needs to look like this
{"adminPrincipal":false,"displayName":"sdfas","groupAware":false,"name":"sdfas"}
(without the Principal: at the start)

Categories