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());