diff --git a/pom.xml b/pom.xml index 33d0052..ba41373 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,11 @@ org.springframework.boot spring-boot-starter-web + + org.zalando + logbook-spring-boot-starter + 3.7.2 + org.springframework.boot spring-boot-starter-validation diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java new file mode 100644 index 0000000..bc6006f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java @@ -0,0 +1,33 @@ +package ru.yandex.practicum.filmorate.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import ru.yandex.practicum.filmorate.exception.ErrorResponse; +import ru.yandex.practicum.filmorate.exception.InternalServerErrorException; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; + +@RestControllerAdvice +public class ErrorHandler { + + @ExceptionHandler + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse handleNotFoundException(final NotFoundException e) { + return new ErrorResponse("Error", e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(final ValidationException e) { + return new ErrorResponse("Error", e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleInternalServerErrorException(final InternalServerErrorException e) { + return new ErrorResponse("Error", e.getMessage()); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java index 9d5b989..b66662b 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -1,95 +1,53 @@ package ru.yandex.practicum.filmorate.controller; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; -import ru.yandex.practicum.filmorate.exception.NotFoundException; -import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.service.FilmService; -import java.time.LocalDate; import java.util.Collection; -import java.util.HashMap; import java.util.Map; +import java.util.Set; @Slf4j @RestController +@RequiredArgsConstructor @RequestMapping("/films") public class FilmController { - private final Map films = new HashMap<>(); + + private final FilmService filmService; @GetMapping public Collection getAll() { - log.debug("GET, all films"); - return films.values(); + return filmService.getAll(); + } + + @GetMapping("/popular") + public Collection getFilmsByLike(@RequestParam(defaultValue = "10") int sizeFilms) { + return filmService.getFilmsByLike(sizeFilms); } @PostMapping public Film create(@Valid @RequestBody Film film) { - log.debug("POST, create film {}", film); - validateReleaseDate(film); - film.setId(getNextId()); - films.put(film.getId(), film); - return film; + return filmService.create(film); } - @PutMapping public Film update(@Valid @RequestBody Film filmUpdate) { - log.debug("PUT, update film {}", filmUpdate); - Integer id = validateUpdate(filmUpdate); - Film oldFilm = films.get(id); - String newName = filmUpdate.getName(); - if (!newName.equals(oldFilm.getName())) oldFilm.setName(newName); - if (filmUpdate.getDescription() != null) { - String newDescription = filmUpdate.getDescription(); - if (!newDescription.equals(oldFilm.getDescription())) oldFilm.setDescription(newDescription); - } - LocalDate newReleaseDate = filmUpdate.getReleaseDate(); - if (!newReleaseDate.equals(oldFilm.getReleaseDate())) oldFilm.setReleaseDate(newReleaseDate); - - if (filmUpdate.getDuration() != null) { - Integer newDuration = filmUpdate.getDuration(); - if (!newDuration.equals(oldFilm.getDuration())) oldFilm.setDuration(newDuration); - } - return oldFilm; + return filmService.update(filmUpdate); } - - private void validateReleaseDate(Film film) { - log.debug("validateReleaseDate start for {}", film); - LocalDate birthdayFilm = LocalDate.of(1895, 12, 28); - if (film.getReleaseDate().isBefore(birthdayFilm)) { - String msg = "дата релиза — не раньше 28 декабря 1895 года"; - log.error(msg); - throw new ValidationException(msg); - } + @PutMapping("/{filmId}/like/{userId}") + public Map> addLike(@PathVariable int filmId, @PathVariable int userId) { + return filmService.addLike(filmId, userId); } - private Integer validateUpdate(Film film) { - log.debug("validateUpdate start for {}", film); - String newName = film.getName(); - if (film.getId() == null) { - String msg = "Id должен быть указан"; - log.error(msg); - throw new ValidationException(msg); - } - Integer id = film.getId(); - if (!films.containsKey(id)) { - String msg = "Фильм с id = " + id + " не найден"; - log.error(msg); - throw new NotFoundException(msg); - } - validateReleaseDate(film); - return id; + @DeleteMapping("/{filmId}/like/{userId}") + public void removeLike(@PathVariable int filmId, @PathVariable int userId) { + filmService.removeLike(filmId, userId); } - private int getNextId() { - int currentMaxId = films.keySet() - .stream() - .mapToInt(id -> id) - .max() - .orElse(0); - return ++currentMaxId; - } + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java index 44b05db..7111058 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -1,100 +1,58 @@ package ru.yandex.practicum.filmorate.controller; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; -import ru.yandex.practicum.filmorate.exception.NotFoundException; -import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.service.UserService; import java.util.Collection; -import java.util.HashMap; import java.util.Map; +import java.util.Set; @Slf4j @RestController @RequestMapping("/users") +@RequiredArgsConstructor public class UserController { - private final Map users = new HashMap<>(); + + private final UserService userService; @GetMapping public Collection getAll() { - log.debug("GET, all users"); - return users.values(); + return userService.getAll(); + } + + @GetMapping("/{userId}/friends") + public Collection getFriendsUser(@PathVariable int userId) { + return userService.getFriendsUser(userId); + } + + @GetMapping("/{userId}/friends/common/{friendsId}") + public Collection getMutualFriends(@PathVariable int userId, @PathVariable int friendsId) { + return userService.getMutualFriends(userId, friendsId); } @PostMapping public User create(@Valid @RequestBody User user) { - log.debug("POST, create user {}", user); - cloneSearchEmail(user); - user.setId(getNextId()); - checkUserName(user); - users.put(user.getId(), user); - return user; + return userService.create(user); } - @PutMapping public User update(@Valid @RequestBody User newUser) { - log.debug("PUT, update User {}", newUser); - Integer id = validateUpdate(newUser); - User oldUser = users.get(id); - String newEmail = newUser.getEmail(); - String newLogin = newUser.getLogin(); - oldUser.setName(newUser.getName()); - if (newUser.getBirthday() != null) oldUser.setBirthday(newUser.getBirthday()); - if (!oldUser.getEmail().equals(newEmail)) oldUser.setEmail(newEmail); - if (!oldUser.getLogin().equals(newLogin)) oldUser.setLogin(newLogin); - return oldUser; - } - - private void cloneSearchEmail(User user) { - log.debug("cloneSearchEmail start for {}", user); - String newEmail = user.getEmail(); - for (User userMap : users.values()) { - if (userMap.getEmail().equals(newEmail)) { - String msg = "Этот Email уже используется"; - log.error(msg); - throw new ValidationException(msg); - } - } + return userService.update(newUser); } - private void checkUserName(User user) { - if (user.getName() == null) { - user.setName(user.getLogin()); - } + @PutMapping("/{userId}/friends/{friendsId}") + public Map> addToFriend(@PathVariable int userId, @PathVariable int friendsId) { + return userService.addToFriend(userId, friendsId); } - private Integer validateUpdate(User newUser) { - log.debug("validateUpdate start for {}", newUser); - checkUserName(newUser); - String newEmail = newUser.getEmail(); - if (newUser.getId() == null) { - String msg = "Id должен быть указан"; - log.error(msg); - throw new ValidationException(msg); - } - Integer id = newUser.getId(); - if (!users.containsKey(id)) { - String msg = "Пользователь с id = " + newUser.getId() + " не найден"; - log.error(msg); - throw new NotFoundException(msg); - } - if (!newEmail.equals(users.get(id).getEmail())) { - cloneSearchEmail(newUser); - } - return id; + @DeleteMapping("/{userId}/friends/{friendsId}") + public void removeFriend(@PathVariable int userId, @PathVariable int friendsId) { + userService.removeFriend(userId, friendsId); } - private int getNextId() { - int currentMaxId = users.keySet() - .stream() - .mapToInt(id -> id) - .max() - .orElse(0); - return ++currentMaxId; - } - } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java new file mode 100644 index 0000000..8ab2e8f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.exception; + +public class ErrorResponse { + private final String error; + private final String description; + + public ErrorResponse(String error, String description) { + this.error = error; + this.description = description; + } + + public String getError() { + return error; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/InternalServerErrorException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/InternalServerErrorException.java new file mode 100644 index 0000000..f368cda --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/InternalServerErrorException.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class InternalServerErrorException extends RuntimeException { + public InternalServerErrorException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index d1a5b3a..478d9e3 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -23,4 +23,6 @@ public class Film { @Positive @NotNull private Integer duration; + private int likes; + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java index f478a22..7622f2d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -12,6 +12,7 @@ @Builder @Data public class User { + private Integer id; @NotNull @NotBlank @@ -24,4 +25,5 @@ public class User { @Past @NotNull private LocalDate birthday; + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java new file mode 100644 index 0000000..2c49ed0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -0,0 +1,42 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.storage.film.InMemoryFilmStorage; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +@RequiredArgsConstructor +@Service +public class FilmService { + + private final InMemoryFilmStorage inMemoryFilmStorage; + + public Collection getAll() { + return inMemoryFilmStorage.getAll(); + } + + public Film create(Film film) { + return inMemoryFilmStorage.create(film); + } + + public Film update(Film filmUpdate) { + return inMemoryFilmStorage.update(filmUpdate); + } + + public Map> addLike(int filmId, int userId) { + return inMemoryFilmStorage.addLike(filmId, userId); + } + + public void removeLike(int filmId, int userId) { + inMemoryFilmStorage.removeLike(filmId, userId); + } + + public Collection getFilmsByLike(Integer sizeFilms) { + return inMemoryFilmStorage.getFilmsByLike(sizeFilms); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java new file mode 100644 index 0000000..b6a5d6c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -0,0 +1,47 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +@RequiredArgsConstructor +@Service +public class UserService { + + private final InMemoryUserStorage inMemoryUserStorage; + + public Collection getAll() { + return inMemoryUserStorage.getAll(); + } + + public User create(User user) { + return inMemoryUserStorage.create(user); + } + + public User update(User newUser) { + return inMemoryUserStorage.update(newUser); + } + + public Map> addToFriend(int userId, int friendsId) { + return inMemoryUserStorage.addToFriend(userId, friendsId); + } + + public void removeFriend(int userId, int friendsId) { + inMemoryUserStorage.removeFriend(userId, friendsId); + } + + public Collection getFriendsUser(int userId) { + return inMemoryUserStorage.getFriendsUser(userId); + } + + public Collection getMutualFriends(int userId, int friendsId) { + return inMemoryUserStorage.getMutualFriends(userId, friendsId); + } + + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java new file mode 100644 index 0000000..9bca9ca --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java @@ -0,0 +1,25 @@ +package ru.yandex.practicum.filmorate.storage.film; + +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public interface FilmStorage { + + Collection getAll(); + + Film getFilmById(int id); + + Film create(Film film); + + Film update(Film filmUpdate); + + Map> addLike(int filmId, int userId); + + void removeLike(Integer filmId, Integer userId); + + Collection getFilmsByLike(Integer sizeFilms); + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java new file mode 100644 index 0000000..d02895f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java @@ -0,0 +1,181 @@ +package ru.yandex.practicum.filmorate.storage.film; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Component +public class InMemoryFilmStorage implements FilmStorage { + + private final InMemoryUserStorage inMemoryUserStorage; + + private final Map films = new HashMap<>(); + private final Map> filmsLikes = new HashMap<>(); + private final LocalDate birthdayFilm = LocalDate.of(1895, 12, 28); + + private int id = 1; + + public int getNextId() { + return id++; + } + + + @Override + public Collection getAll() { + log.info("GET, all films"); + return films.values(); + } + + @Override + public Film getFilmById(int id) { + validateFilmId(id); + return films.get(id); + } + + @Override + public Film create(Film film) { + log.info("POST, create film {}", film); + validateReleaseDate(film); + film.setId(getNextId()); + films.put(film.getId(), film); + return film; + } + + @Override + public Film update(Film filmUpdate) { + log.info("PUT, update film {}", filmUpdate); + Integer id = validateUpdate(filmUpdate); + Film oldFilm = films.get(id); + String newName = filmUpdate.getName(); + if (!newName.equals(oldFilm.getName())) { + oldFilm.setName(newName); + } + if (filmUpdate.getDescription() != null) { + String newDescription = filmUpdate.getDescription(); + if (!newDescription.equals(oldFilm.getDescription())) { + oldFilm.setDescription(newDescription); + } + } + LocalDate newReleaseDate = filmUpdate.getReleaseDate(); + if (!newReleaseDate.equals(oldFilm.getReleaseDate())) { + oldFilm.setReleaseDate(newReleaseDate); + } + + if (filmUpdate.getDuration() != null) { + Integer newDuration = filmUpdate.getDuration(); + if (!newDuration.equals(oldFilm.getDuration())) { + oldFilm.setDuration(newDuration); + } + } + return oldFilm; + } + + @Override + public Map> addLike(int filmId, int userId) { + inMemoryUserStorage.validateUserId(userId); + validateFilmId(filmId); + + if (checkFilmsLikes(filmId)) { + Set likes = filmsLikes.get(filmId); + + if (likes.add(userId)) { + addLikeFilm(filmId); + } + } else { + addLikeFilm(filmId); + + Set likes = new HashSet<>(); + likes.add(userId); + filmsLikes.put(filmId, likes); + } + + return Map.of(films.get(filmId), filmsLikes.get(filmId)); + } + + @Override + public void removeLike(Integer filmId, Integer userId) { + inMemoryUserStorage.validateUserId(userId); + validateFilmId(filmId); + + if (checkFilmsLikes(filmId)) { + Set likes = filmsLikes.get(filmId); + likes.remove(userId); + removeLikeFilm(filmId); + if (likes.isEmpty()) { + filmsLikes.remove(filmId); + } + } + } + + @Override + public Collection getFilmsByLike(Integer sizeFilms) { + return films.values() + .stream() + .sorted(Comparator.comparing(Film::getLikes).reversed()) + .limit(sizeFilms) + .collect(Collectors.toList()); + } + + private void validateFilmId(Integer id) { + if (!films.containsKey(id)) { + throw new NotFoundException("Фильм с id: " + id + " не найден"); + } + } + + + private boolean checkFilmsLikes(int id) { + return filmsLikes.containsKey(id); + } + + private void addLikeFilm(int filmId) { + Film film = films.get(filmId); + film.setLikes(film.getLikes() + 1); + } + + private void removeLikeFilm(int filmId) { + Film film = films.get(filmId); + int like = film.getLikes(); + + if (like != 0) { + film.setLikes(like - 1); + } + } + + private void validateReleaseDate(Film film) { + log.info("validateReleaseDate start for {}", film); + if (film.getReleaseDate().isBefore(birthdayFilm)) { + String msg = "дата релиза — не раньше 28 декабря 1895 года"; + log.error(msg); + throw new ValidationException(msg); + } + + } + + private Integer validateUpdate(Film film) { + log.info("validateUpdate start for {}", film); + if (film.getId() == null) { + String msg = "Id должен быть указан"; + log.error(msg); + throw new ValidationException(msg); + } + Integer id = film.getId(); + if (!films.containsKey(id)) { + String msg = "Фильм с id = " + id + " не найден"; + log.error(msg); + throw new NotFoundException(msg); + } + validateReleaseDate(film); + return id; + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java new file mode 100644 index 0000000..a91fc77 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java @@ -0,0 +1,195 @@ +package ru.yandex.practicum.filmorate.storage.user; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.*; + +@Slf4j +@RequiredArgsConstructor +@Component +public class InMemoryUserStorage implements UserStorage { + + private final Map users = new HashMap<>(); + private final Map> userFriends = new HashMap<>(); + + private int id = 1; + + public int getNextId() { + return id++; + } + + @Override + public Collection getAll() { + log.info("GET, all users"); + return users.values(); + } + + @Override + public User getUserById(int id) { + validateUserId(id); + return users.get(id); + } + + + @Override + public User create(User user) { + log.info("POST, create user {}", user); + cloneSearchEmail(user); + user.setId(getNextId()); + checkOrAddUserName(user); + users.put(user.getId(), user); + return user; + } + + @Override + public User update(User newUser) { + log.info("PUT, update User {}", newUser); + Integer id = validateUpdate(newUser); + User oldUser = users.get(id); + String newEmail = newUser.getEmail(); + String newLogin = newUser.getLogin(); + oldUser.setName(newUser.getName()); + + if (newUser.getBirthday() != null) { + oldUser.setBirthday(newUser.getBirthday()); + } + if (!oldUser.getEmail().equals(newEmail)) { + oldUser.setEmail(newEmail); + } + if (!oldUser.getLogin().equals(newLogin)) { + oldUser.setLogin(newLogin); + } + return oldUser; + } + + @Override + public Map> addToFriend(int userId, int friendsId) { + validateUserId(userId); + validateUserId(friendsId); + + if (checkUserFriends(userId)) { + userFriends.get(userId).add(friendsId); + } else { + Set friendsIds = new HashSet<>(); + friendsIds.add(friendsId); + userFriends.put(userId, friendsIds); + } + + if (checkUserFriends(friendsId)) { + userFriends.get(friendsId).add(userId); + } else { + Set friendsIds = new HashSet<>(); + friendsIds.add(userId); + userFriends.put(friendsId, friendsIds); + } + return Map.of(users.get(userId), userFriends.get(userId), + users.get(friendsId), userFriends.get(friendsId)); + } + + @Override + public void removeFriend(int userId, int friendsId) { + validateUserId(userId); + validateUserId(friendsId); + + if (checkUserFriends(userId)) { + Set friendsIds1 = userFriends.get(userId); + friendsIds1.remove(friendsId); + if (friendsIds1.isEmpty()) { + userFriends.remove(userId); + } + + Set friendsIds2 = userFriends.get(friendsId); + friendsIds2.remove(userId); + if (friendsIds2.isEmpty()) { + userFriends.remove(friendsId); + } + } + } + + @Override + public Collection getFriendsUser(int userId) { + validateUserId(userId); + Set friendsIds = userFriends.get(userId); + List friends = new ArrayList<>(); + if (!checkUserFriends(userId)) { + return friends; + } + + for (int id : friendsIds) { + friends.add(users.get(id)); + } + return friends; + } + + @Override + public Collection getMutualFriends(int userId, int friendsId) { + validateUserId(userId); + validateUserId(friendsId); + + List mutualId = new ArrayList<>(userFriends.get(userId)); + mutualId.retainAll(userFriends.get(friendsId)); + + List mutualFriends = new ArrayList<>(); + for (Integer id : mutualId) { + mutualFriends.add(users.get(id)); + } + return mutualFriends; + } + + @Override + public void validateUserId(int id) { + if (!users.containsKey(id)) { + throw new NotFoundException("Пользователь с id: " + id + " не найден"); + } + } + + private boolean checkUserFriends(int id) { + return userFriends.containsKey(id); + } + + + private void cloneSearchEmail(User user) { + log.info("cloneSearchEmail start for {}", user); + String newEmail = user.getEmail(); + for (User userMap : users.values()) { + if (userMap.getEmail().equals(newEmail)) { + String msg = "Этот Email уже используется"; + log.error(msg); + throw new ValidationException(msg); + } + } + } + + + private void checkOrAddUserName(User user) { + if (user.getName() == null) { + user.setName(user.getLogin()); + } + } + + private Integer validateUpdate(User newUser) { + log.info("validateUpdate start for {}", newUser); + checkOrAddUserName(newUser); + String newEmail = newUser.getEmail(); + if (newUser.getId() == null) { + String msg = "Id должен быть указан"; + log.error(msg); + throw new ValidationException(msg); + } + Integer id = newUser.getId(); + if (!users.containsKey(id)) { + String msg = "Пользователь с id = " + newUser.getId() + " не найден"; + log.error(msg); + throw new NotFoundException(msg); + } + if (!newEmail.equals(users.get(id).getEmail())) { + cloneSearchEmail(newUser); + } + return id; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java new file mode 100644 index 0000000..cc4c3e9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java @@ -0,0 +1,28 @@ +package ru.yandex.practicum.filmorate.storage.user; + +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public interface UserStorage { + + Collection getAll(); + + User getUserById(int id); + + User create(User user); + + User update(User newUser); + + Map> addToFriend(int userId, int friendsId); + + void removeFriend(int userId, int friendsId); + + Collection getFriendsUser(int userId); + + Collection getMutualFriends(int userId, int friendsId); + + void validateUserId(int id); +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..147f265 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1 @@ - +logging.level.org.zalando.logbook= TRACE diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmTest.java index 245a172..2785f5a 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmTest.java @@ -8,6 +8,8 @@ import org.junit.jupiter.api.Test; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.storage.film.InMemoryFilmStorage; +import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; import java.time.LocalDate; import java.util.Set; @@ -23,11 +25,12 @@ public class FilmTest { validator = validatorFactory.usingContext().getValidator(); } - private FilmController filmController; + private InMemoryFilmStorage inMemoryFilmStorage; @BeforeEach public void setUp() { - filmController = new FilmController(); + InMemoryUserStorage inMemoryUserStorage = new InMemoryUserStorage(); + inMemoryFilmStorage = new InMemoryFilmStorage(inMemoryUserStorage); } @@ -77,11 +80,11 @@ public void whenFilmReleaseDateBeforeBirthdayFilmsThrowValidationException() { .description("Super description") .releaseDate(LocalDate.of(1895, 12, 27)) .build(); - assertThrows(ValidationException.class, () -> filmController.create(film)); + assertThrows(ValidationException.class, () -> inMemoryFilmStorage.create(film)); film.setName("Fork"); film.setReleaseDate(LocalDate.of(1895, 12, 28)); - assertDoesNotThrow(() -> filmController.create(film)); + assertDoesNotThrow(() -> inMemoryFilmStorage.create(film)); } // продолжительность фильма должна быть положительным числом. @@ -112,7 +115,7 @@ public void filmCreate() { .build(); Set> violation = validator.validate(film); assertEquals(0, violation.size()); - assertDoesNotThrow(() -> filmController.create(film)); + assertDoesNotThrow(() -> inMemoryFilmStorage.create(film)); } // Правильный сценарий обновления @@ -127,7 +130,7 @@ public void filmUpdate() { .build(); Set> violation = validator.validate(film); assertEquals(0, violation.size()); - assertDoesNotThrow(() -> filmController.create(film)); + assertDoesNotThrow(() -> inMemoryFilmStorage.create(film)); // обновляем film.setId(1); @@ -137,8 +140,8 @@ public void filmUpdate() { film.setDuration(2); Set> violation2 = validator.validate(film); assertEquals(0, violation2.size()); - assertDoesNotThrow(() -> filmController.update(film)); - Film createdFilm = filmController.update(film); + assertDoesNotThrow(() -> inMemoryFilmStorage.update(film)); + Film createdFilm = inMemoryFilmStorage.update(film); assertEquals(film.toString(), createdFilm.toString()); } diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/UserTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/UserTest.java index de62f53..49f4f6d 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/controller/UserTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/UserTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; import java.time.LocalDate; import java.util.Set; @@ -23,11 +24,11 @@ public class UserTest { validator = validatorFactory.usingContext().getValidator(); } - private UserController userController; + private InMemoryUserStorage inMemoryUserStorage; @BeforeEach public void setUp() { - userController = new UserController(); + inMemoryUserStorage = new InMemoryUserStorage(); } // электронная почта не может быть пустой и должна содержать символ @; @@ -92,7 +93,7 @@ public void whenUserNameNullLoginEqualName() { .build(); Set> violationGod = validator.validate(user); assertEquals(0, violationGod.size()); - User createdUser = userController.create(user); + User createdUser = inMemoryUserStorage.create(user); assertEquals(createdUser.getName(), user.getLogin()); } @@ -122,7 +123,7 @@ public void createUser() { .build(); Set> violation = validator.validate(user); assertEquals(0, violation.size()); - assertDoesNotThrow(() -> userController.create(user)); + assertDoesNotThrow(() -> inMemoryUserStorage.create(user)); } @@ -137,7 +138,7 @@ public void updateUser() { .build(); Set> violation = validator.validate(user); assertEquals(0, violation.size()); - assertDoesNotThrow(() -> userController.create(user)); + assertDoesNotThrow(() -> inMemoryUserStorage.create(user)); // обновляем user.setId(1); @@ -147,7 +148,7 @@ public void updateUser() { user.setBirthday(LocalDate.of(2023, 12, 12)); Set> violation2 = validator.validate(user); assertEquals(0, violation2.size()); - User createdUser = userController.update(user); + User createdUser = inMemoryUserStorage.update(user); assertEquals(user.toString(), createdUser.toString());