mirror of
https://gitlab.com/etc404/software-engineering-project.git
synced 2026-05-10 20:52:58 +00:00
Update 12 files
- /demo/src/main/java/com/example/demo/entity/User.java - /demo/src/main/java/com/example/demo/entity/Recipe.java - /demo/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java - /demo/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java - /demo/src/main/java/com/example/demo/service/CustomUserDetailsService.java - /demo/src/main/java/com/example/demo/service/UserService.java - /demo/src/main/java/com/example/demo/service/RecipeService.java - /demo/src/main/java/com/example/demo/repository/RecipeRepo.java - /demo/src/main/java/com/example/demo/config/SecurityConfig.java - /demo/src/main/java/com/example/demo/controller/RecipeController.java - /demo/src/main/java/com/example/demo/controller/UserController.java - /demo/src/main/java/com/example/demo/controller/AdminController.java
This commit is contained in:
@@ -9,24 +9,29 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/login", "/register", "/css/**", "/images/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
.defaultSuccessUrl("/", true)
|
||||
.permitAll()
|
||||
)
|
||||
.logout(logout -> logout.permitAll());
|
||||
return http.build();
|
||||
}
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/login", "/register", "/css/**", "/images/**").permitAll()
|
||||
.requestMatchers("/api/users").permitAll()
|
||||
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
.defaultSuccessUrl("/", true)
|
||||
.permitAll()
|
||||
)
|
||||
.logout(logout -> logout.permitAll());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.example.demo.dto.UserDto;
|
||||
import com.example.demo.service.UserService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/admin")
|
||||
public class AdminController {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public AdminController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@PostMapping("/users/{id}/ban")
|
||||
public ResponseEntity<UserDto> banUser(@PathVariable Integer id) {
|
||||
return new ResponseEntity<>(userService.banUser(id), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/users/{id}/unban")
|
||||
public ResponseEntity<UserDto> unbanUser(@PathVariable Integer id) {
|
||||
return new ResponseEntity<>(userService.unbanUser(id), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/users/{id}/make-admin")
|
||||
public ResponseEntity<UserDto> makeAdmin(@PathVariable Integer id) {
|
||||
return new ResponseEntity<>(userService.makeAdmin(id), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/users/{id}/make-user")
|
||||
public ResponseEntity<UserDto> makeUser(@PathVariable Integer id) {
|
||||
return new ResponseEntity<>(userService.makeUser(id), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import com.example.demo.dto.RecipeDto;
|
||||
import com.example.demo.dto.UserDto;
|
||||
@@ -35,9 +36,9 @@ public class RecipeController {
|
||||
|
||||
// build create recipe REST API
|
||||
@PostMapping
|
||||
public ResponseEntity<RecipeDto> saveRecipe(@Valid @RequestBody Recipe recipe) {
|
||||
RecipeDto recipeDto = recipeService.convertToDto(recipe);
|
||||
return new ResponseEntity<RecipeDto>(recipeService.saveRecipe(recipeDto), HttpStatus.CREATED);
|
||||
public ResponseEntity<RecipeDto> saveRecipe(@RequestBody RecipeDto recipeDto, Authentication authentication) {
|
||||
String currentUsername = authentication.getName();
|
||||
return new ResponseEntity<>(recipeService.saveRecipe(recipeDto, currentUsername), HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
// build get all recipes REST API
|
||||
@@ -69,17 +70,21 @@ public class RecipeController {
|
||||
// build update recipe REST API
|
||||
// http://localhost:8080/api/recipes/(id number goes here)
|
||||
@PutMapping("{id}")
|
||||
public ResponseEntity<RecipeDto> updateRecipe(@PathVariable("id") Integer recipeId, @RequestBody Recipe recipe) {
|
||||
RecipeDto recipeDto = recipeService.convertToDto(recipe);
|
||||
return new ResponseEntity<RecipeDto>(recipeService.updateRecipe(recipeDto, recipeId), HttpStatus.OK);
|
||||
public ResponseEntity<RecipeDto> updateRecipe(
|
||||
@PathVariable("id") Integer recipeId,
|
||||
@RequestBody RecipeDto recipeDto,
|
||||
Authentication authentication) {
|
||||
String currentUsername = authentication.getName();
|
||||
return new ResponseEntity<>(recipeService.updateRecipe(recipeDto, recipeId, currentUsername), HttpStatus.OK);
|
||||
}
|
||||
|
||||
// build delete recipe REST API
|
||||
// http://localhost:8080/api/recipes/(id number goes here)
|
||||
@DeleteMapping("{id}")
|
||||
public ResponseEntity<String> deleteRecipe(@PathVariable("id") Integer recipeId) {
|
||||
recipeService.deleteRecipe(recipeId);
|
||||
return new ResponseEntity<String>("Recipe deleted succesfully!", HttpStatus.OK);
|
||||
public ResponseEntity<String> deleteRecipe(@PathVariable("id") Integer recipeId, Authentication authentication) {
|
||||
String currentUsername = authentication.getName();
|
||||
recipeService.deleteRecipe(recipeId, currentUsername);
|
||||
return new ResponseEntity<>("Recipe deleted successfully!", HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -15,26 +13,20 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.example.demo.dto.UserDto;
|
||||
import com.example.demo.entity.User;
|
||||
import com.example.demo.service.UserService;
|
||||
import com.example.demo.repository.UserRepo;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/users")
|
||||
public class UserController {
|
||||
|
||||
private UserService userService;
|
||||
private UserRepo userRepo;
|
||||
|
||||
public UserController(UserService userService, UserRepo userRepo) {
|
||||
public UserController(UserService userService) {
|
||||
super();
|
||||
this.userService = userService;
|
||||
this.userRepo = userRepo;
|
||||
}
|
||||
|
||||
// build create user REST API
|
||||
@@ -52,20 +44,10 @@ public class UserController {
|
||||
|
||||
// build get user by name REST API
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<List<UserDto>> getUsersByName(@RequestParam String string) {
|
||||
List<UserDto> users = userService.getUsersByName(string);
|
||||
public ResponseEntity<List<UserDto>> getUsersByName(@RequestParam String name) {
|
||||
List<UserDto> users = userService.getUsersByName(name);
|
||||
return new ResponseEntity<>(users, HttpStatus.OK);
|
||||
}
|
||||
|
||||
// build get current user REST API
|
||||
@GetMapping("/me")
|
||||
public UserDto getLoggedInUser(Principal principal) {
|
||||
if (principal == null) return null;
|
||||
String username = principal.getName();
|
||||
User user = (userRepo.findByUsername(username))
|
||||
.orElse(null);
|
||||
return userService.convertToDto(user);
|
||||
}
|
||||
|
||||
|
||||
// build get user by id REST API
|
||||
|
||||
@@ -5,7 +5,6 @@ import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -20,21 +19,18 @@ public class Recipe {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
|
||||
@NotBlank(message = "Please provide a recipe title")
|
||||
@Size(max = 128, message = "Title cannot be longer than 128 characters")
|
||||
private String title;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
@Size(max = 500, message = "Description cannot be longer than 500 characters")
|
||||
private String description;
|
||||
|
||||
@NotNull(message = "Please Provide a prep time amount in minutes")
|
||||
@NotNull(message = "Please Provide a prep time amount in mintutes")
|
||||
@Positive(message = "This value cannot be negative")
|
||||
private Integer prepTimeMinutes;
|
||||
|
||||
@NotNull(message = "Please Provide a cook time amount in minutes")
|
||||
@NotNull(message = "Please Provide a cook time amount in mintutes")
|
||||
@Positive(message = "This value cannot be negative")
|
||||
private Integer cookTimeMinutes;
|
||||
|
||||
@@ -55,14 +51,11 @@ public class Recipe {
|
||||
|
||||
// Recipe ingredients relationship
|
||||
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@NotEmpty(message = "At least one ingredient is required")
|
||||
@Size(max = 256, message = "Cannot have more than 256 ingredients")
|
||||
@NotEmpty(message = "At least one ingredient is required")
|
||||
private Set<RecipeIngredient> recipeIngredients = new HashSet<>();
|
||||
|
||||
// Recipe Steps relationship
|
||||
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@NotEmpty(message = "At least one step is required")
|
||||
@Size(max = 50, message = "Cannot have more than 50 steps")
|
||||
private Set<Step> steps = new HashSet<>();
|
||||
|
||||
// Recipe Images relationship
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
package com.example.demo.entity;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@@ -31,17 +29,15 @@ public class User implements UserDetails {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@NotNull
|
||||
private Integer id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
@NotBlank(message = "Username is required")
|
||||
private String username;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String role;
|
||||
|
||||
@Column(unique = true)
|
||||
@NotBlank(message = "Email is required")
|
||||
private String email;
|
||||
|
||||
private String hashedpassword;
|
||||
@@ -49,20 +45,23 @@ public class User implements UserDetails {
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
// User Recipe relationship
|
||||
@Column(nullable = false)
|
||||
private boolean banned = false;
|
||||
|
||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private Set<Recipe> recipes = new HashSet<>();
|
||||
|
||||
// Favorite relationship and also junction table
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JsonIgnore
|
||||
@JoinTable(name = "favorites", joinColumns = { @JoinColumn(name = "userId") }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "recipeId") })
|
||||
@JoinTable(
|
||||
name = "favorites",
|
||||
joinColumns = { @JoinColumn(name = "userId") },
|
||||
inverseJoinColumns = { @JoinColumn(name = "recipeId") }
|
||||
)
|
||||
private Set<Recipe> FavRecipes = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return new ArrayList<>();
|
||||
return List.of(new SimpleGrantedAuthority(role));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,6 +74,26 @@ public class User implements UserDetails {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return !banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return !banned;
|
||||
}
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
@@ -84,6 +103,7 @@ public class User implements UserDetails {
|
||||
this.email = email;
|
||||
this.hashedpassword = hashedpassword;
|
||||
this.createdAt = createdAt;
|
||||
this.banned = false;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
@@ -130,12 +150,27 @@ public class User implements UserDetails {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public boolean isBanned() {
|
||||
return banned;
|
||||
}
|
||||
|
||||
public void setBanned(boolean banned) {
|
||||
this.banned = banned;
|
||||
}
|
||||
|
||||
public Set<Recipe> getRecipes() {
|
||||
return recipes;
|
||||
}
|
||||
|
||||
public void setRecipes(Set<Recipe> recipes) {
|
||||
this.recipes = recipes;
|
||||
}
|
||||
|
||||
public Set<Recipe> getFavRecipes() {
|
||||
return FavRecipes;
|
||||
}
|
||||
|
||||
public void setFavRecipes(Set<Recipe> favRecipes) {
|
||||
FavRecipes = favRecipes;
|
||||
this.FavRecipes = favRecipes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.example.demo.repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@@ -9,5 +10,9 @@ public interface RecipeRepo extends JpaRepository<Recipe, Integer> {
|
||||
|
||||
List<Recipe> findByTitleContainingIgnoreCase(String name);
|
||||
List<Recipe> findByTitleContainingIgnoreCaseAndTags_NameIn(String title, List<String> tags);
|
||||
|
||||
long countByUserIdAndCreatedAtAfter(Integer userId, LocalDateTime after);
|
||||
|
||||
List<Recipe> findByUserId(Integer userId);
|
||||
|
||||
}
|
||||
@@ -17,7 +17,8 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(@NonNull String username) throws UsernameNotFoundException {
|
||||
return userRepo.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
}
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
return userRepo.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.example.demo.service.Impl;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.example.demo.dto.RecipeDto;
|
||||
@@ -55,6 +57,7 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
this.tagRepository = tagRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeDto convertToDto(Recipe recipe) {
|
||||
List<RecipeIngredientDto> ingredientDtos = recipe.getRecipeIngredients().stream()
|
||||
.map(ri -> new RecipeIngredientDto(ri.getIngredient().getName(), ri.getQuantity(), ri.getUnit(),
|
||||
@@ -71,34 +74,79 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
UserDto userDto = new UserDto(recipe.getUser().getId(), recipe.getUser().getUsername(),
|
||||
recipe.getUser().getEmail());
|
||||
|
||||
return new RecipeDto(recipe.getTitle(), recipe.getDescription(), recipe.getPrepTimeMinutes(),
|
||||
RecipeDto dto = new RecipeDto(recipe.getTitle(), recipe.getDescription(), recipe.getPrepTimeMinutes(),
|
||||
recipe.getCookTimeMinutes(), recipe.getServings(), userDto, recipe.getStatus(), ingredientDtos,
|
||||
stepDtos, imageDtos, tagDtos);
|
||||
|
||||
dto.setId(recipe.getId());
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
private User getCurrentUser(String currentUsername) {
|
||||
return userRepository.findByUsername(currentUsername)
|
||||
.orElseThrow(() -> new NotFoundException("User", "username", currentUsername));
|
||||
}
|
||||
|
||||
private boolean isAdmin(User user) {
|
||||
return "ROLE_ADMIN".equals(user.getRole());
|
||||
}
|
||||
|
||||
private void ensureUserNotBanned(User user) {
|
||||
if (user.isBanned()) {
|
||||
throw new AccessDeniedException("Banned users cannot perform this action.");
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceUploadLimit(User user) {
|
||||
if (isAdmin(user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDateTime cutoff = LocalDateTime.now().minusHours(24);
|
||||
long uploadsInLast24Hours = recipeRepository.countByUserIdAndCreatedAtAfter(user.getId(), cutoff);
|
||||
|
||||
if (uploadsInLast24Hours >= 10) {
|
||||
throw new AccessDeniedException("Upload limit reached. Maximum is 10 recipes per 24 hours.");
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceOwnerOrAdmin(User currentUser, Recipe recipe) {
|
||||
if (isAdmin(currentUser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recipe.getUser().getId().equals(currentUser.getId())) {
|
||||
throw new AccessDeniedException("You do not have permission to modify this recipe.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public RecipeDto saveRecipe(RecipeDto dto) {
|
||||
public RecipeDto saveRecipe(RecipeDto dto, String currentUsername) {
|
||||
|
||||
User user = userRepository.findById(dto.getUserDto().getId())
|
||||
.orElseThrow(() -> new NotFoundException("User", "id", dto.getUserDto().getId()));
|
||||
User currentUser = getCurrentUser(currentUsername);
|
||||
ensureUserNotBanned(currentUser);
|
||||
enforceUploadLimit(currentUser);
|
||||
|
||||
Recipe recipe = new Recipe(dto.getTitle(), dto.getDescription(), dto.getPrepTimeMinutes(),
|
||||
dto.getCookTimeMinutes(), dto.getServings(), user, dto.getStatus());
|
||||
dto.getCookTimeMinutes(), dto.getServings(), currentUser, dto.getStatus());
|
||||
|
||||
for (RecipeIngredientDto riDto : dto.getIngredients()) {
|
||||
if (dto.getIngredients() != null) {
|
||||
for (RecipeIngredientDto riDto : dto.getIngredients()) {
|
||||
|
||||
Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName())
|
||||
.orElseGet(() -> new Ingredient(riDto.getIngredientName()));
|
||||
Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName())
|
||||
.orElseGet(() -> new Ingredient(riDto.getIngredientName()));
|
||||
|
||||
if (ingredient.getId() == null) {
|
||||
ingredientRepository.save(ingredient);
|
||||
if (ingredient.getId() == null) {
|
||||
ingredientRepository.save(ingredient);
|
||||
}
|
||||
|
||||
RecipeIngredient ri = new RecipeIngredient(recipe, ingredient, riDto.getQuantity(), riDto.getUnit(),
|
||||
riDto.getNotes());
|
||||
|
||||
recipe.getRecipeIngredients().add(ri);
|
||||
}
|
||||
|
||||
RecipeIngredient ri = new RecipeIngredient(recipe, ingredient, riDto.getQuantity(), riDto.getUnit(),
|
||||
riDto.getNotes());
|
||||
|
||||
recipe.getRecipeIngredients().add(ri);
|
||||
}
|
||||
|
||||
if (dto.getSteps() != null) {
|
||||
@@ -115,20 +163,21 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
}
|
||||
}
|
||||
|
||||
for (TagDto tDto : dto.getTags()) {
|
||||
if (dto.getTags() != null) {
|
||||
for (TagDto tDto : dto.getTags()) {
|
||||
|
||||
Tag tag = tagRepository.findByName(tDto.getName()).orElseGet(() -> new Tag(tDto.getName()));
|
||||
Tag tag = tagRepository.findByName(tDto.getName()).orElseGet(() -> new Tag(tDto.getName()));
|
||||
|
||||
if (tag.getId() == null) {
|
||||
tagRepository.save(tag);
|
||||
if (tag.getId() == null) {
|
||||
tagRepository.save(tag);
|
||||
}
|
||||
recipe.getTags().add(tag);
|
||||
}
|
||||
recipe.getTags().add(tag);
|
||||
}
|
||||
|
||||
Recipe saved = recipeRepository.save(recipe);
|
||||
|
||||
return getRecipeById(saved.getId());
|
||||
//return convertToDto(saved);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,10 +201,15 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public RecipeDto updateRecipe(RecipeDto recipeDto, Integer id) {
|
||||
public RecipeDto updateRecipe(RecipeDto recipeDto, Integer id, String currentUsername) {
|
||||
User currentUser = getCurrentUser(currentUsername);
|
||||
ensureUserNotBanned(currentUser);
|
||||
|
||||
Recipe existingRecipe = recipeRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("Recipe", "id", id));
|
||||
|
||||
enforceOwnerOrAdmin(currentUser, existingRecipe);
|
||||
|
||||
existingRecipe.setTitle(recipeDto.getTitle());
|
||||
existingRecipe.setDescription(recipeDto.getDescription());
|
||||
existingRecipe.setPrepTimeMinutes(recipeDto.getPrepTimeMinutes());
|
||||
@@ -197,13 +251,10 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
|
||||
for (RecipeIngredientDto riDto : updatedIngredients) {
|
||||
|
||||
// go through the old list of ingredients until we find a match with updated
|
||||
// list
|
||||
RecipeIngredient existingRI = existingRecipe.getRecipeIngredients().stream()
|
||||
.filter(ri -> ri.getIngredient().getName().equals(riDto.getIngredientName())).findFirst()
|
||||
.orElse(null);
|
||||
|
||||
// if old ingredient just update parameters
|
||||
if (existingRI != null) {
|
||||
|
||||
existingRI.setQuantity(riDto.getQuantity());
|
||||
@@ -211,7 +262,6 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
existingRI.setNotes(riDto.getNotes());
|
||||
}
|
||||
|
||||
// if new ingredient, have to make a whole new thing
|
||||
else {
|
||||
|
||||
Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName())
|
||||
@@ -229,7 +279,6 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
}
|
||||
|
||||
if (updatedSteps != null) {
|
||||
// find steps that weren't included
|
||||
for (Step step : existingRecipe.getSteps()) {
|
||||
boolean existsInUpdatedList = updatedSteps.stream()
|
||||
.anyMatch(dto -> dto.getStepNumber().equals(step.getStepNumber()));
|
||||
@@ -237,22 +286,17 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
if (!existsInUpdatedList)
|
||||
stepsToRemove.add(step);
|
||||
}
|
||||
// delete those steps
|
||||
existingRecipe.getSteps().removeAll(stepsToRemove);
|
||||
|
||||
// go through updated steps
|
||||
for (StepDto stepDto : updatedSteps) {
|
||||
|
||||
// find matching step by step number
|
||||
Step existingStep = existingRecipe.getSteps().stream()
|
||||
.filter(s -> s.getStepNumber().equals(stepDto.getStepNumber())).findFirst().orElse(null);
|
||||
|
||||
// if there's a match update the instruction string
|
||||
if (existingStep != null) {
|
||||
existingStep.setInstruction(stepDto.getInstruction());
|
||||
}
|
||||
|
||||
// if no match then make a whole new step
|
||||
else {
|
||||
Step newStep = new Step(existingRecipe, stepDto.getStepNumber(), stepDto.getInstruction());
|
||||
existingRecipe.getSteps().add(newStep);
|
||||
@@ -260,7 +304,6 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
}
|
||||
}
|
||||
|
||||
// same process as above just with images instead
|
||||
if (updatedImages != null) {
|
||||
for (Image image : existingRecipe.getImages()) {
|
||||
boolean existsInUpdatedList = updatedImages.stream()
|
||||
@@ -284,8 +327,6 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
}
|
||||
}
|
||||
|
||||
// same process as above just with tags instead, except for saving the tag
|
||||
// since the relationship for this one was slightly different
|
||||
if (updatedTags != null) {
|
||||
for (Tag tag : existingRecipe.getTags()) {
|
||||
boolean existsInUpdatedList = updatedTags.stream().anyMatch(dto -> dto.getName().equals(tag.getName()));
|
||||
@@ -314,39 +355,45 @@ public class RecipeServiceImpl implements RecipeService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRecipe(Integer Id) {
|
||||
recipeRepository.findById(Id).orElseThrow(() -> new NotFoundException("Recipe", "id", Id));
|
||||
recipeRepository.deleteById(Id);
|
||||
@Transactional
|
||||
public void deleteRecipe(Integer id, String currentUsername) {
|
||||
User currentUser = getCurrentUser(currentUsername);
|
||||
ensureUserNotBanned(currentUser);
|
||||
|
||||
Recipe recipe = recipeRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("Recipe", "id", id));
|
||||
|
||||
enforceOwnerOrAdmin(currentUser, recipe);
|
||||
recipeRepository.delete(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
@Transactional
|
||||
public List<RecipeDto> getRecipes(String name, List<String> tags) {
|
||||
|
||||
List<Recipe> recipes;
|
||||
|
||||
if(!name.isBlank()) {
|
||||
recipes = recipeRepository.findByTitleContainingIgnoreCase(name);
|
||||
}
|
||||
|
||||
else {
|
||||
recipes = recipeRepository.findAll();
|
||||
}
|
||||
|
||||
if(!tags.isEmpty() && !recipes.isEmpty()) {
|
||||
recipes = recipes.stream()
|
||||
.filter(recipe -> recipe.getTags().stream().anyMatch(tag -> tags.contains(tag.getName())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
List<RecipeDto> recipeList = new ArrayList<>();
|
||||
|
||||
for (Recipe recipe : recipes) {
|
||||
RecipeDto dto = convertToDto(recipe);
|
||||
recipeList.add(dto);
|
||||
}
|
||||
|
||||
return recipeList;
|
||||
List<Recipe> recipes;
|
||||
|
||||
if (!name.isBlank()) {
|
||||
recipes = recipeRepository.findByTitleContainingIgnoreCase(name);
|
||||
}
|
||||
|
||||
else {
|
||||
recipes = recipeRepository.findAll();
|
||||
}
|
||||
|
||||
if (!tags.isEmpty() && !recipes.isEmpty()) {
|
||||
recipes = recipes.stream()
|
||||
.filter(recipe -> recipe.getTags().stream().anyMatch(tag -> tags.contains(tag.getName())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
List<RecipeDto> recipeList = new ArrayList<>();
|
||||
|
||||
for (Recipe recipe : recipes) {
|
||||
RecipeDto dto = convertToDto(recipe);
|
||||
recipeList.add(dto);
|
||||
}
|
||||
|
||||
return recipeList;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.demo.service.Impl;
|
||||
ppackage com.example.demo.service.Impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -39,6 +39,10 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
public User saveUser(User user) {
|
||||
if (user.getRole() == null || user.getRole().isBlank()) {
|
||||
user.setRole("ROLE_USER");
|
||||
}
|
||||
user.setBanned(false);
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
@@ -125,4 +129,39 @@ public class UserServiceImpl implements UserService {
|
||||
return userList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDto banUser(Integer id) {
|
||||
User user = userRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("User", "id", id));
|
||||
user.setBanned(true);
|
||||
userRepository.save(user);
|
||||
return convertToDto(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDto unbanUser(Integer id) {
|
||||
User user = userRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("User", "id", id));
|
||||
user.setBanned(false);
|
||||
userRepository.save(user);
|
||||
return convertToDto(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDto makeAdmin(Integer id) {
|
||||
User user = userRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("User", "id", id));
|
||||
user.setRole("ROLE_ADMIN");
|
||||
userRepository.save(user);
|
||||
return convertToDto(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDto makeUser(Integer id) {
|
||||
User user = userRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("User", "id", id));
|
||||
user.setRole("ROLE_USER");
|
||||
userRepository.save(user);
|
||||
return convertToDto(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.example.demo.entity.User;
|
||||
public interface RecipeService {
|
||||
RecipeDto convertToDto(Recipe recipe);
|
||||
|
||||
RecipeDto saveRecipe(RecipeDto recipe);
|
||||
RecipeDto saveRecipe(RecipeDto recipe, String currentUsername);
|
||||
|
||||
List<RecipeDto> getAllRecipes();
|
||||
|
||||
@@ -19,8 +19,9 @@ public interface RecipeService {
|
||||
|
||||
List<RecipeDto> getRecipes(String name, List<String> tags);
|
||||
|
||||
RecipeDto updateRecipe(RecipeDto recipedto, Integer Id);
|
||||
RecipeDto updateRecipe(RecipeDto recipedto, Integer Id, String currentUsername);
|
||||
|
||||
void deleteRecipe(Integer Id);
|
||||
void deleteRecipe(Integer Id, String currentUsername);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -12,15 +12,23 @@ public interface UserService {
|
||||
|
||||
List<UserDto> getAllUsers();
|
||||
|
||||
UserDto getUserById(Integer Id);
|
||||
UserDto getUserById(Integer id);
|
||||
|
||||
List<UserDto> getUsersByName(String name);
|
||||
|
||||
UserDto saveFavorite(Integer userId, Integer recipeId);
|
||||
|
||||
UserDto updateUser(User user, Integer Id);
|
||||
UserDto updateUser(User user, Integer id);
|
||||
|
||||
void deleteUser(Integer Id);
|
||||
void deleteUser(Integer id);
|
||||
|
||||
void deleteFavorite(Integer userId, Integer recipeId);
|
||||
|
||||
UserDto banUser(Integer id);
|
||||
|
||||
UserDto unbanUser(Integer id);
|
||||
|
||||
UserDto makeAdmin(Integer id);
|
||||
|
||||
UserDto makeUser(Integer id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user