set call limitation for some service - spring

i am working on a spring boot project and i want ti set limitation for some service to call.for example i want in 1 minute,5 request send to this endpoint.
#GetMapping(path = "/allJobsByChartCode/{chartCode}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Page<?>> getAllOCMJobsByChartCode(#PathVariable("chartCode") String chartCode, #RequestParam int page, #RequestParam int size, #RequestParam(required = false, name = "orderby") String orderby, #RequestParam(required = false, name = "direction") String direction) {
Page<OCMJobsDTO> gridData = ocmJobsService.getAllOCmJobsByChartCode(chartCode, PageRequest.of(page, size), orderby, direction);
if (gridData.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(gridData, HttpStatus.OK);
}

Related

How to Implement Spring Boot Paging and multiple filtering using Criteria Api

Today was my first time with criteria Api. i have create an application in Spring boot in order to make server side pagination with multiple key of an entity filtering.
So in my case i have created an entity called User and i started to implement code to paginate the data but with Criteria API.
After implementing the pagination without filtering and Criteria Api everything worked perfectly! every page return 8 results and it is well organized by current page, totalPages, etc ..
But later i have decided to start to implement Criteria API by searching my entity username and userRole. my goal is to make that paging i did in the last step mixed with filtering of keys.
In case that my keys are empty then paginate else paginate and filter.
So after implementing i have discouvered that filtering works perfectly but pagination do not work correctly anymore because i am receiving all the results in every page.
that problem happened only after implementing Criteria API which i just discovered today.
I am trying to reach my goal by keeping all i spoke about in one query and paginate correctly
Here what i have done with my UserCriteriaRepository
#Repository
public class UserCriteriaRepository {
private final EntityManager entityManager;
private final CriteriaBuilder criteriaBuilder;
public UserCriteriaRepository(EntityManager entityManager) {
this.entityManager = entityManager;
this.criteriaBuilder = entityManager.getCriteriaBuilder();
}
public ResponsePagingAndSorting<UserDTO> findAllWithFilters(int page, int size, String username, String userRole) {
CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
Root<User> userRoot = criteriaQuery.from(User.class);
Predicate predicate = getPredicate(username,userRole, userRoot);
criteriaQuery.where(predicate);
TypedQuery<User> typedQuery = entityManager.createQuery(criteriaQuery);
typedQuery.setMaxResults(size * 10);
long usersCount = getUsersCount(predicate);
int totalPages = (int) ((usersCount / size) + 1);
List<User> userList = new ArrayList<>();
userList = typedQuery.getResultList();
List<UserDTO> userDTOList = UserMapper.toListDTO(userList);
return new ResponsePagingAndSorting<UserDTO>("Users List ",200,userDTOList,page,
usersCount, totalPages);
}
private Predicate getPredicate(String username, String userRole,
Root<User> userRoot) {
List<Predicate> predicates = new ArrayList<>();
if(Objects.nonNull(username)){
predicates.add(
criteriaBuilder.like(userRoot.get("username"),
"%" + username + "%")
);
}
if(Objects.nonNull(userRole)){
UserRoleType userRoleType = null;
switch (userRole){
case "MEMBER": userRoleType = UserRoleType.MEMBER;
break;
case "ADMIN": userRoleType = UserRoleType.ADMIN;
break;
case "SUPER_ADMIN": userRoleType = UserRoleType.SUPER_ADMIN;
break;
}
if (userRoleType != null) {
predicates.add(
criteriaBuilder.equal(userRoot.get("userRole"),
userRoleType)
);
}
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}
private Pageable getPageable(int page, int size) {
return PageRequest.of(page,size);
}
private long getUsersCount(Predicate predicate) {
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<User> countRoot = countQuery.from(User.class);
countQuery.select(criteriaBuilder.count(countRoot)).where(predicate);
return entityManager.createQuery(countQuery).getSingleResult();
}
}
My Service:
//paging with Criteria Api
#Override
public ResponsePagingAndSorting<UserDTO> getAllUsers(int page, int size ,String username, String userRole) {
ResponsePagingAndSorting<UserDTO> response = userCriteriaRepository.findAllWithFilters(page,size, username, userRole);
return response;
}
My Controller
#GetMapping("/get/all")
#ResponseBody
public ResponsePagingAndSorting<UserDTO> getAllUsers(#RequestParam(defaultValue = "0") int page,
#RequestParam(defaultValue = "8") int size,#RequestParam(defaultValue = "") String username,
#RequestParam(defaultValue = "") String userRole) {
ResponsePagingAndSorting<UserDTO> response = userService.getAllUsers(page,size,username,userRole);
log.warn("Response controller is " + response);
return response;
}
My ResponsePagingAndSorting dto object:
#AllArgsConstructor
#NoArgsConstructor
#Data
public class ResponsePagingAndSorting<T> {
String message;
int status_code;
List<T> body = new ArrayList<>();
int currentPage;
long totalItems;
int totalPages;
}
In Database i have in total of 17 users, so in postman i see all the 17 everytime but if i search by username or userRole or both it works? why pagination works only when i user the filters?
Can not i paginate data without seraching by username or userRole?
what is wrong with my code ???
how to make pagination works correctly with the filtering enabled or disabled?
Why if
Postman screen capture:
unfortunately all results are displayed in page 0
Screen Capture pagination + username filter: works correctly
i hope that i will find a solution
Problem Solved by using JpaSpecification
here the Specification class:
#Component
public class UserSpecification {
public Specification<User> getUsers(String username, String userRole) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (username != null && !username.isEmpty()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("username")),
"%" + username.toLowerCase() + "%"));
}
if (userRole != null && !userRole.isEmpty()) {
UserRoleType userRoleType = null;
switch (userRole) {
case "MEMBER": userRoleType = UserRoleType.MEMBER;
break;
case "ADMIN": userRoleType = UserRoleType.ADMIN;
break;
case "SUPER_ADMIN": userRoleType = UserRoleType.SUPER_ADMIN;
break;
}
predicates.add(criteriaBuilder.equal(root.get("userRole"), userRoleType));
}
query.orderBy(criteriaBuilder.asc(root.get("username")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}

ServiceResponse mocked which gives a null value and not expected this null

I'm writing j-unit Test-cases for my services and in which i couldn't mock service Response properly, Which is giving me a null. can somebody help me in this issue.
public ResponseEntity<Void> lockGet(
#ApiParam(value = "Unique identifier for this request.", required = true) #RequestHeader(value = "service-id", required = true) String serviceId,
#ApiParam(value = "Logged in userid.", required = true) #RequestHeader(value = "user-id", required = true) String userId,
#ApiParam(value = "Unique messageid.", required = true) #RequestHeader(value = "message-id", required = true) String messageId,
#RequestHeader(value = "access-token", required = true) String accessToken,
#ApiParam(value = "Unique id of the doamin of the entity", required = true) #RequestParam(value = "lockDomainId", required = true) Long lockDomainId,
#ApiParam(value = "Unique id of the entity to be fetched", required = true) #RequestParam(value = "lockEntityId", required = true) Long lockEntityId,
HttpServletRequest request, HttpServletResponse response) {
ResponseEntity<Void> result = null;
if (request.getAttribute("user-id") != null)
userId = (String) request.getAttribute("user-id");
String logContext = "||" + lockDomainId + "|" + lockEntityId + "||";
ThreadContext.put("context", logContext);
long t1 = System.currentTimeMillis();
LOG.info("Method Entry: lockGet" + logContext);
ServiceRequest serviceRequest = AppUtils.mapGetRequestHeaderToServiceRequest(serviceId, userId, lockDomainId,
lockEntityId);
try {
ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
// set all the response headers got from serviceResponse
HeaderUtils.setResponseHeaders(serviceResponse.getResponseHeaders(), response);
result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
} catch (Exception ex) {
LOG.error("Error in lockGet", ex);
result = new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
ThreadContext.put("responseTime", String.valueOf(System.currentTimeMillis() - t1));
LOG.info("Method Exit: lockGet");
return result;
}
#Test
public void testLockGetForError() {
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
LockService service = Mockito.mock(LockService.class);
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
I tried in different scenario's which couldn't fix this issue. Can someone help me out. Thanks in advance.
From the code that you have put , the issue that i see is that you are actually mocking the LockService object but when calling the lockApiController.lockGet method the code is not actually working with the mocked LockService since lockApiController has an LockService object of it's own.
One way to solve this issue is to inject the mocked LockService
object into the lockApiController object using #Spy. This way
when the getLock() is called it will be actually called on the
mocked object and will return the mock response provided.
So in your test :
#Test
public void testLockGetForError() {
LockService service = Mockito.mock(LockService.class);
LockApiController lockApiController = Mockito.spy(new LockApiController(service));
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
So you can try passing the mocked LockService object to the spy object.
Another way is to try using the #InjectMocks to inject the mocked
object into the LockApiController.
#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
For example:
#Mock
Map<String, String> wordMap;
#InjectMocks
MyDictionary dic = new MyDictionary();
#Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.getMeaning("aWord"));
}
For the class:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
For both of these to work , you must be having a constructor or appropriate setters to set the mock object to the LockApiController class.
Reference : https://howtodoinjava.com/mockito/mockito-annotations/

BackPressure in Spring Boot application

I want to apply backpressure on my application webservice. So that if number of request is high say more than N I can notify the client. Below is my controller
Before WebFlux
#RequestMapping(value = "/tasks", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseTaskModel> taskForUser(#RequestParam(value = "userId", required = true) String userId,
#RequestParam(required = false) Map<String, String> userData) {
ResponseTaskModel responseTaskModel = service.retrieveNextTaskForUser(userId.toLowerCase(), userData);
return new ResponseEntity<ResponseTaskModel>(responseTaskModel, HttpStatus.OK);
}
After WebFlux
#RequestMapping(value = "/tasks/v1", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Mono taskForUserV1(#RequestParam(value = "userId", required = true) String userId,
#RequestParam(required = false) Map<String, String> userData) {
return service.taskForUserV1(userId, userData);
}
And the service method I am calling is a blocking call to db with three queries, I'm using a postgres database.
Now how shall I apply backpressure on the Mono object returned?

How Spring Hystrix helps when one microservice data requires in other microservices?

Hystrix helps for false tolerence and latency tolerence.By using fallback method we can show empty data or some message that that service is down, but if MicroService1 id needs in other business logic how to handle this?
MicroService1-Here is my Sample Code
This is the API which will be called by Other microservice2. Its Needs to return one String as ID.
#RequestMapping(value = "/list")
public String list() {
return "Some Id";
}
Microservice2- Calling MicroService1 API
#HystrixCommand(commandProperties = { #HystrixProperty(name = "execution.timeout.enabled", value = "true"),
#HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
#HystrixProperty(name = "fallback.enabled", value = "true")
}, fallbackMethod = "fallback", commandKey = "list", groupKey = "EmployeeServiceImpl")
public String list() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(microservice1apiurl, String.class);
}
public String fallback() {
return "";
}

