Skip to content

Commit

Permalink
Merge pull request #245 from bounswe/backend/feature/243-recipe-user-…
Browse files Browse the repository at this point in the history
…rating

Backend/feature/243 recipe user selfRating
  • Loading branch information
atakanyasar authored May 15, 2024
2 parents 182c28c + 8c88605 commit bf91f52
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.group1.cuisines.dao.response.ErrorResponse;
import com.group1.cuisines.entities.User;
import com.group1.cuisines.repositories.UserRepository;
import com.group1.cuisines.services.JwtService;
import com.group1.cuisines.services.UserService;
import io.jsonwebtoken.ExpiredJwtException;
Expand All @@ -17,18 +19,25 @@
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final UserRepository userRepository;
private final JwtService jwtService;
private final UserService userService;
private final ObjectMapper objectMapper;

private static final Logger logger = LoggerFactory.getLogger(UserService.class);

@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
Expand Down Expand Up @@ -57,9 +66,7 @@ protected void doFilterInternal(
StringUtils.isNotEmpty(userEmail) &&
SecurityContextHolder.getContext().getAuthentication() == null
) {
UserDetails userDetails = userService
.userDetailsService()
.loadUserByUsername(userEmail);
UserDetails userDetails = userDetailsService().loadUserByUsername(userEmail);
if (jwtService.isTokenValid(jwt, userDetails)) {
SecurityContext context =
SecurityContextHolder.createEmptyContext();
Expand Down Expand Up @@ -88,4 +95,20 @@ protected void doFilterInternal(
}
filterChain.doFilter(request, response);
}

public UserDetailsService userDetailsService() {
return new UserDetailsService() {

@Override
public UserDetails loadUserByUsername(String usernameOrEmail) {
logger.debug("Attempting to find user by email or username: {}", usernameOrEmail);

User user = userRepository.findByEmailOrUsername(usernameOrEmail, usernameOrEmail)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username or email : " + usernameOrEmail));

return user;
}

};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@
@RequiredArgsConstructor
public class SecurityConfiguration {

private final UserService userService;

private final JwtAuthenticationFilter jwtAuthenticationFilter;


@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
Expand Down Expand Up @@ -69,7 +66,7 @@ public PasswordEncoder passwordEncoder() {
public AuthenticationProvider authenticationProvider() { // Authentication provider
DaoAuthenticationProvider authProvider =
new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userService.userDetailsService());
authProvider.setUserDetailsService(jwtAuthenticationFilter.userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.group1.cuisines.dto.*;
import com.group1.cuisines.entities.Comment;
import com.group1.cuisines.entities.User;
import com.group1.cuisines.services.AuthenticationService;
import com.group1.cuisines.services.RecipeService;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
Expand All @@ -20,6 +21,8 @@
public class RecipeController {
@Autowired
private RecipeService recipeService;
@Autowired
private AuthenticationService authenticationService;


@GetMapping("/recipes/{recipeId}")
Expand All @@ -45,14 +48,13 @@ public ResponseEntity<List<RecipeDto>> getRecipes(@RequestParam(required = false

@PostMapping("/recipes")
public ResponseEntity<?> createRecipe(@RequestBody NewRecipeDto newRecipe) throws Exception{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authenticationService.getUser().map(User::getUsername).orElse(null);

// Check if the user is authenticated
if (authentication == null || authentication.getPrincipal().equals("anonymousUser")) {
if (username == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication required.");
}

String username = authentication.getName();
RecipeDetailDto recipeDetails = recipeService.createRecipe(newRecipe, username);
if (recipeDetails != null) {
return ResponseEntity.status(HttpStatus.CREATED).body(recipeDetails);
Expand All @@ -62,14 +64,12 @@ public ResponseEntity<?> createRecipe(@RequestBody NewRecipeDto newRecipe) throw
}
@DeleteMapping("/recipes/{id}")
public ResponseEntity<?> deleteRecipe(@PathVariable Integer id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authenticationService.getUser().map(User::getUsername).orElse(null);

// Check if the user is authenticated
if (authentication == null || authentication.getPrincipal().equals("anonymousUser")) {
if (username == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication required.");
}

String username = authentication.getName();
if (recipeService.deleteRecipe(id, username)) {
return ResponseEntity.status(HttpStatus.NO_CONTENT).body("Recipe deleted successfully.");
} else {
Expand All @@ -78,12 +78,11 @@ public ResponseEntity<?> deleteRecipe(@PathVariable Integer id) {
}
@PostMapping("/recipes/{recipeId}/rating")
public ResponseEntity<?> rateRecipe(@PathVariable Integer recipeId, @RequestBody RatingDto ratingDto) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getPrincipal().equals("anonymousUser")) {
String username = authenticationService.getUser().map(User::getUsername).orElse(null);
if (username == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication required.");
}

String username = authentication.getName(); // Assuming the username can be obtained like this
boolean success = recipeService.rateRecipe(recipeId, username, ratingDto.getRating());

if (success) {
Expand All @@ -95,12 +94,11 @@ public ResponseEntity<?> rateRecipe(@PathVariable Integer recipeId, @RequestBody

@PostMapping("/recipes/{recipeId}/bookmarks")
public ResponseEntity<?> bookmarkRecipe(@PathVariable Integer recipeId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getPrincipal().equals("anonymousUser")) {
String username = authenticationService.getUser().map(User::getUsername).orElse(null);
if (username == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication required.");
}

String username = authentication.getName(); // Assuming the username can be obtained like this
boolean success = recipeService.bookmarkRecipe(recipeId, username);

if (success) {
Expand All @@ -112,8 +110,8 @@ public ResponseEntity<?> bookmarkRecipe(@PathVariable Integer recipeId) {

@GetMapping("/recipes/{recipeId}/bookmarks")
public ResponseEntity<?> getBookmarks(@PathVariable Integer recipeId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getPrincipal().equals("anonymousUser")) {
String username = authenticationService.getUser().map(User::getUsername).orElse(null);
if (username == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication required.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.*;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecipeDetailsDto {
Expand All @@ -21,6 +22,7 @@ public class RecipeDetailsDto {
private CuisineDto cuisine;
private DishDto dish;
private Double avgRating;
private Integer selfRating;

private AuthorDto author;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface RatingRepository extends JpaRepository<Rating,Integer> {
Rating findByRecipeIdAndUserId(Integer recipeId, Integer userId);
Optional<Rating> findByRecipeIdAndUserId(Integer recipeId, Integer userId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class AuthenticationService {
Expand Down Expand Up @@ -93,4 +97,14 @@ public ApiResponse<AuthenticationTokenResponse> signin(
);
}
}

public Optional<User> getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getPrincipal().equals("anonymousUser")) {
return Optional.empty();
}
String username = authentication.getName();
return userRepository.findByUsername(username);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

@Service
public class RecipeService {
@Autowired
private AuthenticationService authenticationService;
@Autowired
private IngredientsRepository ingredientRepository;

Expand Down Expand Up @@ -126,7 +128,7 @@ public boolean rateRecipe(Integer recipeId, String username, Integer ratingValue
Recipe recipe = recipeRepository.findById(recipeId).orElse(null);

if (user.isPresent() && recipe != null && ratingValue >= 1 && ratingValue <= 5) {
Rating existingRating = ratingRepository.findByRecipeIdAndUserId(recipeId, user.get().getId());
Rating existingRating = ratingRepository.findByRecipeIdAndUserId(recipeId, user.get().getId()).orElse(null);
if (existingRating != null) {
return false; // Indicates that the user has already rated
}
Expand Down Expand Up @@ -185,59 +187,26 @@ public List<CommentsDto> getCommentsByRecipeId(Integer recipeId) {

public RecipeDetailsDto getRecipeById(Integer recipeId) {
Optional<Recipe> recipe = recipeRepository.findById(recipeId);


if (recipe.isPresent()) {
CuisineDto cuisineDto = new CuisineDto();
Recipe r = recipe.get();
if (r.getDish() != null && !r.getDish().getCuisines().isEmpty()) {

cuisineDto.setId(r.getDish().getCuisines().get(0).getId());
cuisineDto.setName(r.getDish().getCuisines().get(0).getName());

}
else if(r.getDish() != null && r.getDish().getCuisines().isEmpty()){
cuisineDto.setId("No cuisine Id from wikidata");
cuisineDto.setName("No cuisine name from wikidata");
}
// Conversion from Recipe entity to RecipeDetailsDto
return new RecipeDetailsDto(
r.getId(),
r.getTitle(),

r.getInstructions(),
r.getIngredients().stream().map(ingredient -> new IngredientsDto( ingredient.getName())).collect(Collectors.toList()),

r.getCookingTime(),
r.getServingSize(),
cuisineDto,

new DishDto(r.getDish().getId(), r.getDish().getName(), r.getDish().getImage()),
r.getAverageRating(),
new AuthorDto(r.getUser().getId(), r.getUser().getFirstName() , r.getUser().getUsername(), r.getUser().getFollowing().size(),r.getUser().getFollowers().size(), r.getUser().getRecipeCount())

);
}
return null;
return recipe.map(this::convertToRecipeDetailsDto).orElse(null);
}

public List<RecipeDetailsDto> getRecipesByType(String type, @Nullable String username) {
if ("explore".equals(type)) {
return recipeRepository.findAll().stream()
.map(this::convertToRecipeDto)
.map(this::convertToRecipeDetailsDto)
.collect(Collectors.toList());
} else if ("following".equals(type) && username != null) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found"));
return user.getFollowing().stream()
.flatMap(followingUser -> followingUser.getRecipes().stream())
.map(this::convertToRecipeDto)
.map(this::convertToRecipeDetailsDto)
.collect(Collectors.toList());
}
return new ArrayList<>(); // Return an empty list if username is null or other conditions are not met
}

private RecipeDetailsDto convertToRecipeDto(Recipe r) {
public RecipeDetailsDto convertToRecipeDetailsDto(Recipe r) {
CuisineDto cuisineDto = new CuisineDto();
if (r.getDish() != null && !r.getDish().getCuisines().isEmpty()) {

Expand All @@ -249,22 +218,25 @@ else if(r.getDish() != null && r.getDish().getCuisines().isEmpty()){
cuisineDto.setId("No cuisine Id from wikidata");
cuisineDto.setName("No cuisine name from wikidata");
}
// Conversion logic here
return new RecipeDetailsDto(
r.getId(),
r.getTitle(),

r.getInstructions(),
r.getIngredients().stream().map(ingredient -> new IngredientsDto( ingredient.getName())).collect(Collectors.toList()),

r.getCookingTime(),
r.getServingSize(),
cuisineDto,
Integer userRating = authenticationService.getUser().map(User::getId)
.flatMap(userId -> ratingRepository.findByRecipeIdAndUserId(r.getId(), userId))
.map(Rating::getRatingValue)
.orElse(null);

new DishDto(r.getDish().getId(), r.getDish().getName(), r.getDish().getImage()),
r.getAverageRating(),
new AuthorDto(r.getUser().getId(), r.getUser().getFirstName() , r.getUser().getUsername(),r.getUser().getFollowing().size(), r.getUser().getFollowers().size(), r.getUser().getRecipeCount())

);
// Conversion logic here
return RecipeDetailsDto.builder()
.id(r.getId())
.name(r.getTitle())
.instructions(r.getInstructions())
.ingredients(r.getIngredients().stream().map(ingredient -> new IngredientsDto( ingredient.getName())).collect(Collectors.toList()))
.cookTime(r.getCookingTime())
.servingSize(r.getServingSize())
.cuisine(cuisineDto)
.dish(new DishDto(r.getDish().getId(), r.getDish().getName(), r.getDish().getImage()))
.avgRating(r.getAverageRating())
.selfRating(userRating)
.author(new AuthorDto(r.getUser().getId(), r.getUser().getFirstName() , r.getUser().getUsername(), r.getUser().getFollowing().size(), r.getUser().getFollowers().size(), r.getUser().getRecipeCount()))
.build();
}
}
Loading

0 comments on commit bf91f52

Please sign in to comment.