diff --git a/README.md b/README.md index 2cf454a..ddca9f6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,103 @@ -# java-filmorate -Template repository for Filmorate project. +# Filmorate +Это проект кинотеки, в который можно добавлять комментарии, оценивать фильмы находить самые популярные а так добавлять +в друзья пользователей кинотеки. + +# Схема БД +Схема БД + +# Примеры запросов + +### Для пользователей + +* Создание пользователя +```SQL +INSERT INTO users(email, + login, + name, + birthday) +VALUES (?,?,?,?); +``` + +* Обновление пользователя +```SQL +UPDATE users + SET email=?, + login=?, + name=?, + birthday=? + WHERE id=?; +``` +* Получение пользоывтеля по ID +```SQL +SELECT * FROM users WHERE id=?; +``` +* Получение всех пользователей +```SQL +SELECT * FROM users; +``` +* Удаление пользователя + ```SQL +DELETE FROM users WHERE id=?; +``` +* Добавление в список друзей + ```SQL +INSERT INTO friends (user_id, to_user_id) VALUES (?,?); +``` +* Получение списка друзей + ```SQL +"SELECT to_user_id FROM friends WHERE user_id=?; +``` +* Обновление списка друзей + ```SQL +DELETE FROM users WHERE id=?; +``` +### Для фильмов + +* Добавление фильма + ```SQL +INSERT INTO films(name, + description, + release_date, + duration, + mpa_rating_id) + VALUES(?,?,?,?,?); +``` +* Обновление фильма + ```SQL +UPDATE films SET name=?, + description=?, + release_date=?, + duration=?, + mpa_rating_id=? +WHERE id=?; +``` +* Удаление фильма + ```SQL +DELETE FROM films WHERE id=?; +``` +* Получение фильма по ID + ```SQL +SELECT * FROM films WHERE id=?; +``` +* Получение всех фильмов + ```SQL +SELECT * FROM films; +``` +* Получение списка лайков фильма + ```SQL +SELECT user_id FROM film_likes WHERE film_id=?; +``` +* Добавление лайка фильма + ```SQL +INSERT INTO film_likes(film_id, user_id) VALUES (?,?); +``` +* Удвление лайка фильма + ```SQL +DELETE FROM film_likes WHERE film_id=? AND user_id=?; +``` +* Получение списка жанров фильма + ```SQL +SELECT g.genre_id AS id, gs.name AS name + FROM genre AS g LEFT OUTER JOIN genres AS gs ON g.genre_id = gs.id + WHERE g.film_id =? ORDER BY id"; +``` diff --git a/filmorate.png b/filmorate.png new file mode 100644 index 0000000..bca715a Binary files /dev/null and b/filmorate.png differ diff --git a/pom.xml b/pom.xml index 2e002aa..baec517 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,24 @@ logbook-spring-boot-starter 3.7.2 + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + runtime + diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java new file mode 100644 index 0000000..1950b3c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java @@ -0,0 +1,43 @@ +package ru.yandex.practicum.filmorate.controller; + +import jakarta.validation.constraints.Positive; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; + +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.service.GenreService; + +import java.util.Collection; + +@Validated +@RestController +@RequestMapping("/genres") +public class GenreController { + + private final GenreService genreService; + private final Logger logger = LoggerFactory.getLogger(GenreService.class); + + @Autowired + public GenreController(GenreService genreService) { + this.genreService = genreService; + } + + @GetMapping({"/{id}"}) + @ResponseStatus(HttpStatus.OK) + public Genre getGenreId(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("возращаем жанр по id: " + id); + return genreService.getGenreId(id); + } + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public Collection getGenreAll() { + logger.info("возращаем список жанров"); + return genreService.getGenreAll(); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java new file mode 100644 index 0000000..177b9d8 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java @@ -0,0 +1,43 @@ +package ru.yandex.practicum.filmorate.controller; + + +import jakarta.validation.constraints.Positive; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.service.MpaService; + +import java.util.Collection; + +@Validated +@RestController +@RequestMapping("/mpa") +public class MpaController { + private final Logger logger = LoggerFactory.getLogger(MpaController.class); + + private final MpaService mpaService; + + @Autowired + public MpaController(MpaService mpaService) { + this.mpaService = mpaService; + } + + + @GetMapping({"/{id}"}) + @ResponseStatus(HttpStatus.OK) + public Mpa getMpaId(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("возращаем жанр по id: " + id); + return mpaService.getMpaId(id); + } + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public Collection getMpaAll() { + logger.info("возращаем список жанров"); + return mpaService.getMpaAll(); + } +} 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 938f2b8..03988a7 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -90,7 +90,7 @@ public Collection friendsGetList(@Positive(message = "неверное з @ResponseStatus(HttpStatus.OK) public Collection friendsGetCommonList(@Positive(message = "неверное значение") @PathVariable long id, @Positive(message = "неверное значение") @PathVariable long otherId) { - logger.info("показывает список друзей"); + logger.info("показывает обший список друзей"); return userService.friendsGetCommonList(id, otherId); } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java new file mode 100644 index 0000000..dd8d262 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(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 297ba77..03296f2 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -5,15 +5,11 @@ import lombok.*; import java.time.LocalDate; +import java.util.HashSet; import java.util.Set; -/** - * Film. - */ -@Getter -@Setter -@ToString -@AllArgsConstructor +@Data +@NoArgsConstructor public class Film extends DataModel implements Comparable { private Long id; @@ -34,7 +30,12 @@ public class Film extends DataModel implements Comparable { @Min(1) private int duration; - private Set likesId; + private Set likesId = new HashSet<>(); + + private Set genres = new HashSet<>(); + + // @NotNull (message ="рейтинг не может быть пустым") + private Mpa mpa; @Override public int compareTo(Film o) { diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Friends.java b/src/main/java/ru/yandex/practicum/filmorate/model/Friends.java new file mode 100644 index 0000000..50de1a3 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Friends.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +public class Friends { + + @NonNull + private long userId; + + @NonNull + private long toUserId; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java new file mode 100644 index 0000000..3244cd4 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java @@ -0,0 +1,18 @@ +package ru.yandex.practicum.filmorate.model; + + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +public class Genre extends DataModel { + + @NonNull + private Long id; + + @NonNull + private String name; + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Like.java b/src/main/java/ru/yandex/practicum/filmorate/model/Like.java new file mode 100644 index 0000000..957dbb0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Like.java @@ -0,0 +1,17 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +public class Like { + + @NonNull + private long userId; + + @NonNull + private long filmId; + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java new file mode 100644 index 0000000..963d28b --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java @@ -0,0 +1,18 @@ +package ru.yandex.practicum.filmorate.model; + +import jakarta.validation.constraints.Positive; +import lombok.*; + +@Data +@NoArgsConstructor +public class Mpa extends DataModel { + + @NonNull + @Positive + private long id; + + + private String name; + + +} 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 c8dd66e..ecb60fa 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -5,12 +5,11 @@ import lombok.*; import java.time.LocalDate; -import java.util.Set; @Setter @Getter @ToString -@AllArgsConstructor +@RequiredArgsConstructor @EqualsAndHashCode(exclude = {"id", "name", "birthday"}, callSuper = false) public class User extends DataModel { @@ -30,7 +29,4 @@ public class User extends DataModel { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate birthday; - private Set friendsId; - - } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java index e9aea22..433fdb7 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -8,12 +8,12 @@ import ru.yandex.practicum.filmorate.controller.ValidateController; import ru.yandex.practicum.filmorate.exception.ErrorIsNull; import ru.yandex.practicum.filmorate.exception.ValidationException; -import ru.yandex.practicum.filmorate.model.Film; -import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.model.*; import ru.yandex.practicum.filmorate.storage.FilmStorage; import ru.yandex.practicum.filmorate.storage.UserStorage; import java.util.Collection; +import java.util.HashSet; import java.util.stream.Collectors; @Service @@ -23,6 +23,7 @@ public class FilmService { private final UserStorage userStorage; private final ValidateController validateController; + private final Logger logger = LoggerFactory.getLogger(FilmService.class); @Autowired @@ -36,6 +37,7 @@ public FilmService(FilmStorage filmStorage, UserStorage userStorage, public Film create(Film film) { validateController.validateFilm(film); Film returnFilm = filmStorage.create(film); + filmStorage.addGenre(returnFilm.getId(), returnFilm.getGenres()); logger.info("Фильм добавлен id: " + returnFilm.getId()); return returnFilm; } @@ -46,19 +48,26 @@ public Film getFilmId(long id) { throw new ErrorIsNull("нет такого id"); } logger.info("поиск по id=" + id); + + getFilm.setGenres(filmStorage.getGenre(id)); + getFilm.setLikesId(new HashSet<>(filmStorage.getLikeId(id))); return getFilm; } public Collection getAll() { logger.info("Вернули список"); - Collection returnAll = filmStorage.getAll(); - if (returnAll.isEmpty()) { + Collection returnAllFilm = filmStorage.getAll(); + + if (returnAllFilm.isEmpty()) { throw new ErrorIsNull("список пуст"); } - return returnAll; + filmStorage.getAllFullGenreLike(returnAllFilm); + return returnAllFilm; } + public void delete(long id) { + Film delFilm = filmStorage.delete(id); if (delFilm == null) { throw new ErrorIsNull("нет такого id"); @@ -80,6 +89,8 @@ public Film update(Film newFilm) { } validateController.validateFilm(newFilm); logger.info("запись фильма обновлена"); + + filmStorage.updateGenre(newFilm.getId(), newFilm.getGenres()); return filmStorage.update(newFilm); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java new file mode 100644 index 0000000..2a57a4f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java @@ -0,0 +1,43 @@ +package ru.yandex.practicum.filmorate.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.GenreStorage; + +import java.util.Collection; + +@Service +public class GenreService { + private final GenreStorage genreStorage; + private final Logger logger = LoggerFactory.getLogger(GenreStorage.class); + + @Autowired + public GenreService(GenreStorage genreStorage) { + this.genreStorage = genreStorage; + } + + public Genre getGenreId(long id) { + Genre result = genreStorage.getId(id); + if (result == null) { + logger.warn("нет такого жанра"); + throw new ErrorIsNull("нет такого жанра"); + } + logger.info("возвращаем жанр по id: " + id); + return result; + } + + public Collection getGenreAll() { + logger.info("возращаем все жанры"); + Collection reselt = genreStorage.getAll(); + if (reselt.isEmpty()) { + throw new ErrorIsNull("список жанров пуст"); + } + return reselt; + } + + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java new file mode 100644 index 0000000..8553b60 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java @@ -0,0 +1,43 @@ +package ru.yandex.practicum.filmorate.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.storage.MpaStorage; + +import java.util.Collection; + +@Service +public class MpaService { + + private final Logger logger = LoggerFactory.getLogger(MpaStorage.class); + private final MpaStorage mpa; + + @Autowired + public MpaService(MpaStorage mpa) { + this.mpa = mpa; + } + + public Mpa getMpaId(long id) { + Mpa result = mpa.getId(id); + if (result == null) { + logger.warn("нет такого рейтиена"); + throw new ErrorIsNull("нет такого рейтиена"); + } + logger.info("возвращаем рейтинг по id: " + id); + return result; + } + + public Collection getMpaAll() { + logger.info("возращаем все жанры"); + Collection reselt = mpa.getAll(); + if (reselt.isEmpty()) { + throw new ErrorIsNull("список жанров пуст"); + } + return reselt; + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java index 1ecaa82..72da770 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -22,7 +22,7 @@ public class UserService { private final Logger logger = LoggerFactory.getLogger(FilmService.class); @Autowired - public UserService(UserStorage userStorage, ValidateController validateController) { + public UserService(UserStorage userStorage, ValidateController validateController) { this.userStorage = userStorage; this.validateController = validateController; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java index 38ac22e..e8d4fc1 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java @@ -1,8 +1,11 @@ package ru.yandex.practicum.filmorate.storage; import ru.yandex.practicum.filmorate.model.DataModel; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Genre; import java.util.Collection; +import java.util.Set; public interface FilmStorage { @@ -21,4 +24,15 @@ public interface FilmStorage { Collection setLikeId(long id, long userId); Collection delLikesId(long id, long userId); + + void addGenre(long filmId, Set genres); + + void deleteGenre(long filmId); + + void updateGenre(long filmId, Set genres); + + Set getGenre(long filmId); + + void getAllFullGenreLike(Collection films); + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/GenreStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/GenreStorage.java new file mode 100644 index 0000000..99204ab --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/GenreStorage.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.DataModel; + +import java.util.Collection; + +public interface GenreStorage { + + T getId(long id); + + Collection getAll(); + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java deleted file mode 100644 index a625616..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java +++ /dev/null @@ -1,73 +0,0 @@ -package ru.yandex.practicum.filmorate.storage; - -import org.springframework.stereotype.Repository; -import ru.yandex.practicum.filmorate.model.Film; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -@Repository -public class InMemoryFilmStorage extends Storage implements FilmStorage { - - @Override - public Film create(Film film) { - film.setId(getNextId()); - if (film.getLikesId() == null) { - film.setLikesId(new HashSet<>() { - }); - } - dataMap.put(film.getId(), film); - logger.info("Фильм добавлен id: " + film.getId()); - return film; - } - - @Override - public Film update(Film newFilm) { - Film oldFilm = dataMap.get(newFilm.getId()); - oldFilm.setName(newFilm.getName()); - oldFilm.setDescription(newFilm.getDescription()); - oldFilm.setReleaseDate(newFilm.getReleaseDate()); - oldFilm.setDuration(newFilm.getDuration()); - - if (newFilm.getLikesId() == null) { - newFilm.setLikesId(new HashSet<>() { - }); - } - oldFilm.setLikesId(newFilm.getLikesId()); - logger.info("Запись фильма обновлена"); - return oldFilm; - } - - @Override - public Collection getLikeId(long id) { - Film getLikes = dataMap.get(id); - return getLikes.getLikesId(); - } - - @Override - public Collection setLikeId(long id, long userId) { - Film getLikes = dataMap.get(id); - getLikes.getLikesId().add(userId); - return getLikes.getLikesId(); - } - - @Override - public Collection delLikesId(long id, long userId) { - Film getLikes = dataMap.get(id); - Set newLikesList = getLikes.getLikesId(); - newLikesList.remove(id); - getLikes.setLikesId(newLikesList); - return newLikesList; - } - - private long getNextId() { - long currentMaxId = dataMap.keySet() - .stream() - .mapToLong(id -> id) - .max() - .orElse(0); - return ++currentMaxId; - - } -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java deleted file mode 100644 index 3fc2128..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java +++ /dev/null @@ -1,78 +0,0 @@ -package ru.yandex.practicum.filmorate.storage; - -import org.springframework.stereotype.Repository; -import ru.yandex.practicum.filmorate.model.User; - -import java.util.*; - -@Repository -public class InMemoryUserStorage extends Storage implements UserStorage { - - @Override - public User create(User user) { - user.setId(getNextId()); - if (user.getFriendsId() == null) { - user.setFriendsId(new HashSet<>() { - }); - } - dataMap.put(user.getId(), user); - logger.info("Пользователь добавлен id: " + user.getId()); - return user; - } - - @Override - public User update(User newUser) { - User oldUser = dataMap.get(newUser.getId()); - oldUser.setName(newUser.getName()); - oldUser.setEmail(newUser.getEmail()); - oldUser.setLogin(newUser.getLogin()); - oldUser.setBirthday(newUser.getBirthday()); - oldUser.setFriendsId(newUser.getFriendsId()); - logger.info("Запись пользователя обновлена"); - return oldUser; - } - - @Override - public Collection getFriendId(long id) { - User getFriends = dataMap.get(id); - return getFriends.getFriendsId(); - } - - @Override - public Collection setFriendId(long id, long friendsId) { - User getFriends = dataMap.get(id); //добавление в друзья - Set newFriendsList = getFriends.getFriendsId(); - newFriendsList.add(friendsId); - getFriends.setFriendsId(newFriendsList); - - User getFriendsTo = dataMap.get(friendsId); //зеркальное добавление в друзья - Set newFriendsListTo = getFriendsTo.getFriendsId(); - newFriendsListTo.add(id); - getFriendsTo.setFriendsId(newFriendsListTo); - return newFriendsList; - } - - @Override - public Collection delFriendId(long id, long friendsId) { - User getFriends = dataMap.get(id); //удаление из друзей - Set newFriendsList = getFriends.getFriendsId(); - newFriendsList.remove(friendsId); - getFriends.setFriendsId(newFriendsList); - - User getFriendsTo = dataMap.get(friendsId); //удаление из друзей - Set newFriendsListTo = getFriendsTo.getFriendsId(); - newFriendsListTo.remove(id); - getFriendsTo.setFriendsId(newFriendsListTo); - - return newFriendsList; - } - - private long getNextId() { - long currentMaxId = dataMap.keySet() - .stream() - .mapToLong(id -> id) - .max() - .orElse(0); - return ++currentMaxId; - } -} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/MpaStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/MpaStorage.java new file mode 100644 index 0000000..bf53b0c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/MpaStorage.java @@ -0,0 +1,14 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.DataModel; + +import java.util.Collection; + +public interface MpaStorage { + + + T getId(long id); + + Collection getAll(); + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java index 3cb17a4..05d0bbb 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java @@ -4,6 +4,7 @@ import java.util.Collection; + public interface UserStorage { T create(T data); diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/BaseRepository.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/BaseRepository.java new file mode 100644 index 0000000..83de445 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/BaseRepository.java @@ -0,0 +1,71 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; + +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.List; +import java.util.Optional; + +// базовый блок обработки запросов с db +@RequiredArgsConstructor +public class BaseRepository { + + + protected final JdbcTemplate jdbc; + protected final RowMapper mapper; + + protected Optional findOne(String query, Object... params) { //возращаем 1 объект и db + try { + T result = jdbc.queryForObject(query, mapper, params); + return Optional.ofNullable(result); // возращаем объект в обёртке optional + } catch (EmptyResultDataAccessException ignored) { + return Optional.empty(); // если ошибка возращаем пустую обертку optional + } catch (DataAccessException e) { + throw new ErrorIsNull(e.getMessage()); + } + } + + protected List findMany(String query, Object... params) { //возращаем список с объектами из db + return jdbc.query(query, mapper, params); + } + + protected boolean delete(String query, long id) { // удаление элемента по id из db + int rowsDeleted = jdbc.update(query, id); + return rowsDeleted > 0; + } + + protected void update(String query, Object... params) { //обновление данных в db + int rowUpdated = jdbc.update(query, params); + if (rowUpdated == 0) { + throw new NotFoundException("не удалось обновить данные"); + } + } + + protected long insert(String query, Object... params) { //добавление новой записи с возращением id записи + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + jdbc.update(connection -> { + PreparedStatement ps = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + for (int idx = 0; idx < params.length; idx++) { + ps.setObject(idx + 1, params[idx]); + } + return ps; + }, keyHolder); + Integer id = keyHolder.getKeyAs(Integer.class); + // Возвращаем id нового пользователя + if (id != null) { + return id; + } else { + throw new ValidationException("не удалось сохраанить данные"); + } + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStore.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStore.java new file mode 100644 index 0000000..e010c19 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStore.java @@ -0,0 +1,202 @@ +package ru.yandex.practicum.filmorate.storage.dao; + + +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.rowset.SqlRowSet; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.FilmStorage; + +import java.util.*; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.yandex.practicum.filmorate.storage.mappers.GenreRowMapper; + +@Repository + +public class FilmDbStore extends BaseRepository implements FilmStorage { + + protected final Logger logger = LoggerFactory.getLogger(FilmDbStore.class); //логгер + + public FilmDbStore(JdbcTemplate jdbc, RowMapper mapper) { + super(jdbc, mapper); + } + + @Override + public Film create(Film film) { //добавление нового фильма + + try { + String query = "INSERT INTO films(name, description, release_date, duration, mpa_rating_id) VALUES(?,?,?,?,?)"; + long id = insert( + query, + film.getName(), + film.getDescription(), + film.getReleaseDate(), + film.getDuration(), + film.getMpa().getId() + ); + film.setId(id); + logger.info("добавили фильм id: " + id); + } catch (DataAccessException e) { + throw new ErrorIsNull(e.getMessage()); + } + return film; + } + + @Override + public Film update(Film film) { //обновили запись фильма + String query = "UPDATE films SET name=?, description=?, release_date=?, duration=?, mpa_rating_id=? WHERE id=?"; + update( + query, + film.getName(), + film.getDescription(), + film.getReleaseDate(), + film.getDuration(), + film.getMpa().getId(), + film.getId() + ); + + logger.info("запись фильма обновлена"); + return film; + } + + @Override + public Film delete(long id) { // удаление фильма по id + Film film = getId(id); + String query = "DELETE FROM films WHERE id=?"; + boolean isDelete = delete(query, id); + if (isDelete) { + logger.info("удален фильм id: " + id); + + return film; + } else { + throw new NotFoundException("ошибка удаления записи"); + } + } + + @Override + public Film getId(long id) { //получам фильм по id + String query = "SELECT f.*, mpa_r.name AS mpa_name FROM films AS f LEFT OUTER JOIN MPA_RATING AS mpa_r " + + "ON f.MPA_RATING_ID = mpa_r.id WHERE f.id=?"; + Optional film = findOne(query, id); + if (film.isPresent()) { + logger.info("возращаем фильм id: " + id); + Film result = film.get(); + result.setLikesId(new HashSet<>(getLikeId(result.getId()))); + return result; + } else { + throw new ErrorIsNull("фильма с таким id нет"); + } + } + + @Override + public Collection getAll() { //получам весь список фильмов + String query = "SELECT f.id, f.name, f.description, f.release_date, f.duration, f.mpa_rating_id, mpa_r.name AS mpa_name " + + "FROM films AS f LEFT OUTER JOIN MPA_RATING AS mpa_r ON f.MPA_RATING_ID = mpa_r.id"; + logger.info("возращаем список фильмов"); + return findMany(query); + } + + public void getAllFullGenreLike(Collection films) { + List filmsId = films.stream().map(film -> String.valueOf(film.getId())).collect(Collectors.toList()); //создам список id фильмов из переданной коллекции + Map filmMap = films.stream().collect(Collectors.toMap(Film::getId, film -> film)); //создаем мапу id фильмом и самим фильмом) + + String queryGenre = "SELECT g.film_id, gs.id AS id, gs.name" + + " FROM GENRE AS g LEFT OUTER JOIN GENRES AS gs ON g.GENRE_ID = gs.ID WHERE film_id IN (%s)"; + String queryLikes = "SELECT film_id, user_id FROM film_likes WHERE film_id IN (%s)"; + + SqlRowSet rsGenre = jdbc.queryForRowSet(String.format(queryGenre, String.join(",", filmsId))); + while (rsGenre.next()) { + Genre genre = new Genre(); + genre.setId(rsGenre.getLong("id")); + genre.setName(rsGenre.getString("name")); + filmMap.get(rsGenre.getLong("film_id")).getGenres().add(genre); + } + + SqlRowSet rsLike = jdbc.queryForRowSet(String.format(queryLikes, String.join(",", filmsId))); + while (rsLike.next()) { + filmMap.get(rsLike.getLong("film_id")).getLikesId().add(rsLike.getLong("user_id")); + } + } + + @Override + public Collection getLikeId(long filmId) { // получение списка лайков + String query = "SELECT user_id FROM film_likes WHERE film_id=" + filmId; + logger.info("получили список лайков"); + return jdbc.queryForList(query, Long.class); + } + + @Override + public Collection setLikeId(long filmId, long userId) { //добавление лайка + Collection likeList = getLikeId(filmId); + if (likeList.contains(userId)) { + throw new ErrorIsNull("данный пользователе уже поставил лайк"); + } + String query = "INSERT INTO film_likes(film_id, user_id) VALUES (?,?)"; + int rowUpdated = jdbc.update(query, filmId, userId); + if (rowUpdated > 0) { + logger.info("лайк добавлен"); + return getLikeId(filmId); + } else { + throw new ErrorIsNull("ошибка добавления лайка"); + } + } + + @Override + public Collection delLikesId(long filmId, long userId) { // удалили лайк из списка + String quary = "DELETE FROM film_likes WHERE film_id=? AND user_id=?"; + int rowDeleted = jdbc.update(quary, filmId, userId); + if (rowDeleted > 0) { + logger.info("удалили лайк из списка"); + return getLikeId(filmId); + } + throw new ErrorIsNull("ошибка при удалении лайка"); + } + + @Override + public void addGenre(long filmId, Set genreSet) { + try { + List batch = new ArrayList<>(); + for (Genre genre : genreSet) { + Object[] value = new Object[]{filmId, genre.getId()}; + batch.add(value); + } + jdbc.batchUpdate("INSERT INTO genre(film_id, genre_id) VALUES(?,?)", batch); + logger.info("добавление списка жанров фильма в db"); + } catch (DataAccessException e) { + throw new ErrorIsNull(e.getMessage()); + } + } + + @Override + public void deleteGenre(long filmId) { + jdbc.update("DELETE FROM genre WHERE film_id=?", filmId); + logger.info("удаление списка жанров фильма в db"); + } + + @Override + public void updateGenre(long filmId, Set genreSet) { + deleteGenre(filmId); + addGenre(filmId, genreSet); + logger.info("обновление списка жанров фильма в db"); + } + + @Override + public Set getGenre(long filmId) { + String quary = "SELECT g.genre_id AS id, gs.name AS name " + + "FROM genre AS g LEFT OUTER JOIN genres AS gs ON g.genre_id = gs.id " + + "WHERE g.film_id =" + filmId + " ORDER BY id"; + Set newGenreSet = new HashSet<>(jdbc.query(quary, new GenreRowMapper())); + + logger.info("возращаем список жанров фильма"); + return newGenreSet; + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorage.java new file mode 100644 index 0000000..6654298 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorage.java @@ -0,0 +1,42 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.GenreStorage; + +import java.util.Collection; +import java.util.Optional; + +@Repository +public class GenreDbStorage extends BaseRepository implements GenreStorage { + + protected final Logger logger = LoggerFactory.getLogger(GenreDbStorage.class); //логгер + + public GenreDbStorage(JdbcTemplate jdbc, RowMapper mapper) { + super(jdbc, mapper); + } + + @Override + public Genre getId(long id) { + String query = "SELECT * FROM genres WHERE id=?"; + Optional genre = findOne(query, id); + if (genre.isPresent()) { + logger.info("возращаем жанр id: " + id); + return genre.get(); + } else { + throw new ErrorIsNull("значение жанра пустое"); + } + } + + @Override + public Collection getAll() { + String query = "SELECT * FROM genres"; + logger.info("возращаем список жанров"); + return findMany(query); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorage.java new file mode 100644 index 0000000..33698f3 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorage.java @@ -0,0 +1,44 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.storage.MpaStorage; + +import java.util.Collection; +import java.util.Optional; + + +@Repository +public class MpaDbStorage extends BaseRepository implements MpaStorage { + + protected final Logger logger = LoggerFactory.getLogger(MpaDbStorage.class); //логгер + + public MpaDbStorage(JdbcTemplate jdbc, RowMapper mapper) { + super(jdbc, mapper); + } + + @Override + public Mpa getId(long id) { + String query = "SELECT * FROM mpa_rating WHERE id=?"; + Optional mpa = findOne(query, id); + if (mpa.isPresent()) { + logger.info("возращаем райтинг id: " + id); + return mpa.get(); + } else { + throw new ErrorIsNull("значение райтинга пустое"); + } + } + + @Override + public Collection getAll() { + String query = "SELECT * FROM mpa_rating"; + logger.info("возращаем список райтингов"); + return findMany(query); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorage.java new file mode 100644 index 0000000..4d138bb --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorage.java @@ -0,0 +1,118 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.UserStorage; + +import java.util.*; + +@Repository +public class UserDbStorage extends BaseRepository implements UserStorage { + public UserDbStorage(JdbcTemplate jdbc, RowMapper mapper) { + super(jdbc, mapper); + } + + protected final Logger logger = LoggerFactory.getLogger(UserDbStorage.class); //логгер + + @Override + public User create(User user) { //создаем пользвателя + String query = "INSERT INTO users(email, login, name, birthday) VALUES (?,?,?,?)"; + + long id = insert( + query, + user.getEmail(), + user.getLogin(), + user.getName(), + user.getBirthday() + ); + user.setId(id); + logger.info("пользователь создан id: " + id); + return user; + } + + @Override + public User update(User user) { //обновляем запись пользователя + String query = "UPDATE users SET email=?, login=?, name=?, birthday=? WHERE id=?"; + update(query, + user.getEmail(), + user.getLogin(), + user.getName(), + user.getBirthday(), + user.getId() + ); + logger.info("пользователь обновлен"); + return user; + } + + @Override + public User delete(long id) { //удаляем пользователя по id + User user = getId(id); + String query = "DELETE FROM users WHERE id=?"; + boolean isDelete = delete(query, id); + if (isDelete) { + logger.info("пользователь удален id: " + id); + return user; + } else throw new NullPointerException("ошибка удаления пользователя"); + } + + @Override + public User getId(long id) { //получаем пользователя по id + String query = "SELECT * FROM users WHERE id=?"; + Optional user = findOne(query, id); + if (user.isPresent()) { + logger.info("вернули пользователя по id: " + id); + return user.get(); + } else { + throw new ErrorIsNull("нет такого пользователя"); + } + } + + @Override + public Collection getAll() { //получаем всех пользователей + String query = "SELECT * FROM users"; + logger.info("вернули всех пользователей"); + return findMany(query); + } + + @Override + public Collection getFriendId(long id) { //получаем список друзей по id пользователя + String query = "SELECT to_user_id FROM friends WHERE user_id=" + id; + logger.info("вернуть список друзей"); + return jdbc.queryForList(query, Long.class); + } + + @Override + public Collection setFriendId(long id, long userId) { //добавляем в сисок друзей + Collection friendsList = getFriendId(id); + if (friendsList.contains(userId)) { + throw new ErrorIsNull("уже есть в друзьях"); + } + String query = "INSERT INTO friends (user_id, to_user_id) VALUES (?,?)"; + int rowUpdated = jdbc.update(query, id, userId); + if (rowUpdated > 0) { + logger.info("добавлен в список друзей"); + return getFriendId(id); + } else { + throw new ErrorIsNull("ошибка добавления в друзья"); + } + } + + @Override + public Collection delFriendId(long id, long userId) { //удаляем из друзей + + String query = "DELETE FROM friends WHERE user_id=? AND to_user_id=?"; + int rowUpdated = jdbc.update(query, id, userId); + if (rowUpdated > 0) { + logger.info("удален из списока друзей"); + return getFriendId(id); + } else { + logger.info("в списке нет друга для удаления"); + return getFriendId(id); + } + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FilmRowMappers.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FilmRowMappers.java new file mode 100644 index 0000000..bce413c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FilmRowMappers.java @@ -0,0 +1,35 @@ +package ru.yandex.practicum.filmorate.storage.mappers; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; + +@Component +public class FilmRowMappers implements RowMapper { + @Override + public Film mapRow(ResultSet rs, int rowNum) throws SQLException { + + Mpa mpa = new Mpa(); + mpa.setId(rs.getInt("mpa_rating_id")); + mpa.setName(rs.getString("mpa_name")); + + + Film film = new Film(); + film.setId(rs.getLong("id")); + film.setName(rs.getString("name")); + film.setDescription(rs.getString("description")); + + Timestamp releaseDate = rs.getTimestamp("release_date"); + film.setReleaseDate(releaseDate.toLocalDateTime().toLocalDate()); + + film.setDuration(rs.getInt("duration")); + film.setMpa(mpa); + + return film; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FriendsRowMappers.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FriendsRowMappers.java new file mode 100644 index 0000000..1d46f9f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FriendsRowMappers.java @@ -0,0 +1,21 @@ +package ru.yandex.practicum.filmorate.storage.mappers; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Friends; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class FriendsRowMappers implements RowMapper { + @Override + public Friends mapRow(ResultSet rs, int rowNum) throws SQLException { + Friends friends = new Friends(); + + friends.setUserId(rs.getInt("user_id")); + friends.setToUserId(rs.getInt("to_user_id")); + + return friends; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/GenreRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/GenreRowMapper.java new file mode 100644 index 0000000..1a4e6ce --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/GenreRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.storage.mappers; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Genre; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class GenreRowMapper implements RowMapper { + @Override + public Genre mapRow(ResultSet rs, int rowNum) throws SQLException { + Genre genre = new Genre(); + genre.setId(rs.getLong("id")); + genre.setName(rs.getString("name")); + return genre; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/LikeRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/LikeRowMapper.java new file mode 100644 index 0000000..8946f53 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/LikeRowMapper.java @@ -0,0 +1,22 @@ +package ru.yandex.practicum.filmorate.storage.mappers; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Like; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class LikeRowMapper implements RowMapper { + + @Override + public Like mapRow(ResultSet rs, int rowNum) throws SQLException { + Like like = new Like(); + + like.setUserId(rs.getInt("user_id")); + like.setFilmId(rs.getInt("film_id")); + + return like; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/MpaRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/MpaRowMapper.java new file mode 100644 index 0000000..53cd4fa --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/MpaRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.storage.mappers; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class MpaRowMapper implements RowMapper { + @Override + public Mpa mapRow(ResultSet rs, int rowNum) throws SQLException { + Mpa mpa = new Mpa(); + mpa.setId(rs.getLong("id")); + mpa.setName(rs.getString("name")); + return mpa; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/UserRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/UserRowMapper.java new file mode 100644 index 0000000..603c2cf --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/UserRowMapper.java @@ -0,0 +1,26 @@ +package ru.yandex.practicum.filmorate.storage.mappers; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.User; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; + +@Component +public class UserRowMapper implements RowMapper { + @Override + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + User user = new User(); + user.setId(rs.getLong("id")); + user.setEmail(rs.getString("email")); + user.setLogin(rs.getString("login")); + user.setName(rs.getString("name")); + + Timestamp birthdayDate = rs.getTimestamp("birthday"); + user.setBirthday(birthdayDate.toLocalDateTime().toLocalDate()); + + return user; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..f0e14c4 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,5 @@ - +spring.sql.init.mode=always +spring.datasource.url=jdbc:h2:file:./db/filmorate +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 94c6c3e..70f48db 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -2,4 +2,9 @@ logging: level: org: zalando: - logbook: TRACE \ No newline at end of file + logbook: TRACE + +spring: + h2: + console: + enabled: true \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..81f1b1f --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,14 @@ +MERGE INTO mpa_rating (id, name) +VALUES (1, 'G'), +(2, 'PG'), +(3, 'PG-13'), +(4, 'R'), +(5,'NC-17'); + +MERGE INTO genres (id, name) +VALUES (1, 'Комедия'), +(2,'Драма'), +(3,'Мультфильм'), +(4,'Триллер'), +(5,'Документальный'), +(6,'Боевик'); \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..6d6589e --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,50 @@ +--DROP TABLE IF EXISTS film_likes; +--DROP TABLE IF EXISTS friends; +--DROP TABLE IF EXISTS USERS; +--DROP TABLE IF EXISTS GENRE; +--DROP TABLE IF EXISTS GENRES; +--DROP TABLE IF EXISTS FILMS; +--DROP TABLE IF EXISTS MPA_RATING; + + +CREATE TABLE IF NOT EXISTS mpa_rating( +id INTEGER PRIMARY KEY NOT NULL, +name VARCHAR(50) NOT NULL +); + +CREATE TABLE IF NOT EXISTS films( +id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, +name VARCHAR(200) NOT NULL, +description VARCHAR(200) NOT NULL, +release_date DATE NOT NULL, +duration INTEGER NOT NULL, +mpa_rating_id INTEGER REFERENCES mpa_rating (id) NOT NULL +); + +CREATE TABLE IF NOT EXISTS genres( +id INTEGER PRIMARY KEY NOT NULL, +name VARCHAR(50) NOT NULL +); + +CREATE TABLE IF NOT EXISTS genre( +film_id INTEGER REFERENCES films (id) NOT NULL, +genre_id INTEGER REFERENCES genres (id) NOT NULL +); + +CREATE TABLE IF NOT EXISTS users( +id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, +email VARCHAR(50) UNIQUE NOT NULL, +login VARCHAR(50) UNIQUE NOT NULL, +name VARCHAR(50) NOT NULL, +birthday DATE NOT NULL +); + +CREATE TABLE IF NOT EXISTS friends( +user_id INTEGER REFERENCES users(id), +to_user_id INTEGER REFERENCES users(id) +); + +CREATE TABLE IF NOT EXISTS film_likes ( +film_id INTEGER REFERENCES films (id), +user_id INTEGER REFERENCES users (id) +); \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java index 78fce0a..138bc68 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java @@ -10,20 +10,23 @@ import ru.yandex.practicum.filmorate.model.Film; import java.time.LocalDate; -import java.util.HashSet; -import java.util.Set; public class FilmControllerTest { Exception exception; private final ValidateController validate = new ValidateController(); private Film film; - Set testList = new HashSet<>() { - }; + + @BeforeEach void setUp() { - film = new Film(1L, "name", "описание ", LocalDate.now().minusYears(3), 20, testList); + film = new Film(); + film.setId(1L); + film.setName("name"); + film.setDescription("описание "); + film.setReleaseDate(LocalDate.now().minusYears(3)); + film.setDuration(20); } @Test diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java deleted file mode 100644 index dc5cfdf..0000000 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.yandex.practicum.filmorate; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class FilmorateApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java index 1f633f6..a327a99 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java @@ -9,8 +9,6 @@ import ru.yandex.practicum.filmorate.model.User; import java.time.LocalDate; -import java.util.HashSet; -import java.util.Set; public class UserControllerTest { @@ -18,12 +16,17 @@ public class UserControllerTest { Exception exception; private final ValidateController validate = new ValidateController(); private User user; - Set testList = new HashSet<>() { - }; + @BeforeEach void setUp() { - user = new User(1L, "ya@yandex.ru", "login", "name", LocalDate.now().minusYears(3), testList); + user = new User(); + user.setId(1L); + user.setEmail("ya@yandex.ru"); + user.setLogin("login"); + user.setName("name"); + user.setBirthday(LocalDate.now().minusYears(3)); + } @Test @@ -96,11 +99,4 @@ public void userFutureBirthday() { Assertions.assertEquals("введена дата из будущего", exception.getMessage()); } - @Test - public void userFriends() { - User user1 = new User(1L, "ya1@yandex.ru", "login1", "name1", LocalDate.now().minusYears(3), testList); - User user2 = new User(2L, "ya2@yandex.ru", "login2", "name2", LocalDate.now().minusYears(3), testList); - user1.getFriendsId().add(2L); - Assertions.assertEquals(2, user1.getFriendsId().stream().toList().get(0)); - } } diff --git a/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java b/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java index a37520e..c882e4f 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java @@ -3,14 +3,13 @@ import jakarta.validation.ConstraintViolation; import jakarta.validation.Validation; import jakarta.validation.Validator; -import org.apache.logging.log4j.util.Strings; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Mpa; import ru.yandex.practicum.filmorate.model.User; import java.time.LocalDate; -import java.util.HashSet; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -20,24 +19,39 @@ public class ValidateTest { private Validator validator; - Set testList = new HashSet<>() { - }; + + User user = new User(); + Film film = new Film(); + @BeforeEach void setUp() { validator = Validation.buildDefaultValidatorFactory().getValidator(); + user.setId(1L); + user.setEmail("ya@yandex.ru"); + user.setLogin("login"); + user.setName("name"); + user.setBirthday(LocalDate.now().minusYears(3)); + + film.setId(1L); + film.setName("kino"); + film.setDescription("kino o kine"); + film.setReleaseDate(LocalDate.of(1990, 02, 22)); + film.setDuration(25); + film.setMpa(new Mpa()); + } @Test void validatorUserOk() { - User user = new User(1L, "name@yabex.ru", "log", "name", LocalDate.now().minusYears(3), testList); Set> violations = validator.validate(user); assertTrue(violations.isEmpty()); } @Test void validatorUserTest() { - User user = new User(1L, "nameyabex.ru", "log", "name", LocalDate.now().plusYears(3), testList); + user.setEmail("nameyabex.ru"); + user.setBirthday(LocalDate.now().plusDays(3)); Set> violations = validator.validate(user); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(2); @@ -47,7 +61,9 @@ void validatorUserTest() { @Test void validatorUserNotNull() { - User user = new User(1L, null, null, "name", null, testList); + user.setEmail(null); + user.setLogin(null); + user.setBirthday(null); Set> violations = validator.validate(user); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(3); @@ -57,15 +73,14 @@ void validatorUserNotNull() { @Test void validatorFilmOk() { - Film film = new Film(1L, "kino", "kino o kine", LocalDate.of(1990, 02, 22), 25, testList); Set> violations = validator.validate(film); assertTrue(violations.isEmpty()); } @Test void validatorFilmTest() { - Film film = new Film(1L, "kino", Strings.repeat("*", 220), - LocalDate.now().plusYears(3), 2, testList); + film.setDescription("*".repeat(220)); + film.setReleaseDate(LocalDate.now().plusYears(3)); Set> violations = validator.validate(film); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(2); @@ -75,8 +90,9 @@ void validatorFilmTest() { @Test void validatorFilmNotNull() { - Film film = new Film(1L, null, null, - null, 2, testList); + film.setName(null); + film.setReleaseDate(null); + film.setDescription(null); Set> violations = validator.validate(film); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(3); diff --git a/src/test/java/ru/yandex/practicum/filmorate/dao/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/dao/FilmDbStorageTest.java new file mode 100644 index 0000000..afd69ca --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/dao/FilmDbStorageTest.java @@ -0,0 +1,172 @@ +package ru.yandex.practicum.filmorate.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.storage.dao.FilmDbStore; +import ru.yandex.practicum.filmorate.storage.mappers.FilmRowMappers; + +import java.time.LocalDate; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@Import({FilmDbStore.class, FilmRowMappers.class}) +class FilmDbStorageTest { + private final FilmDbStore filmDbStore; + private final JdbcTemplate jdbc; + private Film film; + + @BeforeEach + void setUp() { + jdbc.update("DELETE FROM films"); + jdbc.update("ALTER TABLE films ALTER COLUMN id RESTART WITH 1"); + + film = new Film(); + + film.setName("film1"); + film.setDescription("film o filme"); + film.setReleaseDate(LocalDate.now().minusYears(1)); + film.setDuration(20); + Mpa mpa = new Mpa(); + mpa.setId(1); + film.setMpa(mpa); + filmDbStore.create(film); + } + + @Test + public void testFindFilmById() { + Optional filmOptional = Optional.of(filmDbStore.getId(1L)); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 1L) + ); + } + + @Test + public void testFindFilmByName() { + Optional filmOptional = Optional.of(filmDbStore.getId(1L)); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("name", "film1") + ); + } + + @Test + public void testFindFilmByDescription() { + Optional filmOptional = Optional.of(filmDbStore.getId(1L)); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("description", "film o filme") + ); + } + + @Test + public void testFindFilmByDuration() { + Optional filmOptional = Optional.of(filmDbStore.getId(1L)); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("duration", 20) + ); + } + + @Test + public void testFindFilmByReleaseDate() { + Optional filmOptional = Optional.of(filmDbStore.getId(1L)); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("releaseDate", LocalDate.now().minusYears(1)) + ); + } + + @Test + public void testFindFilmByMpa() { + Film film1 = filmDbStore.getId(1L); + assertThat(film1.getMpa().getId()).toString().equals("1"); + } + + @Test + public void testUpdateFilmById() { + film = filmDbStore.getId(1L); + film.setId(2L); + Optional filmOptional = Optional.of(film); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 2L) + ); + } + + @Test + public void testUpdateFilmByName() { + film = filmDbStore.getId(1L); + film.setName("film2"); + Optional filmOptional = Optional.of(film); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("name", "film2") + ); + } + + @Test + public void testUpdateFilmByDescription() { + film = filmDbStore.getId(1L); + film.setDescription("film o filme2"); + Optional filmOptional = Optional.of(film); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("description", "film o filme2") + ); + } + + @Test + public void testUpdateFilmByDuration() { + film = filmDbStore.getId(1L); + film.setDuration(25); + Optional filmOptional = Optional.of(film); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("duration", 25) + ); + } + + @Test + public void testUpdateFilmByReleaseDate() { + film = filmDbStore.getId(1L); + film.setReleaseDate(LocalDate.now().minusYears(2)); + Optional filmOptional = Optional.of(film); + assertThat(filmOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("releaseDate", LocalDate.now().minusYears(2)) + ); + } + + @Test + public void testUpdateFilmByMpa() { + Film film1 = filmDbStore.getId(1L); + Mpa mpa = film1.getMpa(); + mpa.setId(2); + film1.setMpa(mpa); + assertThat(film1.getMpa().getId()).toString().equals("2"); + } + +} diff --git a/src/test/java/ru/yandex/practicum/filmorate/dao/GenreDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/dao/GenreDbStorageTest.java new file mode 100644 index 0000000..ad0eac0 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/dao/GenreDbStorageTest.java @@ -0,0 +1,45 @@ +package ru.yandex.practicum.filmorate.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.dao.GenreDbStorage; +import ru.yandex.practicum.filmorate.storage.mappers.GenreRowMapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@Import({GenreDbStorage.class, GenreRowMapper.class}) +class GenreDbStorageTest { + private final GenreDbStorage genreDbStorage; + + @Test + public void testGetGenreById() { + + Optional userOptional = Optional.of(genreDbStorage.getId(1)); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 1L) + ); + } + + @Test + public void testGetMpaAll() { + List mpaList = new ArrayList<>(genreDbStorage.getAll()); + Assertions.assertEquals(mpaList.size(), 6, "Колличество записей должно совпадать"); + Assertions.assertEquals(mpaList.get(1), genreDbStorage.getId(2), "возращается не правильное значение"); + } + +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/dao/MpaDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/dao/MpaDbStorageTest.java new file mode 100644 index 0000000..71f1b12 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/dao/MpaDbStorageTest.java @@ -0,0 +1,49 @@ +package ru.yandex.practicum.filmorate.dao; + +import lombok.RequiredArgsConstructor; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; +import ru.yandex.practicum.filmorate.model.Mpa; + +import ru.yandex.practicum.filmorate.storage.dao.MpaDbStorage; +import ru.yandex.practicum.filmorate.storage.mappers.MpaRowMapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@Import({MpaDbStorage.class, MpaRowMapper.class}) +class MpaDbStorageTest { + private final MpaDbStorage mpaDbStorage; + + @Test + public void testGetMpaById() { + + Optional userOptional = Optional.of(mpaDbStorage.getId(1)); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 1L) + ); + } + + @Test + public void testGetMpaAll() { + List mpaList = new ArrayList<>(mpaDbStorage.getAll()); + Assertions.assertEquals(mpaList.size(), 5, "Колличество записей должно совпадать"); + Assertions.assertEquals(mpaList.get(1), mpaDbStorage.getId(2),"возращается не правильное значение"); + } + +} + diff --git a/src/test/java/ru/yandex/practicum/filmorate/dao/UserDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/dao/UserDbStorageTest.java new file mode 100644 index 0000000..2b91c19 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/dao/UserDbStorageTest.java @@ -0,0 +1,131 @@ +package ru.yandex.practicum.filmorate.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.dao.UserDbStorage; +import ru.yandex.practicum.filmorate.storage.mappers.UserRowMapper; + +import java.time.LocalDate; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@Import({UserDbStorage.class, UserRowMapper.class}) +class UserDbStorageTest { + private final UserDbStorage userStorage; + private final JdbcTemplate jdbc; + private User user; + + @BeforeEach + void setUp() { + jdbc.update("DELETE FROM users"); + jdbc.update("ALTER TABLE users ALTER COLUMN id RESTART WITH 1"); + + user = new User(); + user.setEmail("test1@ya.ru"); + user.setLogin("login1"); + user.setName("name1"); + user.setBirthday(LocalDate.now().minusYears(1)); + userStorage.create(user); + } + + @Test + public void testFindUserById() { + Optional userOptional = Optional.of(userStorage.getId(1)); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 1L) + ); + } + + @Test + public void testFindUserByName() { + Optional userOptional = Optional.of(userStorage.getId(1)); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("name", "name1") + ); + } + + @Test + public void testFindUserByLogin() { + Optional userOptional = Optional.of(userStorage.getId(1)); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("login", "login1") + ); + } + + @Test + public void testFindUserByBirthday() { + Optional userOptional = Optional.of(userStorage.getId(1)); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("birthday", LocalDate.now().minusYears(1)) + ); + } + + @Test + public void testUpdateUserById() { + user = userStorage.getId(1); + user.setId(2L); + Optional userOptional = Optional.of(user); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 2L) + ); + } + + @Test + public void testUpdateUserByName() { + user = userStorage.getId(1); + user.setName("name2"); + Optional userOptional = Optional.of(user); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("name", "name2") + ); + } + + @Test + public void testUpdateUserByLogin() { + user = userStorage.getId(1); + user.setLogin("login2"); + Optional userOptional = Optional.of(user); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("login", "login2") + ); + } + + @Test + public void testUpdateUserByBirthday() { + user = userStorage.getId(1); + user.setBirthday(LocalDate.now().minusYears(2)); + Optional userOptional = Optional.of(user); + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("birthday", LocalDate.now().minusYears(2)) + ); + } + +} \ No newline at end of file