Java: GroupSequenceProvider for Validation, object is null in getValidationGroups method

This is what I am trying to achieve:
I have an update request object and user is allowed to do Partial Updates. But I want to validate the field only if it is in the request body. Otherwise, it is OK to be null. To achieve this, I am using GroupSequenceProvider to let the Validator know what groups to validate. What am I doing wrong here? If there is a blunder, how do I fix it?
Documentation: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html#example-implementing-using-default-group-sequence-provider
#GroupSequenceProvider(UpdateUserRegistrationGroupSequenceProvider.class)
public class UpdateUserRegistrationRequestV1 {
#NotBlank(groups = {EmailExistsInRequest.class})
#Email(groups = {EmailExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {EmailExistsInRequest.class})
private String email;
#NotNull(groups = {PasswordExistsInRequest.class})
#Size(min = 8, max = 255, groups = {PasswordExistsInRequest.class})
private String password;
#NotNull(groups = {FirstNameExistsInRequest.class})
#Size(max = 255, groups = {FirstNameExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {FirstNameExistsInRequest.class})
private String firstName;
// THERE ARE GETTERS AND SETTERS BELOW
}
Group Sequence Provider Code:
public class UpdateUserRegistrationGroupSequenceProvider implements DefaultGroupSequenceProvider<UpdateUserRegistrationRequestV1> {
public interface EmailExistsInRequest {}
public interface PasswordExistsInRequest {}
public interface FirstNameExistsInRequest {}
#Override
public List<Class<?>> getValidationGroups(UpdateUserRegistrationRequestV1 updateUserRegistrationRequestV1) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add(Default.class);
defaultGroupSequence.add(UpdateUserRegistrationRequestV1.class);
if(StringUtils.hasText(updateUserRegistrationRequestV1.getEmail())) {
defaultGroupSequence.add(EmailExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getPassword())) {
defaultGroupSequence.add(PasswordExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getFirstName())) {
defaultGroupSequence.add(FirstNameExistsInRequest.class);
}
return defaultGroupSequence;
}
}
I am using Spring MVC, so this is how my controller method looks,
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable("userId") Long userId,
#RequestBody #Valid UpdateUserRegistrationRequestV1 request) {
logger.info("Received update request = " + request + " for userId = " + userId);
registrationService.updateUser(userId, conversionService.convert(request, User.class));
}
Now the problem is, the parameter "updateUserRegistrationRequestV1" in the UpdateUserRegistrationGroupSequenceProvider.getValidationGroups method is null. This is the request object that I am sending in the request body and I am sending email field with it.
What am I doing wrong?
I too went through the same issue ,and hopefully solved it
You just have to check the object is null and put all your conditions inside it.
public List<Class<?>> getValidationGroups(Employee object) {
List<Class<?>> sequence = new ArrayList<>();
//first check if the object is null
if(object != null ){
if (!object.isDraft()) {
sequence.add(Second.class);
}
}
// Apply all validation rules from default group
sequence.add(Employee.class);
return sequence;
}

Resources