diff --git a/ER Diagramm.png b/ER Diagramm.png new file mode 100644 index 0000000..360a74b Binary files /dev/null and b/ER Diagramm.png differ diff --git a/README.md b/README.md index 2cf454a..94ba408 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ -# java-filmorate -Template repository for Filmorate project. +# Java filmorate + +*Как работает база данных:* + +**1. Таблица movie** +PK: movie_id (уникальный идентификатор фильма) +Содержит основные поля с названием, описанием фильма, id жанра (one to one с таблицей genre), mpa_id(one to one с таблицей mpa) +**2. Таблица movie_advanced** +PK: movie_id (one to one с таблицей movie) +Содержит дополнительную информацию о фильме. Создана для опциональных характеристик картины с запасом на расширение, что бы не перегружать остальное. +**3. Таблицы genre, mpa** - содержат информацию о жанре и рейтинге фильма. Вынесены в отдельную таблицу для упрощения корректировок значений. +**4. Таблица user** +PK: user_id (уникальный идентификатор пользователя) +Содержит основные поля, характеризующие пользователя +**5. Таблица user_friendlist** +PK: user_id+friend_id - ключ составной, связка id юзера+ id юзера-друга уникальна. +Предполагается выгрузка списка друзей по user_id и фильтрация по friendship_status, который отражает статус взаимоотношений: +0 - запрос отправлен от "друга" +1 - запрос согласован. +**6. Таблица movie_rating** +PK: movie_id + user_id - ключ составной, связка фильма и id лайкнувшего юзера уникальна. +Из неё можно будет каунтить рейтинг фильма по id, а также искать пересечения по лайкнувшим юзерам для составления списка обших фильмов. + +**Примеры запросов** +1. Получение фильма по id: + SELECT* + FROM movie + WHERE movie_id = {id фильма}; +2. Получение TOP-10 фильмов + SELECT * + FROM movie + WHERE movie_id IN (SELECT movie_id + FROM movie_rating + GROUP BY movie_id + ORDER BY COUNT(user_id) DESC + LIMIT 10); + + + +![Диаграмма базы данных](src/main/resources/Java-filmorate%20SQL%20db.png) + +SP12 start \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7d653f4..55240c4 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,24 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + 2.1.210 + runtime + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-test + 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 8f3cccf..b4662c8 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -1,7 +1,6 @@ package ru.yandex.practicum.filmorate.controller; import lombok.extern.slf4j.Slf4j; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.model.Film; @@ -50,4 +49,9 @@ public Film removeLike(@PathVariable Integer filmId, @PathVariable Integer id) { public Collection getTop(@RequestParam(defaultValue = "10") Integer count) { return filmService.top(count); } + + @GetMapping("/{id}") + public Film getFilm(@PathVariable Integer id) { + return filmService.getFilm(id); + } } 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..8faf1ff --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java @@ -0,0 +1,32 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.service.FilmService; + +import java.util.Collection; + +@Slf4j +@RestController +@RequestMapping("/genres") +public class GenreController { + + private final FilmService filmService; + + @Autowired + public GenreController(FilmService filmService) { + this.filmService = filmService; + } + + @GetMapping + public Collection getAllGenres() { + return filmService.getAllGenres(); + } + + @GetMapping("/{id}") + public Genre getGenre(@PathVariable Integer id) { + return filmService.getGenre(id); + } +} 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..b6c1ac0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MPAController.java @@ -0,0 +1,35 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.service.FilmService; + +import java.util.Collection; + +@Slf4j +@RestController +@RequestMapping("/mpa") +public class MPAController { + + private final FilmService filmService; + + @Autowired + public MPAController(FilmService filmService) { + this.filmService = filmService; + } + + @GetMapping + public Collection getAllGenres() { + return filmService.getAllMpa(); + } + + @GetMapping("/{id}") + public Mpa getGenre(@PathVariable Integer id) { + return filmService.getMpa(id); + } +} 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 792ef82..cd33d50 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -57,4 +57,9 @@ public Collection getFriends(@PathVariable Integer id) { return userService.getFriends(id); } + @GetMapping("/test") + public Collection testSequense(@PathVariable Integer id) { + return userService.getFriends(id); + } + } \ No newline at end of file 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 fe52d60..477acf8 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -3,14 +3,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import ru.yandex.practicum.filmorate.exceptions.UnknownDataException; import java.time.LocalDate; +import java.util.Collection; import java.util.HashSet; import java.util.Set; - @AllArgsConstructor +@NoArgsConstructor @Data public class Film { private int id; @@ -19,6 +21,8 @@ public class Film { private LocalDate releaseDate; private long duration; private int rating = 0; + private Collection genres; + private Mpa mpa; @JsonIgnore private Set whoLikes; @@ -48,3 +52,4 @@ public Film removeLike(Integer id) { } + 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..7f7eaf4 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java @@ -0,0 +1,24 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode +public class Genre { + private int id; + private String name; + + public Genre(int id, String name) { + this.id = id; + this.name = name; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } +} 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..ebccd05 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java @@ -0,0 +1,22 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; + +@Data +public class Mpa { + private int id; + private String name; + + public Mpa(int id, String name) { + this.id = id; + this.name = name; + } + + public int getId() { + return id; + } + + public String getName() { + return 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 662b7af..f99fbfe 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -2,6 +2,7 @@ import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.time.LocalDate; @@ -11,6 +12,7 @@ @Slf4j @AllArgsConstructor +@NoArgsConstructor @Data public class User { private int id; @@ -18,26 +20,9 @@ public class User { private String login; private String name; private LocalDate birthday; - private Set friends; + private Set friends; - public User addFriend(Integer friendId) { - if (friends == null) { - friends = new HashSet<>(); - } - friends.add(friendId); - log.info("Друзья пользователя " + id + ": " + friends); - return this; - } - - public User deleteFriend(Integer friendId) { - if (friends == null) { - friends = new HashSet<>(); - } - friends.remove(friendId); - return this; - } - - public Collection getFriends() { + public Collection getFriends() { if (friends == null) { friends = new HashSet<>(); } 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 275d547..6f18962 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -3,77 +3,78 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import ru.yandex.practicum.filmorate.exceptions.UnknownDataException; import ru.yandex.practicum.filmorate.model.Film; -import ru.yandex.practicum.filmorate.storage.film.InMemoryFilmStorage; -import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.storage.film.FilmDbStorage; +import ru.yandex.practicum.filmorate.storage.user.UserStorage; import java.util.Collection; -import java.util.Comparator; -import java.util.stream.Collectors; @Service @Slf4j public class FilmService { - private final InMemoryFilmStorage inMemoryFilmStorage; - private final InMemoryUserStorage inMemoryUserStorage; + private final FilmDbStorage filmStorage; @Autowired - public FilmService(InMemoryFilmStorage inMemoryFilmStorage, InMemoryUserStorage inMemoryUserStorage) { - this.inMemoryFilmStorage = inMemoryFilmStorage; - this.inMemoryUserStorage = inMemoryUserStorage; + public FilmService(FilmDbStorage filmStorage, UserStorage userStorage) { + this.filmStorage = filmStorage; } public Collection findAllFilms() { - return inMemoryFilmStorage.findAllFilms(); + log.info("Команда: найти все фильмы"); + return filmStorage.findAllFilms(); } public Film createFilm(Film film) { - return inMemoryFilmStorage.createFilm(film); + log.info("Команда: создать фильм " + film.getName()); + return filmStorage.createFilm(film); } public Film updateFilm(Film film) { - return inMemoryFilmStorage.updateFilm(film); + log.info("Команда: обновить фильм " + film.getName()); + return filmStorage.updateFilm(film); } - public Film addLike(Integer filmId, Integer id) { - if (!checkFilmUserAvalaibility(filmId, id)) { - throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно поставить лайк."); - } - log.info("Запрошено добавление лайка фильму: " + filmId + ". От пользователя: " + id); - return inMemoryFilmStorage.findAllFilms().stream() - .filter(film -> film.getId() == filmId) - .map(film -> film.addLike(id)) - .findFirst() - .orElseThrow(() -> new UnknownDataException("Запрошенные ресурсы отсутствуют")); - } public Film removeLike(Integer filmId, Integer id) { - if (!checkFilmUserAvalaibility(filmId, id)) { - throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно удалить лайк."); - } - log.info("Запрошено удаление лайка фильму: " + filmId + ". От пользователя: " + id); - return inMemoryFilmStorage.findAllFilms().stream() - .filter(film -> film.getId() == filmId) - .map(film -> film.removeLike(id)) - .findFirst() - .orElseThrow(() -> new UnknownDataException("Запрошенные ресурсы отсутствуют")); + log.info("Команда: удалить лайк"); + return filmStorage.removeLike(filmId, id); } public Collection top(Integer count) { - log.info("Запрошен топ фильмов в количестве: " + count); - return inMemoryFilmStorage.findAllFilms().stream() - .sorted(Comparator.comparingInt(Film::getRating).reversed()) - .limit(count) - .collect(Collectors.toList()); + log.info("Команда: получить топ фильмов (" + count + ")"); + return filmStorage.top(count); + } + + public Film addLike(Integer filmId, Integer id) { + log.info("Команда: добавить лайк"); + return filmStorage.addLike(filmId, id); + } + + public Collection getAllGenres() { + log.info("Команда: получить все жанры"); + return filmStorage.getAllGenres(); + } + + public Genre getGenre(Integer count) { + log.info("Команда: получить жанр"); + return filmStorage.getGenre(count); + } + + public Collection getAllMpa() { + log.info("Команда: получить все возрастные рейтинги"); + return filmStorage.getAllMpa(); + } + public Mpa getMpa(Integer id) { + log.info("Команда: получить возрастной рейтинг"); + return filmStorage.getMpa(id); } - //проверка наличия - //проверка добавления. Если всё хорошо, вернёт true - private boolean checkFilmUserAvalaibility(Integer filmId, Integer id) { - return inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == id) && - inMemoryFilmStorage.findAllFilms().stream().anyMatch(film -> film.getId() == filmId); + public Film getFilm(Integer id) { + log.info("Команда: запрос фильма"); + return filmStorage.getFilm(id); } } 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 71373e4..61a5f41 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -3,119 +3,58 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import ru.yandex.practicum.filmorate.exceptions.UnknownDataException; import ru.yandex.practicum.filmorate.model.User; -import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; +import ru.yandex.practicum.filmorate.storage.user.UserDbStorage; import java.util.Collection; -import java.util.Set; -import java.util.stream.Collectors; @Service @Slf4j public class UserService { - private final InMemoryUserStorage inMemoryUserStorage; + private final UserDbStorage userDbStorage; @Autowired - public UserService(InMemoryUserStorage userStorage) { - this.inMemoryUserStorage = userStorage; + public UserService(UserDbStorage userStorage) { + this.userDbStorage = userStorage; } public Collection findAllUsers() { - return inMemoryUserStorage.findAllUsers(); + return userDbStorage.findAllUsers(); } public User createUser(User user) { - return inMemoryUserStorage.createUser(user); + return userDbStorage.createUser(user); } public User updateUser(User user) { - return inMemoryUserStorage.updateUser(user); + return userDbStorage.updateUser(user); } //добавление в друзья public User addFriend(Integer id, Integer friendId) { - if (!checkFriendsAvalaibility(id, friendId)) { - throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно добавить в друзья."); - } - User updatedFriend = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == friendId) - .map(user -> user.addFriend(id)) - .findFirst() - .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует")); - inMemoryUserStorage.updateUser(updatedFriend); - - User updatedUser = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == id) - .map(user -> user.addFriend(friendId)) - .findFirst() - .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует")); - log.info("Запрошено добавление в друзья: " + id + ". От пользователя: " + friendId); - return inMemoryUserStorage.updateUser(updatedUser); + log.info("Команда: добавить друга"); + return userDbStorage.addFriend(id, friendId); } //удаление из друзей public User removeFriend(Integer id, Integer friendId) { - if (!checkFriendsAvalaibility(id, friendId)) { - throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно удалить из друзей."); - } - User updatedFriend = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == friendId) - .map(user -> user.deleteFriend(id)) - .findFirst() - .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует")); - inMemoryUserStorage.updateUser(updatedFriend); - - User updatedUser = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == id) - .map(user -> user.deleteFriend(friendId)) - .findFirst() - .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует")); - log.info("Запрошено удаление из в друзей: " + id + ". От пользователя: " + friendId); - return inMemoryUserStorage.updateUser(updatedUser); + log.info("Команда: удалить друга"); + return userDbStorage.removeFriend(id, friendId); } //вывод списка общих друзей public Collection getCommonFriends(Integer id, Integer friendId) { - if (!checkFriendsAvalaibility(id, friendId)) - throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список общих друзей."); - User firstUser = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == id) - .findFirst() - .orElse(null); - User secondUser = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == friendId) - .findFirst() - .orElse(null); - Set commonId = firstUser.getFriends().stream() - .filter(secondUser.getFriends()::contains) - .collect(Collectors.toSet()); - log.info("Запрошены общие друзья: " + id + ". И: " + friendId); - return inMemoryUserStorage.findAllUsers().stream() - .filter(user -> commonId.contains(user.getId())) - .collect(Collectors.toList()); + log.info("Команда: получить список общих друзей"); + return userDbStorage.getCommonFriends(id, friendId); } + //получить друзей public Collection getFriends(Integer id) { - if (!(inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == id))) { - throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список друзей"); - } - Collection friendsIds = inMemoryUserStorage.findAllUsers().stream() - .filter(user -> user.getId() == id) - .flatMap(user -> user.getFriends().stream()) - .collect(Collectors.toSet()); - return inMemoryUserStorage.findAllUsers().stream() - .filter(user -> friendsIds.contains(user.getId())) - .collect(Collectors.toSet()); - } - - //проверка добавления друга - private boolean checkFriendsAvalaibility(Integer id, Integer friendId) { - return inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == id) && - inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == friendId); + log.info("Команда: получить список друзей"); + return userDbStorage.getFriends(id); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java new file mode 100644 index 0000000..9fbfe43 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java @@ -0,0 +1,323 @@ +package ru.yandex.practicum.filmorate.storage.film; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exceptions.BadDataException; +import ru.yandex.practicum.filmorate.exceptions.UnknownDataException; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.time.LocalDate; +import java.util.*; + +@Repository +@Qualifier("filmDbStorage") +@Slf4j +@Component +@Primary + +public class FilmDbStorage implements FilmStorage { + + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public FilmDbStorage(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public Collection findAllFilms() { + String sql = "SELECT movie_id, movie_name, movie_description, movie_release, movie_duration FROM movie"; + List films = jdbcTemplate.query(sql, (rs, rowNum) -> { + Film film = new Film(); + film.setId(rs.getInt("movie_id")); + film.setName(rs.getString("movie_name")); + film.setDescription(rs.getString("movie_description")); + film.setReleaseDate(rs.getDate("movie_release").toLocalDate()); + String durationStr = rs.getString("movie_duration"); + String[] timeParts = durationStr.split(":"); + long durationInSeconds = Integer.parseInt(timeParts[0]) * 3600 + + Integer.parseInt(timeParts[1]) * 60 + + Integer.parseInt(timeParts[2]); + film.setDuration(durationInSeconds); + return film; + }); + return films; + } + + @Override + public Film createFilm(Film film) { + validateFilm(film); + + long durationInSeconds = film.getDuration(); + String durationFormatted = String.format("%02d:%02d:%02d", + (durationInSeconds / 3600), + (durationInSeconds % 3600) / 60, + durationInSeconds % 60); + + film.setId(getNextFilmId()); + + String sql = "INSERT INTO movie (movie_id, movie_name, movie_description, movie_release, movie_duration, mpa_id) " + + "VALUES (?, ?, ?, ?, ?, ?)"; + jdbcTemplate.update(sql, + film.getId(), + film.getName(), + film.getDescription(), + film.getReleaseDate(), + durationFormatted, + film.getMpa() != null ? film.getMpa().getId() : null); + //проверка на существование жанров + if (film.getGenres() != null && !film.getGenres().isEmpty()) { + String genreSql = "MERGE INTO movie_genre (movie_id, genre_id) KEY (movie_id, genre_id) VALUES (?, ?)"; + for (Genre genre : film.getGenres()) { + jdbcTemplate.update(genreSql, film.getId(), genre.getId()); + } + } + return film; + } + + + @Override + public Film updateFilm(Film film) { + validateFilm(film); + String checkSql = "SELECT COUNT(*) FROM movie WHERE movie_id = ?"; + Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class, film.getId()); + if (count == null || count == 0) { + throw new UnknownDataException("Фильм с таким ID не найден."); + } + String sql = "UPDATE movie SET movie_name = ?, movie_description = ?, movie_release = ?, movie_duration = ? WHERE movie_id = ?"; + long durationInSeconds = film.getDuration(); + String durationFormatted = String.format("%02d:%02d:%02d", + (durationInSeconds / 3600), + (durationInSeconds % 3600) / 60, + durationInSeconds % 60); + jdbcTemplate.update(sql, + film.getName(), + film.getDescription(), + film.getReleaseDate(), + durationFormatted, + film.getId()); + return film; + } + + + private void validateFilm(Film film) { + if (film == null) { + throw new BadDataException("Тело запроса не должно быть пустым"); + } + if (film.getDescription() == null || film.getDescription().isBlank()) { + throw new BadDataException("Описание не может быть пустым"); + } + if (film.getName() == null || film.getName().isBlank()) { + throw new BadDataException("Имя не может быть пустым"); + } + if (film.getDescription().length() > 200) { + throw new BadDataException("Число символов в описании не должно превышать 200"); + } + if (film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { + throw new BadDataException("Дата выхода фильма не должна быть раньше дня рождения кинематорграфа"); + } + if (film.getDuration() < 1) { + throw new BadDataException("Неверная длительность фильма"); + } + //Блок для проверки жанра и рейтинга + if (film.getGenres() != null) { + for (Genre genre : film.getGenres()) { + if (genre.getId() > 6) { + throw new BadDataException("Некорректный id жанра: " + genre.getId()); + } + } + } + if (film.getMpa() != null && film.getMpa().getId() > 5) { + throw new BadDataException("Некорректный id MPA: " + film.getMpa().getId()); + } + } + + //Метод для получения нового уникального ID для пользователя + private Integer getNextFilmId() { + String sql = "SELECT COALESCE(MAX(movie_id), 0) FROM movie"; + Long currentMaxId = jdbcTemplate.queryForObject(sql, Long.class); + return Math.toIntExact(currentMaxId + 1); + } + + // Добавляем лайк + public Film addLike(Integer filmId, Integer userId) { + if (!((checkMovie(filmId)) && checkUser(userId))) { + throw new UnknownDataException("Фильм или пользователь отсутствует"); + } + String checkLikeQuery = "SELECT COUNT(*) FROM movie_rating WHERE movie_id = ? AND user_id = ?"; + int likeCount = jdbcTemplate.queryForObject(checkLikeQuery, Integer.class, filmId, userId); + if (likeCount > 0) { + throw new UnknownDataException("Пользователь уже поставил лайк этому фильму."); + } + String insertLikeQuery = "INSERT INTO movie_rating (movie_id, user_id) VALUES (?, ?)"; + jdbcTemplate.update(insertLikeQuery, filmId, userId); + Film film = getFilmById(filmId); + if (film != null) { + if (film.getWhoLikes() == null) { + film.setWhoLikes(new HashSet<>()); + } + film.getWhoLikes().add(userId); + film.setRating(film.getRating() + 1); + } + + return film; + } + + //Метод гета фильма по id из таблицы + private Film getFilmById(Integer filmId) { + String sql = "SELECT movie_id, movie_name, movie_description, movie_release, movie_duration " + + "FROM movie WHERE movie_id = ?"; + + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { + Film film = new Film(); + film.setId(rs.getInt("movie_id")); + film.setName(rs.getString("movie_name")); + film.setDescription(rs.getString("movie_description")); + film.setReleaseDate(rs.getDate("movie_release").toLocalDate()); + java.sql.Time time = rs.getTime("movie_duration"); + long durationInSeconds = time != null ? time.getTime() / 1000 : 0; + film.setDuration(durationInSeconds); + film.setWhoLikes(new HashSet<>()); + film.setRating(0); + return film; + }, filmId); + } + + //Вывод рейтинга фильма + public Collection top(Integer count) { + log.info("Запрошен топ фильмов в количестве: " + count); + String sql = "SELECT m.movie_id, m.movie_name, m.movie_description, m.movie_release, m.movie_duration, " + + "COUNT(r.user_id) AS like_count " + + "FROM movie m " + + "LEFT JOIN movie_rating r ON m.movie_id = r.movie_id " + + "GROUP BY m.movie_id " + + "ORDER BY like_count DESC " + + "LIMIT ?"; + List films = jdbcTemplate.query(sql, (rs, rowNum) -> { + Film film = new Film(); + film.setId(rs.getInt("movie_id")); + film.setName(rs.getString("movie_name")); + film.setDescription(rs.getString("movie_description")); + film.setReleaseDate(rs.getDate("movie_release").toLocalDate()); + film.setDuration(rs.getTime("movie_duration").toLocalTime().toSecondOfDay()); + film.setRating(rs.getInt("like_count")); + film.setWhoLikes(new HashSet<>()); + + return film; + }, count); + return films; + } + + //Удаление лайка + public Film removeLike(Integer filmId, Integer userId) { + String checkLikeQuery = "SELECT COUNT(*) FROM movie_rating WHERE movie_id = ? AND user_id = ?"; + int likeCount = jdbcTemplate.queryForObject(checkLikeQuery, Integer.class, filmId, userId); + + if (likeCount == 0) { + throw new UnknownDataException("Пользователь не ставил лайк этому фильму."); + } + String deleteLikeQuery = "DELETE FROM movie_rating WHERE movie_id = ? AND user_id = ?"; + jdbcTemplate.update(deleteLikeQuery, filmId, userId); + Film film = getFilmById(filmId); + if (film != null) { + if (film.getWhoLikes() == null) { + film.setWhoLikes(new HashSet<>()); + } + film.getWhoLikes().remove(userId); + film.setRating(film.getRating() - 1); + } + return film; + } + + public Boolean checkMovie(int filmId) { + String sql = "SELECT COUNT(*) FROM movie WHERE movie_id = ?"; + int count = jdbcTemplate.queryForObject(sql, Integer.class, filmId); + return count > 0; + } + + public Boolean checkUser(int id) { + String sql = "SELECT COUNT(*) FROM users WHERE user_id = ?"; + int count = jdbcTemplate.queryForObject(sql, Integer.class, id); + return count > 0; + } + + + public List getAllGenres() { + log.info("Сработал метод запроса жанров"); + String sql = "SELECT genre_id, genre_name FROM genre"; + return jdbcTemplate.query(sql, (rs, rowNum) -> + new Genre(rs.getInt("genre_id"), rs.getString("genre_name"))); + } + + public Genre getGenre(Integer id) { + if (id > 6) { + throw new UnknownDataException("Запрашиваемый жанр отсутствует"); + } + String sql = "SELECT genre_id, genre_name FROM genre WHERE genre_id = ?"; + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> + new Genre(rs.getInt("genre_id"), rs.getString("genre_name")), id); + } + + public Collection getAllMpa() { + String sql = "SELECT mpa_id, mpa_name FROM mpa"; + return jdbcTemplate.query(sql, (rs, rowNum) -> + new Mpa(rs.getInt("mpa_id"), rs.getString("mpa_name"))); + } + + public Mpa getMpa(Integer id) { + if (id > 5) { + throw new UnknownDataException("Запрашиваемый жанр отсутствует"); + } + String sql = "SELECT mpa_id, mpa_name FROM mpa WHERE mpa_id = ?"; + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> + new Mpa(rs.getInt("mpa_id"), rs.getString("mpa_name")), id); + } + + //Гетаем фильм + public Film getFilm(Integer id) { + String sql = "SELECT movie_id, movie_name, movie_description, movie_release, movie_duration, mpa_id " + + "FROM movie WHERE movie_id = ?"; + + Film film = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { + Film result = new Film(); + result.setId(rs.getInt("movie_id")); + result.setName(rs.getString("movie_name")); + result.setDescription(rs.getString("movie_description")); + result.setReleaseDate(rs.getDate("movie_release").toLocalDate()); + result.setDuration(rs.getTime("movie_duration").toLocalTime().toSecondOfDay()); + + int mpaId = rs.getInt("mpa_id"); + if (mpaId != 0) { + result.setMpa(getMpa(mpaId)); + } + + return result; + }, id); + + if (film == null) { + throw new UnknownDataException("Фильм с ID " + id + " не найден."); + } + film.setGenres(getGenresByFilmId(id)); + return film; + } + + //Вытаскиваем жанры по id фильма + private Collection getGenresByFilmId(int filmId) { + String sql = "SELECT g.genre_id, g.genre_name " + + "FROM movie_genre mg " + + "JOIN genre g ON mg.genre_id = g.genre_id " + + "WHERE mg.movie_id = ?"; + Set genres = new HashSet<>(jdbcTemplate.query(sql, (rs, rowNum) -> + new Genre(rs.getInt("genre_id"), rs.getString("genre_name")), filmId)); + return genres.isEmpty() ? Collections.emptyList() : genres; + } + +} 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 index 9269bf3..41a8471 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java @@ -15,6 +15,7 @@ @Component @Slf4j + public class InMemoryUserStorage implements UserStorage { private final Map users = new HashMap<>(); diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorage.java new file mode 100644 index 0000000..36ef6fb --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorage.java @@ -0,0 +1,242 @@ +package ru.yandex.practicum.filmorate.storage.user; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Repository; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.exceptions.BadDataException; +import ru.yandex.practicum.filmorate.exceptions.UnknownDataException; +import ru.yandex.practicum.filmorate.model.User; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +@Repository +@Qualifier("userDbStorage") +@Component +@Slf4j +@Primary +public class UserDbStorage implements UserStorage { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public UserDbStorage(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + //Получение всех пользователей + @Override + public Collection findAllUsers() { + log.info("findAllUsers"); + String sql = "SELECT user_id, user_name, user_email, user_birthdate, user_login FROM users"; + return jdbcTemplate.query(sql, new RowMapper() { + @Override + public User mapRow(java.sql.ResultSet rs, int rowNum) throws java.sql.SQLException { + User user = new User(); + user.setId(rs.getInt("user_id")); + user.setName(rs.getString("user_name")); + user.setEmail(rs.getString("user_email")); + user.setBirthday(rs.getDate("user_birthdate").toLocalDate()); + user.setLogin(rs.getString("user_login")); + return user; + } + }); + } + + //Создание пользователя + @Override + public User createUser(@RequestBody User user) { + log.info("createUser"); + validateUser(user); + user.setId(getNextUserId()); + String sql = "INSERT INTO users (user_id, user_name, user_email, user_birthdate, user_login) " + + "VALUES (?, ?, ?, ?, ?)"; + jdbcTemplate.update(sql, + user.getId(), + user.getName(), + user.getEmail(), + user.getBirthday(), + user.getLogin()); + log.info("Новый пользователь создан с ID: " + user.getId()); + return user; + } + + //Обновление пользователя + @Override + public User updateUser(@RequestBody User user) { + log.info("updateUser"); + validateUser(user); + if (!userExists(user.getId())) { + throw new UnknownDataException("Пользователь отсутствует"); + } + String sql = "UPDATE users SET user_name = ?, user_email = ?, user_birthdate = ?, user_login = ? WHERE user_id = ?"; + + jdbcTemplate.update(sql, + user.getName(), + user.getEmail(), + user.getBirthday(), + user.getLogin(), + user.getId()); + + log.info("Пользователь с ID " + user.getId() + " обновлён"); + return user; + } + + //Метод для получения нового уникального ID для пользователя + private Integer getNextUserId() { + log.info("getNextUserId"); + String sql = "SELECT COALESCE(MAX(user_id), 0) FROM users"; + Long currentMaxId = jdbcTemplate.queryForObject(sql, Long.class); + return Math.toIntExact(currentMaxId + 1); + } + + //Метод проверки создания пользователя + public void validateUser(User user) { + log.info("validateUser"); + if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { + throw new BadDataException("Логин не может быть пустым или содержать пробелы"); + } + if (user.getName() == null || user.getName().isBlank()) { + user.setName(user.getLogin()); + } + if (user.getEmail() == null || user.getEmail().isBlank()) { + throw new BadDataException("Адрес электронной почты не может быть пустым"); + } + if (!user.getEmail().contains("@")) { + throw new BadDataException("Неверный адрес электронной почты"); + } + if (user.getBirthday().isAfter(LocalDate.now())) { + throw new BadDataException("Дата рождения в будущем"); + } + } + + //Метод проверки существования id пользователя в базе данных. Если есть - вернёт true + private boolean userExists(int userId) { + log.info("userExist"); + String sql = "SELECT COUNT(*) FROM users WHERE user_id = ?"; + int count = jdbcTemplate.queryForObject(sql, Integer.class, userId); + return count > 0; + } + + public boolean checkFriendsAvalaibility(Integer id, Integer friendId) { + log.info("checkFriendAvailability"); + String sql = "SELECT COUNT(*) FROM users WHERE user_id IN (?, ?)"; + Integer count = jdbcTemplate.queryForObject(sql, Integer.class, id, friendId); + return count != null && count == 2; + } + + public User addFriend(Integer id, Integer friendId) { + log.info("addFriend"); + if (!checkFriendsAvalaibility(id, friendId)) { + throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно добавить в друзья."); + } + String checkExistenceSql = "SELECT COUNT(*) FROM user_friendlist WHERE user_id = ? AND friend_id = ?"; + Integer count = jdbcTemplate.queryForObject(checkExistenceSql, Integer.class, id, friendId); + if (count != null && count > 0) { + throw new UnknownDataException("Дружба уже существует."); + } + + String sqlInsert1 = "INSERT INTO user_friendlist (user_id, friend_id) VALUES (?, ?)"; + jdbcTemplate.update(sqlInsert1, id, friendId); + return getUserWithFriends(id); + } + + private User getUserWithFriends(Integer userId) { + log.info("getUserwithFriends"); + String sqlUser = "SELECT * FROM users WHERE user_id = ?"; + User user = jdbcTemplate.queryForObject(sqlUser, this::mapRowToUser, userId); + String sqlFriends = "SELECT friend_id FROM user_friendlist WHERE user_id = ?"; + Set friendIds = new HashSet<>(jdbcTemplate.queryForList(sqlFriends, Integer.class, userId)); + Set friends = new HashSet<>(); + for (Integer friendId : friendIds) { + User friend = getUserById(friendId); + friends.add(friend); + } + user.setFriends(friends); + return user; + } + + public User getUserById(Integer id) { + log.info("getUserById"); + String sql = "SELECT * FROM users WHERE user_id = ?"; + return jdbcTemplate.queryForObject(sql, this::mapRowToUser, id); + } + + private User mapRowToUser(ResultSet rs, int rowNum) throws SQLException { + log.info("mapRowTOUser"); + User user = new User(); + user.setId(rs.getInt("user_id")); + user.setEmail(rs.getString("user_email")); + user.setLogin(rs.getString("user_login")); + user.setName(rs.getString("user_name")); + user.setBirthday(rs.getDate("user_birthdate").toLocalDate()); + return user; + } + + //Удаление из друзей. Боже, надо было делать сразу с рассчётом на это. Столько лишних переписываний + public User removeFriend(Integer id, Integer friendId) { + log.info("removeFriend " + friendId + " у пользователя " + id); + if (!checkFriendsAvalaibility(id, friendId)) { + throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно удалить из друзей."); + } + String sqlDelete = "DELETE FROM user_friendlist WHERE user_id = ? AND friend_id = ?"; + jdbcTemplate.update(sqlDelete, id, friendId); + return getUserWithFriends(id); + } + + //Общие друзья + public Collection getCommonFriends(Integer id, Integer friendId) { + log.info("getCommonFriends"); + if (!checkFriendsAvalaibility(id, friendId)) { + throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список общих друзей."); + } + String sqlFriendsOfFirstUser = "SELECT friend_id FROM user_friendlist WHERE user_id = ?"; + Set friendsOfFirstUser = new HashSet<>(jdbcTemplate.queryForList(sqlFriendsOfFirstUser, Integer.class, id)); + String sqlFriendsOfSecondUser = "SELECT friend_id FROM user_friendlist WHERE user_id = ?"; + Set friendsOfSecondUser = new HashSet<>(jdbcTemplate.queryForList(sqlFriendsOfSecondUser, Integer.class, friendId)); + friendsOfFirstUser.retainAll(friendsOfSecondUser); + if (friendsOfFirstUser.isEmpty()) { + return new ArrayList<>(); + } + String sqlCommonFriends = String.format( + "SELECT * FROM users WHERE user_id IN (%s)", + friendsOfFirstUser.stream().map(String::valueOf).collect(Collectors.joining(", ")) + ); + return jdbcTemplate.query(sqlCommonFriends, this::mapRowToUser); + } + + public Collection getFriends(Integer id) { + log.info("getFriends"); + String sqlCheckUser = "SELECT COUNT(*) FROM users WHERE user_id = ?"; + int userCount = jdbcTemplate.queryForObject(sqlCheckUser, Integer.class, id); + if (userCount == 0) { + throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список друзей"); + } + String sqlFriendsIds = "SELECT friend_id FROM user_friendlist WHERE user_id = ?"; + List friendsIds = jdbcTemplate.queryForList(sqlFriendsIds, Integer.class, id); + if (friendsIds.isEmpty()) { + return new ArrayList<>(); + } + String sqlFriendsData = String.format( + "SELECT * FROM users WHERE user_id IN (%s)", + friendsIds.stream().map(String::valueOf).collect(Collectors.joining(", ")) + ); + List friends = jdbcTemplate.query(sqlFriendsData, this::mapRowToUser); + for (User friend : friends) { + Set friendSet = new HashSet<>(getUserWithFriends(friend.getId()).getFriends()); + friend.setFriends(friendSet); + } + return friends; + } + +} \ No newline at end of file diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..691a082 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,9 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + username: sa + password: + jpa: + hibernate: + ddl-auto: none diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0e3dc5c..23272ef 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,6 +5,26 @@ logbook: type: request match: - path: /api/sensitive + logging: level: - org.zalando.logbook: WARN \ No newline at end of file + org.zalando.logbook: WARN + +spring: + sql: + init: + mode: ALWAYS + + datasource: + url: jdbc:h2:file:./db/filmorate + driver-class-name: org.h2.Driver + username: sa + password: password + + h2: + console: + enabled: true + + jpa: + hibernate: + ddl-auto: update diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..9130d69 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,63 @@ +CREATE TABLE IF NOT EXISTS movie ( + movie_id INTEGER PRIMARY KEY, + movie_name VARCHAR, + movie_description VARCHAR, + movie_release DATE, + movie_duration TIME, + genre_id INTEGER, + mpa_id INTEGER +); + +CREATE TABLE IF NOT EXISTS genre ( + genre_id INTEGER PRIMARY KEY, + genre_name VARCHAR +); + +CREATE TABLE IF NOT EXISTS movie_genre ( + movie_id INTEGER, + genre_id INTEGER, + PRIMARY KEY (movie_id, genre_id), + FOREIGN KEY (movie_id) REFERENCES movie(movie_id) ON DELETE CASCADE, + FOREIGN KEY (genre_id) REFERENCES genre(genre_id) ON DELETE CASCADE +); + +MERGE INTO genre (genre_id, genre_name) KEY(genre_id) VALUES + (1, 'Комедия'), + (2, 'Драма'), + (3, 'Мультфильм'), + (4, 'Триллер'), + (5, 'Документальный'), + (6, 'Боевик'); + +CREATE TABLE IF NOT EXISTS mpa ( + mpa_id INTEGER PRIMARY KEY, + mpa_name VARCHAR +); + +MERGE INTO mpa (mpa_id, mpa_name) KEY(mpa_id) VALUES + (1, 'G'), + (2, 'PG'), + (3, 'PG-13'), + (4, 'R'), + (5, 'NC-17'); + + +CREATE TABLE IF NOT EXISTS users ( + user_id INTEGER PRIMARY KEY, + user_name VARCHAR, + user_email VARCHAR, + user_birthdate DATE, + user_login VARCHAR +); + +CREATE TABLE IF NOT EXISTS user_friendlist ( + user_id INTEGER, + friend_id INTEGER, + PRIMARY KEY (user_id, friend_id) +); + +CREATE TABLE IF NOT EXISTS movie_rating ( + movie_id INTEGER, + user_id INTEGER, + PRIMARY KEY (movie_id, user_id) +); diff --git a/src/test/java/ru/yandex/practicum/filmorate/DataBaseStorageTests.java b/src/test/java/ru/yandex/practicum/filmorate/DataBaseStorageTests.java new file mode 100644 index 0000000..3a3bf9b --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/DataBaseStorageTests.java @@ -0,0 +1,202 @@ +package ru.yandex.practicum.filmorate; + +import lombok.RequiredArgsConstructor; +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.Film; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.film.FilmDbStorage; +import ru.yandex.practicum.filmorate.storage.user.UserDbStorage; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@JdbcTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) +@Import({UserDbStorage.class, FilmDbStorage.class}) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class DataBaseStorageTests { + private final UserDbStorage userStorage; + private final FilmDbStorage filmStorage; + + + @Test + void createAndFindUserInDataBase() { + User newUser = new User(); + newUser.setEmail("test@qwerty.com"); + newUser.setLogin("testuser"); + newUser.setName("Test User"); + newUser.setBirthday(LocalDate.of(1990, 1, 1)); + + User createdUser = userStorage.createUser(newUser); + Optional retrievedUser = Optional.ofNullable(userStorage.getUserById(createdUser.getId())); + + assertThat(retrievedUser).isPresent(); + assertThat(retrievedUser.get().getEmail()).isEqualTo("test@qwerty.com"); + } + + @Test + void createUserUpdateUserInDatabase() { + User newUser = new User(); + newUser.setEmail("test@drhdhr.com"); + newUser.setLogin("test"); + newUser.setName("Test Test"); + newUser.setBirthday(LocalDate.of(1995, 5, 15)); + + User createdUser = userStorage.createUser(newUser); + createdUser.setName("New Test"); + User updatedUser = userStorage.updateUser(createdUser); + + assertThat(updatedUser.getName()).isEqualTo("New Test"); + } + + @Test + void createAndFindAllUsers() { + User user1 = new User(); + user1.setEmail("user1@dhrhd.com"); + user1.setLogin("user1"); + user1.setName("User One"); + user1.setBirthday(LocalDate.of(1990, 1, 1)); + + User user2 = new User(); + user2.setEmail("user2@hhdhdr.com"); + user2.setLogin("user2"); + user2.setName("User Two"); + user2.setBirthday(LocalDate.of(1992, 2, 2)); + + userStorage.createUser(user1); + userStorage.createUser(user2); + + Collection users = userStorage.findAllUsers(); + assertEquals(2, users.size()); + } + + @Test + void getCommonFriendsFromDatabase() { + User user1 = new User(); + user1.setEmail("user1@tsw.com"); + user1.setLogin("user1"); + user1.setName("User One"); + user1.setBirthday(LocalDate.of(1990, 1, 1)); + + User user2 = new User(); + user2.setEmail("user2@afawf.com"); + user2.setLogin("user2"); + user2.setName("User Two"); + user2.setBirthday(LocalDate.of(1992, 2, 2)); + + User user3 = new User(); + user3.setEmail("user3@blabla.com"); + user3.setLogin("user3"); + user3.setName("User Three"); + user3.setBirthday(LocalDate.of(1993, 3, 3)); + + User createdUser1 = userStorage.createUser(user1); + User createdUser2 = userStorage.createUser(user2); + User createdUser3 = userStorage.createUser(user3); + + userStorage.addFriend(createdUser1.getId(), createdUser3.getId()); + userStorage.addFriend(createdUser2.getId(), createdUser3.getId()); + + Collection commonFriends = userStorage.getCommonFriends(createdUser1.getId(), createdUser2.getId()); + assertEquals(1, commonFriends.size()); + assertTrue(commonFriends.contains(createdUser3)); + } + + @Test + void getFilmFromDatabase() { + Film newFilm = new Film(); + newFilm.setName("H8full 8"); + newFilm.setDescription("Western Detective"); + newFilm.setReleaseDate(LocalDate.of(2016, 1, 1)); + newFilm.setDuration(167); + + Film createdFilm = filmStorage.createFilm(newFilm); + Optional retrievedFilm = Optional.ofNullable(filmStorage.getFilm(createdFilm.getId())); + + assertThat(retrievedFilm).isPresent(); + assertThat(retrievedFilm.get().getName()).isEqualTo("H8full 8"); + } + + @Test + void updateFilmInDataBase() { + Film newFilm = new Film(); + newFilm.setName("Avengers: Infinity War"); + newFilm.setDescription("Popcorn"); + newFilm.setReleaseDate(LocalDate.of(2021, 1, 1)); + newFilm.setDuration(150); + + Film createdFilm = filmStorage.createFilm(newFilm); + createdFilm.setName("Avengers: Final"); + Film updatedFilm = filmStorage.updateFilm(createdFilm); + + assertThat(updatedFilm.getName()).isEqualTo("Avengers: Final"); + } + + @Test + void findAllFilmsInCollection() { + Film film1 = new Film(); + film1.setName("Movie47"); + film1.setDescription("doubtful"); + film1.setReleaseDate(LocalDate.of(2020, 1, 1)); + film1.setDuration(100); + + Film film2 = new Film(); + film2.setName("Movie48"); + film2.setDescription("better"); + film2.setReleaseDate(LocalDate.of(2019, 2, 2)); + film2.setDuration(110); + + filmStorage.createFilm(film1); + filmStorage.createFilm(film2); + + Collection films = filmStorage.findAllFilms(); + assertEquals(2, films.size()); + } + + @Test + void addLikeAndgetTopFilmsFromDatabase() { + Film film1 = new Film(); + film1.setName("XXX"); + film1.setDescription("XXX"); + film1.setReleaseDate(LocalDate.of(2021, 1, 1)); + film1.setDuration(120); + + Film film2 = new Film(); + film2.setName("Sinister"); + film2.setDescription("Description 2"); + film2.setReleaseDate(LocalDate.of(2021, 2, 2)); + film2.setDuration(90); + + filmStorage.createFilm(film1); + filmStorage.createFilm(film2); + + User newUser = new User(); + newUser.setEmail("test@qwerty.com"); + newUser.setLogin("testuser"); + newUser.setName("Test User1"); + newUser.setBirthday(LocalDate.of(1990, 1, 1)); + User createdUser = userStorage.createUser(newUser); + User newUser2 = new User(); + newUser.setEmail("test@fawf.com"); + newUser.setLogin("testuser"); + newUser.setName("Test User2"); + newUser.setBirthday(LocalDate.of(1992, 5, 6)); + User createdUser2 = userStorage.createUser(newUser); + + filmStorage.addLike(film2.getId(), 1); + filmStorage.addLike(film2.getId(), 2); + + Collection topFilms = filmStorage.top(1); + assertEquals(1, topFilms.size()); + assertThat(topFilms.iterator().next().getName()).isEqualTo("Sinister"); + } +} 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 10075eb..0000000 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java +++ /dev/null @@ -1,132 +0,0 @@ -package ru.yandex.practicum.filmorate; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import ru.yandex.practicum.filmorate.exceptions.BadDataException; -import ru.yandex.practicum.filmorate.model.Film; -import ru.yandex.practicum.filmorate.model.User; -import ru.yandex.practicum.filmorate.storage.film.InMemoryFilmStorage; -import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -@SpringBootTest -class FilmorateApplicationTests { - - @Test - void contextLoads() { - } - - @Test - void correctFilm() { - InMemoryFilmStorage master = new InMemoryFilmStorage(); - Film film = new Film(1, "The Hateful eight", "Western", LocalDate.of(2015, 12, 18), 187, 0, new HashSet<>()); - master.createFilm(film); - Collection filmsCollect = master.findAllFilms(); - ArrayList testFilms = new ArrayList<>(filmsCollect); - Assertions.assertEquals(film, testFilms.get(0)); - } - - @Test - void incorretDateFilm() { - InMemoryFilmStorage master = new InMemoryFilmStorage(); - Film film = new Film(0, "The Hateful eight", "Western", LocalDate.of(1890, 12, 18), 187, 0, new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createFilm(film), - "Дата выхода фильма не должна быть раньше дня рождения кинематорграфа" - ); - - } - - @Test - void incorretFilmNaming() { - InMemoryFilmStorage master = new InMemoryFilmStorage(); - Film film = new Film(0, " ", "Western", LocalDate.of(2015, 12, 18), 187, 0, new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createFilm(film), - "Название не может быть пустым" - ); - } - - @Test - void incorretFilmDuration() { - InMemoryFilmStorage master = new InMemoryFilmStorage(); - Film film = new Film(0, "The Hateful Eight", "Western", LocalDate.of(2015, 12, 18), -187, 0, new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createFilm(film), - "Неверная длительность фильма" - ); - } - - @Test - void correctUserTest() { - InMemoryUserStorage master = new InMemoryUserStorage(); - User user = new User(0, "JD@post.com", "JohnDoe", "John", LocalDate.of(1980, 12, 12), new HashSet<>()); - master.createUser(user); - Collection usersCollect = master.findAllUsers(); - ArrayList testFilms = new ArrayList<>(usersCollect); - Assertions.assertEquals(user, testFilms.get(0)); - } - - @Test - void incorrectMailUserTest() { - InMemoryUserStorage master = new InMemoryUserStorage(); - User user = new User(0, "JDpost.com", "JohnDoe", "John", LocalDate.of(1980, 12, 12), new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createUser(user), - "Неверный адрес электронной почты" - ); - } - - @Test - void incorrectEmptyMailUserTest() { - InMemoryUserStorage master = new InMemoryUserStorage(); - User user = new User(0, " ", "JohnDoe", "John", LocalDate.of(1980, 12, 12), new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createUser(user), - "Адрес электронной почты не может быть пустым" - ); - } - - @Test - void incorrectLoginUserTest() { - InMemoryUserStorage master = new InMemoryUserStorage(); - User user = new User(0, "JD@post.com", "John Doe", "John", LocalDate.of(1980, 12, 12), new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createUser(user), - "Логин не может быть пустым или содержать пробелы" - ); - } - - @Test - void incorrectNameUserTest() { - InMemoryUserStorage master = new InMemoryUserStorage(); - User user = new User(0, "JD@post.com", "JohnDoe", "", LocalDate.of(1980, 12, 12), new HashSet<>()); - master.createUser(user); - Assertions.assertEquals("JohnDoe", user.getName()); - } - - @Test - void incorrectBirthdateUserTest() { - InMemoryUserStorage master = new InMemoryUserStorage(); - User user = new User(0, "JD@post.com", "JohnDoe", "John", LocalDate.of(2980, 12, 12), new HashSet<>()); - BadDataException thrown = assertThrows( - BadDataException.class, - () -> master.createUser(user), - "Дата рождения в будущем" - ); - } -} -