diff --git a/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java b/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java index 653b2358..7ee00e81 100644 --- a/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java +++ b/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java @@ -45,6 +45,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) .hasRole("ADMIN") // Require ADMIN role for "/api/v1/resources" .requestMatchers(HttpMethod.POST,"/**") .authenticated() + .requestMatchers("/feed?type=following").authenticated() .requestMatchers(HttpMethod.DELETE,"/**").authenticated()) // Require authentication for all other requests .sessionManagement( manager -> diff --git a/backend/src/main/java/com/group1/cuisines/controllers/FeedController.java b/backend/src/main/java/com/group1/cuisines/controllers/FeedController.java new file mode 100644 index 00000000..ff3fd957 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/controllers/FeedController.java @@ -0,0 +1,45 @@ +package com.group1.cuisines.controllers; + +import com.group1.cuisines.dao.response.SuccessResponse; +import com.group1.cuisines.dto.RecipeDetailsDto; +import com.group1.cuisines.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; + +@RestController +@RequestMapping("/api/v1") +public class FeedController { + + @Autowired + private RecipeService recipeService; + + @GetMapping("/feed") + public ResponseEntity getFeed(@RequestParam String type, Authentication authentication) { + if (!"explore".equals(type) && !"following".equals(type)) { + return ResponseEntity.badRequest().body("Invalid type parameter."); + } + + if ("following".equals(type)) { + if (authentication == null || !authentication.isAuthenticated()) { + // Return an empty set and a message for unauthenticated users + return ResponseEntity.ok(new SuccessResponse<>(Collections.emptyList(), "No content available. Please log in and follow other users !.")); + } + // Fetch following users' recipes for authenticated users + String username = authentication.getName(); + List recipes = recipeService.getRecipesByType(type, username); + return ResponseEntity.ok(new SuccessResponse<>(recipes, "Recipes fetched successfully from followed users.")); + } + + // For 'explore', accessible to everyone + List recipes = recipeService.getRecipesByType(type, null); + return ResponseEntity.ok(new SuccessResponse<>(recipes, "Recipes fetched successfully.")); + } +} diff --git a/backend/src/main/java/com/group1/cuisines/entities/User.java b/backend/src/main/java/com/group1/cuisines/entities/User.java index 5e7d5923..53c31bdf 100644 --- a/backend/src/main/java/com/group1/cuisines/entities/User.java +++ b/backend/src/main/java/com/group1/cuisines/entities/User.java @@ -1,10 +1,7 @@ package com.group1.cuisines.entities; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -18,6 +15,8 @@ @NoArgsConstructor @AllArgsConstructor @Entity +@Getter +@Setter @Table(name = "users") public class User implements UserDetails { @@ -53,6 +52,11 @@ public class User implements UserDetails { @Builder.Default private int recipeCount = 0; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private Set recipes = new HashSet<>(); + + + diff --git a/backend/src/main/java/com/group1/cuisines/services/RecipeService.java b/backend/src/main/java/com/group1/cuisines/services/RecipeService.java index 71ba3672..01d2c3e4 100644 --- a/backend/src/main/java/com/group1/cuisines/services/RecipeService.java +++ b/backend/src/main/java/com/group1/cuisines/services/RecipeService.java @@ -3,14 +3,17 @@ import com.group1.cuisines.dto.*; import com.group1.cuisines.entities.*; import com.group1.cuisines.repositories.*; +import jakarta.annotation.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; @Service @@ -217,4 +220,50 @@ else if(r.getDish() != null && r.getDish().getCuisines().isEmpty()){ return null; } + public List getRecipesByType(String type, @Nullable String username) { + if ("explore".equals(type)) { + return recipeRepository.findAll().stream() + .map(this::convertToRecipeDto) + .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) + .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) { + CuisineDto cuisineDto = new CuisineDto(); + 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 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, + + 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().getFollowers().size(), r.getUser().getRecipeCount()) + + ); + } }