Skip to content

Commit

Permalink
Task 46 : Implement RateLimiterServiceImpl and UserServiceImpl with J…
Browse files Browse the repository at this point in the history
…ava Doc and Revision
  • Loading branch information
Rapter1990 committed Jul 2, 2024
1 parent 7b7a278 commit c3c67a1
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.springboot.ratelimiter.user.service.impl;

import com.springboot.ratelimiter.user.service.RateLimiterService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
* Service class named {@link RateLimiterServiceImpl} implementing for rate limiting operations using Redis.
* This service checks if the current request is allowed based on configured rate limiting rules.
*/
@Service
@RequiredArgsConstructor
public class RateLimiterServiceImpl implements RateLimiterService {

private final RedisTemplate<String, Object> redisTemplate;

@Value("${rate.limiter.max.requests}")
private int MAX_REQUESTS;

@Value("${rate.limiter.time.window.seconds}")
private int TIME_WINDOW_IN_SECONDS;

/**
* Checks if the current request is allowed based on rate limiting rules.
*
* @return true if the request is allowed, false otherwise
*/
@Override
public boolean isAllowed() {

String key = "rate_limiter:" + "user_creation";
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();

Integer currentCount = (Integer) valueOperations.get(key);
if (currentCount == null) {
valueOperations.set(key, 1, TIME_WINDOW_IN_SECONDS, TimeUnit.SECONDS);
return true;
} else if (currentCount < MAX_REQUESTS) {
valueOperations.increment(key);
return true;
} else {
return false;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.springboot.ratelimiter.user.service.impl;

import com.springboot.ratelimiter.common.exception.ratelimit.RateLimitExceededException;
import com.springboot.ratelimiter.common.exception.user.EmailAlreadyExistsException;
import com.springboot.ratelimiter.common.exception.user.UserNotFoundException;
import com.springboot.ratelimiter.common.model.page.CustomPage;
import com.springboot.ratelimiter.user.User;
import com.springboot.ratelimiter.user.mapper.CreateUserRequestToUserEntityMapper;
import com.springboot.ratelimiter.user.mapper.ListUserEntityToListUserMapper;
import com.springboot.ratelimiter.user.mapper.UpdateUserRequestToUserEntityMapper;
import com.springboot.ratelimiter.user.mapper.UserEntityToUserMapper;
import com.springboot.ratelimiter.user.model.UserEntity;
import com.springboot.ratelimiter.user.payload.request.CreateUserRequest;
import com.springboot.ratelimiter.user.payload.request.UpdateUserRequest;
import com.springboot.ratelimiter.user.payload.request.UserPagingRequest;
import com.springboot.ratelimiter.user.repository.UserRepository;
import com.springboot.ratelimiter.user.service.RateLimiterService;
import com.springboot.ratelimiter.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
* Service class named {@link UserServiceImpl} implementing for managing user operations.
*/
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService {

private final UserRepository userRepository;

private final RateLimiterService rateLimiterService;

private final UserEntityToUserMapper userEntityToUserMapper = UserEntityToUserMapper.initialize();

private final CreateUserRequestToUserEntityMapper createUserRequestToUserEntity =
CreateUserRequestToUserEntityMapper.initialize();

private final ListUserEntityToListUserMapper listUserEntityToListUserMapper =
ListUserEntityToListUserMapper.initialize();

/**
* Creates a new user based on the provided CreateUserRequest.
*
* @param createUserRequest the CreateUserRequest containing user details
* @return the created User object
*/
@Override
@Transactional
public User createUser(CreateUserRequest createUserRequest) {

if (!rateLimiterService.isAllowed()) {
throw new RateLimitExceededException("Rate limit exceeded");
}

if (userRepository.existsByEmail(createUserRequest.getEmail())) {
throw new EmailAlreadyExistsException(createUserRequest.getEmail());
}

final UserEntity userEntityToBeSaved = createUserRequestToUserEntity.map(createUserRequest);
final UserEntity savedUserEntity = userRepository.save(userEntityToBeSaved);
return userEntityToUserMapper.map(savedUserEntity);

}

/**
* Retrieves a user by their unique identifier.
*
* @param id the identifier of the user to retrieve
* @return the User object if found, otherwise null
*/
@Override
public User getUserById(String id) {

if (!rateLimiterService.isAllowed()) {
throw new RateLimitExceededException("Rate limit exceeded");
}

final UserEntity userEntity = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
return userEntityToUserMapper.map(userEntity);

}

/**
* Updates an existing user identified by their unique identifier.
*
* @param id the identifier of the user to update
* @param updateUserRequest the UpdateUserRequest containing updated user details
* @return the updated User object
*/
@Override
@Transactional
public User updateUser(String id, UpdateUserRequest updateUserRequest) {

if (!rateLimiterService.isAllowed()) {
throw new RateLimitExceededException("Rate limit exceeded");
}

if (userRepository.existsByEmail(updateUserRequest.getEmail())) {
throw new EmailAlreadyExistsException(updateUserRequest.getEmail());
}

final UserEntity userEntity = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));


UserEntity userEntityToBeUpdated = UpdateUserRequestToUserEntityMapper
.mapForUpdate(userEntity, updateUserRequest);

final UserEntity userEntityUpdated = userRepository.save(userEntityToBeUpdated);

return userEntityToUserMapper.map(userEntityUpdated);

}

/**
* Deletes a user by their unique identifier.
*
* @param id the identifier of the user to delete
*/
@Override
@Transactional
public void deleteUserById(String id) {

if (!rateLimiterService.isAllowed()) {
throw new RateLimitExceededException("Rate limit exceeded");
}

UserEntity userEntity = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));

userRepository.delete(userEntity);

}

/**
* Retrieves a paginated list of users based on the provided UserPagingRequest.
*
* @param userPagingRequest the UserPagingRequest containing pagination parameters
* @return a CustomPage containing the list of users
*/
@Override
public CustomPage<User> getUsers(UserPagingRequest userPagingRequest) {

if (!rateLimiterService.isAllowed()) {
throw new RateLimitExceededException("Rate limit exceeded");
}

final Page<UserEntity> userEntityPage = userRepository.findAll(userPagingRequest.toPageable());

if (userEntityPage.getContent().isEmpty()) {
throw new UserNotFoundException("Couldn't find any User");
}

final List<User> userDomainModels = listUserEntityToListUserMapper.toUserList(userEntityPage.getContent());

return CustomPage.of(userDomainModels, userEntityPage);

}

}

0 comments on commit c3c67a1

Please sign in to comment.