diff --git a/FilmorateDatabase.png b/FilmorateDatabase.png index fb11672..82b8999 100644 Binary files a/FilmorateDatabase.png and b/FilmorateDatabase.png differ diff --git a/README.md b/README.md index 922d267..986bb4a 100644 --- a/README.md +++ b/README.md @@ -48,16 +48,6 @@ user_id integer (Первичный ключ): Идентификатор пол friend_id integer (Внешний ключ): Идентификатор другого пользователя(потенциального друга). -status_id integer (Внешний ключ): Идентификатор статуса дружбы между пользователями. - -## Таблица friendship_status. - -### Хранит информацию о статусах дружбы. - -status_id integer (Первичный ключ): Идентификатор статуса дружбы между пользователями. - -status_name varchar: Наименование статуса. - ## Таблица film_likes. ### Хранит информацию о лайках, которые пользователи поставили фильмам. @@ -66,21 +56,21 @@ user_id integer (Первичный ключ): Идентификатор пол film_id integer (Внешний ключ): Идентификатор фильма. -## Таблица film_ganres. +## Таблица film_genres. ### Хранит информацию о жанре фильма. film_id integer (Первичный ключ): Идентификатор фильма. -ganre_id integer (Внешний ключ): Идентификатор жанра. +genre_id integer (Внешний ключ): Идентификатор жанра. -## Таблица ganre. +## Таблица genre. ### Хранит информацию о жанрах. -ganre_id integer (Первичный ключ): Идентификатор жанра. +genre_id integer (Первичный ключ): Идентификатор жанра. -ganre_name varchar: Наименование жанра. +genre_name varchar: Наименование жанра. ## Таблица mpa. diff --git a/pom.xml b/pom.xml index 9522a8a..937f887 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ 21 + org.springframework.boot spring-boot-starter-web @@ -26,17 +27,31 @@ org.springframework.boot spring-boot-starter-validation - - org.projectlombok - lombok - provided + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-starter-jdbc org.springframework.boot spring-boot-starter-test test + + + org.projectlombok + lombok + provided + + diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java index 843905e..0166034 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java +++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java @@ -3,10 +3,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + @SpringBootApplication public class FilmorateApplication { public static void main(String[] args) { SpringApplication.run(FilmorateApplication.class, args); + } + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java index 0b04798..9c4f782 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.HttpStatus; import ru.yandex.practicum.filmorate.exception.DataNotFoundException; import ru.yandex.practicum.filmorate.model.ErrorResponse; @@ -32,5 +33,12 @@ public ErrorResponse handleNotFoundException(final DataNotFoundException e) { } -} + @ExceptionHandler + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse emptyData(final EmptyResultDataAccessException e) { + log.info("404 {}", e.getMessage()); + return new ErrorResponse(e.getMessage()); + + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java index 0f35e51..e5ba330 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -2,6 +2,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; @@ -12,7 +13,6 @@ import java.util.Collection; -import java.util.List; @RestController @@ -28,9 +28,14 @@ public Collection getFilms() { return filmService.getFilms(); } + @GetMapping(path = "/films/{id}") + public Film getFilmById(@PathVariable("id") int id) { + return filmService.getFilmById(id); + } + @PostMapping(path = "/films") - public Film createFilm(@Valid @RequestBody Film film) throws ValidationException { + public Film createFilm(@Valid @RequestBody Film film) throws ValidationException, DataNotFoundException { return filmService.createFilm(film); } @@ -53,9 +58,9 @@ public void deleteLike(@PathVariable("id") int id, @PathVariable("userId") int u } @GetMapping(path = "/films/popular") - public List getPopularFilms(@RequestParam(defaultValue = "10") int count) { + public Collection getPopularFilms(@RequestParam(defaultValue = "10") int count) { return filmService.getPopularFilms(count); } -} +} \ No newline at end of file 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..ec90f27 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java @@ -0,0 +1,30 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.service.GenreService; + +import java.util.Collection; + +@RestController +@RequestMapping +@Slf4j +@RequiredArgsConstructor +public class GenreController { + private final GenreService genreService; + + @GetMapping(path = "/genres/{id}") + public Genre getGenreById(@PathVariable("id") int id) throws DataNotFoundException { + return genreService.getGenreById(id); + + } + + @GetMapping(path = "/genres") + public Collection getGenres() { + return genreService.getGenres(); + + } +} 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..a6c7f8b --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java @@ -0,0 +1,34 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.service.MpaService; + +import java.util.Collection; + +@RestController +@RequestMapping +@Slf4j +@RequiredArgsConstructor +public class MpaController { + + private final MpaService mpaService; + + @GetMapping(path = "/mpa/{id}") + public Mpa getMpaById(@PathVariable("id") int id) throws DataNotFoundException { + return mpaService.getById(id); + + } + + @GetMapping(path = "/mpa") + public Collection getAllMpa() { + return mpaService.getAllMpa(); + + } +} 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 7b3d42d..25db820 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -59,4 +59,4 @@ public List getUserFriends(@PathVariable("id") int id) throws DataNotFound public List getCommonFriends(@PathVariable("id") int id, @PathVariable("otherId") int otherId) throws DataNotFoundException { return userService.getCommonFriends(id, otherId); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java index 589a8d6..6050fca 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java @@ -4,4 +4,4 @@ public class DataNotFoundException extends Exception { public DataNotFoundException(String message) { super(message); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java index fd0100a..25158c8 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java @@ -4,4 +4,4 @@ public class ValidationException extends Exception { public ValidationException(String message) { super(message); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java index aea38a7..88afe64 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java @@ -17,4 +17,4 @@ public String toString() { return "error= " + error; } -} +} \ 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 640bdbf..2b11c3d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -1,27 +1,29 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; +import jakarta.validation.constraints.NotNull; +import lombok.*; import java.time.LocalDate; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; + +import java.util.LinkedHashSet; @Getter @Setter -@RequiredArgsConstructor +@AllArgsConstructor +@NoArgsConstructor +@ToString +@EqualsAndHashCode(of = "id") public class Film { private Integer id; private String name; private String description; + @NotNull private LocalDate releaseDate; private Long duration; + @NotNull private Mpa mpa; - private Set likes = new HashSet<>(); - private Collection genres; + private LinkedHashSet genres; -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/FriendshipStatus.java b/src/main/java/ru/yandex/practicum/filmorate/model/FriendshipStatus.java deleted file mode 100644 index 2afafc6..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/model/FriendshipStatus.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.yandex.practicum.filmorate.model; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@NoArgsConstructor -public class FriendshipStatus { - public Long id; - public String name; -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java index a298ddf..b900188 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java @@ -1,14 +1,14 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; @Setter @Getter @NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode public class Genre { - private int id; + private Integer id; private String name; -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java index 146e154..58150b0 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java @@ -8,7 +8,7 @@ @Setter @NoArgsConstructor public class Mpa { - private int id; + private Integer id; private String name; -} +} \ No newline at end of file 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 75662c3..62e3e76 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -1,8 +1,6 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import java.time.LocalDate; import java.util.HashSet; @@ -10,7 +8,10 @@ @Getter @Setter +@AllArgsConstructor @NoArgsConstructor +@ToString +@EqualsAndHashCode public class User { private Set friendsList = new HashSet<>(); private Integer id; @@ -19,4 +20,4 @@ public class User { private String name; private LocalDate birthday; -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/FilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/FilmRepository.java new file mode 100644 index 0000000..d4f2678 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/FilmRepository.java @@ -0,0 +1,26 @@ +package ru.yandex.practicum.filmorate.repository; + +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; + +public interface FilmRepository { + Film save(Film film); + + Film update(Film film); + + Film getById(int id); + + Collection getFilms(); + + void setGenre(Film film); + + + void getGenre(Film film); + + void addLike(int filmId, int userId); + + void deleteLike(int filmId, int userId); + + Collection getPopularFilms(int count); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/GenreRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/GenreRepository.java new file mode 100644 index 0000000..ea0e9e1 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/GenreRepository.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.repository; + +import ru.yandex.practicum.filmorate.model.Genre; + +import java.util.Collection; + +public interface GenreRepository { + Genre getById(int id); + + Collection getAll(); + + boolean genreExist(int id); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcFilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcFilmRepository.java new file mode 100644 index 0000000..07a36ae --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcFilmRepository.java @@ -0,0 +1,154 @@ +package ru.yandex.practicum.filmorate.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.repository.mappers.FilmRowMapper; +import ru.yandex.practicum.filmorate.repository.mappers.GenreRowMapper; + +import java.util.*; + +@Repository +@RequiredArgsConstructor +public class JdbcFilmRepository implements FilmRepository { + private final NamedParameterJdbcOperations jdbc; + private final FilmRowMapper mapper; + private final GenreRowMapper genreRowMapper; + + + @Override + public Film save(Film film) { + + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("name", film.getName()); + params.addValue("description", film.getDescription()); + params.addValue("release_date", film.getReleaseDate()); + params.addValue("duration", film.getDuration()); + params.addValue("mpa_id", film.getMpa().getId()); + + jdbc.update("INSERT INTO FILMS (NAME, DESCRIPTION, RELEASE_DATE, DURATION, MPA_ID) VALUES(:name,:description ,:release_date ,:duration ,:mpa_id)", params, keyHolder); + + film.setId(keyHolder.getKeyAs(Integer.class)); + if (film.getGenres() != null) { + setGenre(film); + } + return film; + } + + @Override + public Film update(Film film) { + + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("name", film.getName()); + params.addValue("description", film.getDescription()); + params.addValue("release_date", film.getReleaseDate()); + params.addValue("duration", film.getDuration()); + params.addValue("mpa_id", film.getMpa().getId()); + params.addValue("film_id", film.getId()); + jdbc.update("DELETE FROM FILM_GENRES WHERE FILM_ID=:film_id", params, keyHolder); + + if (film.getGenres() != null) { + setGenre(film); + } + film.setGenres(new LinkedHashSet<>()); + jdbc.update("UPDATE FILMS SET NAME=:name, DESCRIPTION=:description, RELEASE_DATE=:release_date, DURATION=:duration, MPA_ID=:mpa_id WHERE FILM_ID=:film_id", params, keyHolder); + + + return film; + } + + + @Override + public Film getById(int id) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("film_id", id); + Film film = jdbc.queryForObject("SELECT f.*, m.MPA_NAME FROM FILMS f JOIN MPA m ON f.MPA_ID = m.MPA_ID WHERE f.FILM_ID = :film_id", params, mapper); + if (film.getGenres() != null) { + getGenre(film); + } + return film; + + + } + + @Override + public Collection getFilms() { + final String query = "SELECT f.*, m.MPA_NAME FROM FILMS f JOIN MPA m ON f.MPA_ID = m.MPA_ID"; + Collection films = jdbc.query(query, mapper); + + + for (Film film : films) { + + getGenre(film); + + } + return films; + + } + + @Override + public void setGenre(Film film) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("film_id", film.getId()); + Set duplicateGenres = new HashSet<>(); + + if (film.getGenres() != null) { + Collection genres = film.getGenres(); + for (Genre genre : genres) { + if (!duplicateGenres.contains(genre)) { + params.addValue("genre_id", genre.getId()); + jdbc.update("INSERT INTO FILM_GENRES (FILM_ID, GENRE_ID) VALUES(:film_id, :genre_id);", params, keyHolder); + duplicateGenres.add(genre); + } + } + } + + } + + @Override + public void getGenre(Film film) { + MapSqlParameterSource params = new MapSqlParameterSource(); + + params.addValue("film_id", film.getId()); + + Collection genres = jdbc.query("SELECT g.GENRE_ID AS id ,g.GENRE_NAME AS name FROM FILM_GENRES fg JOIN GENRES g ON fg.GENRE_ID = g.GENRE_ID WHERE fg.FILM_ID = :film_id", params, genreRowMapper); + LinkedHashSet filmGenres = new LinkedHashSet<>(genres); + film.setGenres(filmGenres); + } + + @Override + public void addLike(int filmId, int userId) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + params.addValue("film_id", filmId); + jdbc.update("INSERT INTO FILM_LIKES (USER_ID, FILM_ID) VALUES(:user_id, :film_id)", params, keyHolder); + } + + @Override + public void deleteLike(int filmId, int userId) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + params.addValue("film_id", filmId); + jdbc.update("DELETE FROM FILM_LIKES WHERE FILM_ID=:film_id AND USER_ID=:user_id;", params, keyHolder); + } + + @Override + public Collection getPopularFilms(int count) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("limit", count); + Collection popularFilms = jdbc.query("SELECT f.*, m.MPA_NAME , COUNT(fl.USER_ID) AS likes FROM FILMS f JOIN FILM_LIKES fl ON fl.FILM_ID = f.FILM_ID JOIN MPA m ON m.MPA_ID = f.MPA_ID GROUP BY f.FILM_ID ORDER BY COUNT(fl.USER_ID) DESC LIMIT :limit", params, mapper); + return popularFilms; + + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcGenreRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcGenreRepository.java new file mode 100644 index 0000000..42215f9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcGenreRepository.java @@ -0,0 +1,40 @@ +package ru.yandex.practicum.filmorate.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.repository.mappers.GenreRowMapper; + +import java.util.Collection; + +@Repository +@RequiredArgsConstructor +public class JdbcGenreRepository implements GenreRepository { + + private final NamedParameterJdbcOperations jdbc; + private final GenreRowMapper mapper; + + @Override + public Genre getById(int id) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("genre_id", id); + return jdbc.queryForObject("SELECT * FROM GENRES WHERE GENRE_ID = :genre_id", params, mapper); + } + + @Override + public Collection getAll() { + final String query = "select * FROM GENRES ORDER BY GENRE_ID "; + return jdbc.query(query, mapper); + } + + @Override + public boolean genreExist(int id) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("genre_id", id); + return Boolean.TRUE.equals(jdbc.queryForObject("SELECT EXISTS(SELECT 1 FROM GENRES WHERE genre_id =:genre_id)", params, boolean.class)); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcMpaRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcMpaRepository.java new file mode 100644 index 0000000..6bcd0b2 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcMpaRepository.java @@ -0,0 +1,37 @@ +package ru.yandex.practicum.filmorate.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.repository.mappers.MpaRowMapper; + +import java.util.Collection; + +@Repository +@RequiredArgsConstructor +public class JdbcMpaRepository implements MpaRepository { + private final NamedParameterJdbcOperations jdbc; + private final MpaRowMapper mapper; + + @Override + public Mpa getById(int id) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("mpa_id", id); + return jdbc.queryForObject("SELECT * FROM MPA WHERE MPA_ID = :mpa_id", params, mapper); + } + + @Override + public Collection getAll() { + final String query = "select * FROM MPA ORDER BY MPA_ID"; + return jdbc.query(query, mapper); + } + + @Override + public boolean mpaExist(int id) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("mpa_id", id); + return Boolean.TRUE.equals(jdbc.queryForObject("SELECT EXISTS(SELECT 1 FROM MPA WHERE mpa_id =:mpa_id)", params, boolean.class)); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcUserRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcUserRepository.java new file mode 100644 index 0000000..b3ea4b7 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcUserRepository.java @@ -0,0 +1,93 @@ +package ru.yandex.practicum.filmorate.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class JdbcUserRepository implements UserRepository { + + private final NamedParameterJdbcOperations jdbc; + private final RowMapper mapper; + + @Override + public User save(User user) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("login", user.getLogin()); + params.addValue("email", user.getEmail()); + params.addValue("user_name", user.getName()); + params.addValue("birthday", user.getBirthday()); + jdbc.update("INSERT INTO USERS (LOGIN, EMAIL, USER_NAME, BIRTHDAY) VALUES(:login,:email ,:user_name ,:birthday )", params, keyHolder); + user.setId(keyHolder.getKeyAs(Integer.class)); + return user; + } + + @Override + public User update(User user) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("login", user.getLogin()); + params.addValue("email", user.getEmail()); + params.addValue("user_name", user.getName()); + params.addValue("birthday", user.getBirthday()); + params.addValue("user_id", user.getId()); + jdbc.update("UPDATE PUBLIC.USERS SET LOGIN=:login, EMAIL=:email, USER_NAME=:user_name, BIRTHDAY=:birthday WHERE USER_ID=:user_id", params, keyHolder); + return user; + + + } + + @Override + public Optional getById(int userId) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + return Optional.ofNullable(jdbc.queryForObject("SELECT * FROM USERS WHERE USER_ID = :user_id", params, mapper)); + } + + @Override + public Collection getUsers() { + final String query = "select * FROM USERS"; + return jdbc.query(query, mapper); + + } + + @Override + public void addFriend(int userId, int friendId) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + params.addValue("friend_id", friendId); + jdbc.update("INSERT INTO FRIENDS(USER_ID, FRIEND_ID) VALUES(:user_id,:friend_id)", params, keyHolder); + } + + @Override + public void deleteFriend(int userId, int friendId) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + params.addValue("friend_id", friendId); + jdbc.update("DELETE FROM FRIENDS WHERE USER_ID=:user_id AND FRIEND_ID=:friend_id", params, keyHolder); + } + + @Override + public List getUserFriends(int userId) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + return jdbc.query("SELECT * FROM USERS where USER_ID IN (SELECT FRIEND_ID FROM FRIENDS WHERE USER_ID = :user_id) ", params, mapper); + + } + +} + + diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/MpaRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/MpaRepository.java new file mode 100644 index 0000000..1010256 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/MpaRepository.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.repository; + +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.util.Collection; + +public interface MpaRepository { + Mpa getById(int id); + + Collection getAll(); + + boolean mpaExist(int id); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/UserRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/UserRepository.java new file mode 100644 index 0000000..9432e17 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/UserRepository.java @@ -0,0 +1,23 @@ +package ru.yandex.practicum.filmorate.repository; + +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public interface UserRepository { + User save(User user); + + User update(User user); + + Optional getById(int userId); + + Collection getUsers(); + + void addFriend(int userId, int friendId); + + void deleteFriend(int userId, int friendId); + + List getUserFriends(int userId); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/FilmRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/FilmRowMapper.java new file mode 100644 index 0000000..b1fbe8f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/FilmRowMapper.java @@ -0,0 +1,30 @@ +package ru.yandex.practicum.filmorate.repository.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.util.LinkedHashSet; + +@Component +public class FilmRowMapper implements RowMapper { + @Override + public Film mapRow(ResultSet rs, int rowNum) throws SQLException { + Mpa mpa = new Mpa(); + mpa.setId(rs.getInt("mpa_id")); + mpa.setName(rs.getString("mpa_name")); + Film film = new Film(); + film.setId(rs.getInt("film_id")); + film.setName(rs.getString("name")); + film.setDescription(rs.getString("description")); + film.setReleaseDate(rs.getDate("release_date").toLocalDate()); + film.setDuration(rs.getLong("duration")); + film.setMpa(mpa); + film.setGenres(new LinkedHashSet<>()); + return film; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/GenreRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/GenreRowMapper.java new file mode 100644 index 0000000..7fb7dbb --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/GenreRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.repository.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 { + + public Genre mapRow(ResultSet resultSet, int rowNum) throws SQLException { + Genre genre = new Genre(); + genre.setId(resultSet.getInt("genre_id")); + genre.setName(resultSet.getString("genre_name")); + return genre; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/MpaRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/MpaRowMapper.java new file mode 100644 index 0000000..b31ed13 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/MpaRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.repository.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.getInt("mpa_id")); + mpa.setName(rs.getString("mpa_name")); + return mpa; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/UserRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/UserRowMapper.java new file mode 100644 index 0000000..f6e2182 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/UserRowMapper.java @@ -0,0 +1,22 @@ +package ru.yandex.practicum.filmorate.repository.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; + +@Component +public class UserRowMapper implements RowMapper { + @Override + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + User user = new User(); + user.setId(rs.getInt("user_id")); + user.setLogin(rs.getString("login")); + user.setEmail(rs.getString("email")); + user.setName(rs.getString("user_name")); + user.setBirthday(rs.getDate("birthday").toLocalDate()); + return user; + } +} 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 0731a2a..2d78e87 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -1,73 +1,25 @@ package ru.yandex.practicum.filmorate.service; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; import ru.yandex.practicum.filmorate.exception.DataNotFoundException; 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.storage.FilmStorage; -import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; -@Service -@RequiredArgsConstructor -@Slf4j -public class FilmService { +public interface FilmService { + Collection getFilms(); - private final FilmStorage filmStorage; - private final UserService userService; + Film getFilmById(int id); - public Collection getFilms() { - return filmStorage.getFilms(); - } + Film createFilm(Film film) throws ValidationException, DataNotFoundException; - public Film createFilm(Film film) throws ValidationException { - return filmStorage.createFilm(film); - } + Film updateFilm(Film newFilm) throws DataNotFoundException; - public Film updateFilm(Film newFilm) throws DataNotFoundException { + void addLike(int filmId, int userId) throws DataNotFoundException; - return filmStorage.updateFilm(newFilm); - } + void deleteLike(int filmId, int userId) throws DataNotFoundException; - public void addLike(int id, int userId) throws DataNotFoundException { - Film film = get(id); - User user = userService.get(userId); + Collection getPopularFilms(int count); - film.getLikes().add(userId); - log.info("add like: {}", film); - } - - public void deleteLike(int id, int userId) throws DataNotFoundException { - Film film = get(id); - User user = userService.get(userId); - - film.getLikes().remove(userId); - log.info("delete like: {}", film); - } - - public List getPopularFilms(int count) { - List popularFilms = new ArrayList<>(filmStorage.getFilms()); - popularFilms.sort(FILM_COMPARATOR.reversed()); - return popularFilms.stream().limit(count).collect(Collectors.toList()); - - } - - public static final Comparator FILM_COMPARATOR = new Comparator() { - @Override - public int compare(Film o1, Film o2) { - return o1.getLikes().size() - o2.getLikes().size(); - } - - }; - - public Film get(int filmId) throws DataNotFoundException { - return filmStorage.get(filmId); - } + Film get(int filmId) throws DataNotFoundException; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmServiceImpl.java new file mode 100644 index 0000000..3aaa927 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmServiceImpl.java @@ -0,0 +1,95 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.repository.JdbcFilmRepository; +import ru.yandex.practicum.filmorate.repository.JdbcGenreRepository; +import ru.yandex.practicum.filmorate.repository.JdbcMpaRepository; +import ru.yandex.practicum.filmorate.storage.FilmStorage; + +import java.time.LocalDate; +import java.time.Month; +import java.util.Collection; + +@Service +@RequiredArgsConstructor +@Slf4j +public class FilmServiceImpl implements FilmService { + + private final FilmStorage filmStorage; + private final JdbcFilmRepository jdbcFilmRepository; + private final JdbcGenreRepository jdbcGenreRepository; + private final JdbcMpaRepository jdbcMpaRepository; + private final UserServiceImpl userService; + + @Override + public Collection getFilms() { + return jdbcFilmRepository.getFilms(); + } + + @Override + public Film getFilmById(int id) { + return jdbcFilmRepository.getById(id); + } + + @Override + public Film createFilm(Film film) throws ValidationException, DataNotFoundException { + LocalDate date = LocalDate.of(1895, Month.DECEMBER, 28); + if (film.getName() == null || film.getName().isEmpty()) { + throw new ValidationException("Название фильма не может быть пустым"); + } + if (film.getDescription() == null || film.getDescription().length() > 200) { + throw new ValidationException("Максимальная длина описания — 200 символов"); + } + if (film.getReleaseDate().isBefore(date)) { + throw new ValidationException("Дата релиза — не раньше 28 декабря 1895 года"); + } + if (film.getDuration() <= 0) { + throw new ValidationException("Продолжительность фильма должна быть положительным числом"); + } + if (!jdbcMpaRepository.mpaExist(film.getMpa().getId())) { + throw new DataNotFoundException("Некорректный возрастной рейтинг"); + } + if (film.getGenres() != null) { + for (Genre genre : film.getGenres()) { + if (!jdbcGenreRepository.genreExist(genre.getId())) { + throw new DataNotFoundException("Некорректный жанр"); + } + } + } + return jdbcFilmRepository.save(film); + } + + @Override + public Film updateFilm(Film newFilm) throws DataNotFoundException { + jdbcFilmRepository.getById(newFilm.getId()); + return jdbcFilmRepository.update(newFilm); + } + + @Override + public void addLike(int filmId, int userId) throws DataNotFoundException { + jdbcFilmRepository.addLike(filmId, userId); + } + + @Override + public void deleteLike(int filmId, int userId) throws DataNotFoundException { + jdbcFilmRepository.deleteLike(filmId, userId); + } + + @Override + public Collection getPopularFilms(int count) { + return jdbcFilmRepository.getPopularFilms(count); + + } + + + @Override + public Film get(int filmId) throws DataNotFoundException { + return filmStorage.get(filmId); + } +} \ No newline at end of file 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..91ce376 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java @@ -0,0 +1,12 @@ +package ru.yandex.practicum.filmorate.service; + +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.model.Genre; + +import java.util.Collection; + +public interface GenreService { + Genre getGenreById(int id) throws DataNotFoundException; + + Collection getGenres(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/GenreServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/GenreServiceImpl.java new file mode 100644 index 0000000..70146f0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/GenreServiceImpl.java @@ -0,0 +1,28 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.repository.JdbcGenreRepository; + +import java.util.Collection; + +@Service +@RequiredArgsConstructor +public class GenreServiceImpl implements GenreService { + private final JdbcGenreRepository jdbcGenreRepository; + + @Override + public Genre getGenreById(int id) throws DataNotFoundException { + if (!jdbcGenreRepository.genreExist(id)) { + throw new DataNotFoundException("Некорректный id"); + } + return jdbcGenreRepository.getById(id); + } + + @Override + public Collection getGenres() { + return jdbcGenreRepository.getAll(); + } +} 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..f701fba --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java @@ -0,0 +1,12 @@ +package ru.yandex.practicum.filmorate.service; + +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.util.Collection; + +public interface MpaService { + Mpa getById(int id) throws DataNotFoundException; + + Collection getAllMpa(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/MpaServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/MpaServiceImpl.java new file mode 100644 index 0000000..1678b31 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/MpaServiceImpl.java @@ -0,0 +1,28 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.repository.JdbcMpaRepository; + +import java.util.Collection; + +@Service +@RequiredArgsConstructor +public class MpaServiceImpl implements MpaService { + private final JdbcMpaRepository jdbcMpaRepository; + + @Override + public Mpa getById(int id) throws DataNotFoundException { + if (!jdbcMpaRepository.mpaExist(id)) { + throw new DataNotFoundException("Некорректный id"); + } + return jdbcMpaRepository.getById(id); + } + + @Override + public Collection getAllMpa() { + return jdbcMpaRepository.getAll(); + } +} 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 de588ba..4e92a80 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -1,83 +1,24 @@ package ru.yandex.practicum.filmorate.service; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import ru.yandex.practicum.filmorate.exception.DataNotFoundException; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.User; -import ru.yandex.practicum.filmorate.storage.UserStorage; -import java.util.ArrayList; import java.util.Collection; import java.util.List; +public interface UserService { + Collection getUsers(); -@Service -@RequiredArgsConstructor -public class UserService { - private final UserStorage userStorage; + User createUser(User user) throws ValidationException; - public Collection getUsers() { - return userStorage.getUsers(); - } + User updateUser(User newUser) throws DataNotFoundException; - public User createUser(User user) throws ValidationException { + void addFriend(int id, int friendId) throws DataNotFoundException; - return userStorage.createUser(user); - } + void deleteFriend(int id, int friendId) throws DataNotFoundException; - public User updateUser(User newUser) throws DataNotFoundException { - return userStorage.updateUser(newUser); + List getUserFriends(int id) throws DataNotFoundException; - } - - public void addFriend(int id, int friendId) throws DataNotFoundException { - - User user = get(id); - User friend = get(friendId); - - user.getFriendsList().add(friendId); - friend.getFriendsList().add(id); - } - - public void deleteFriend(Integer id, int friendId) throws DataNotFoundException { - - User user = get(id); - User friend = get(friendId); - - user.getFriendsList().remove(friendId); - friend.getFriendsList().remove(id); - } - - public List getUserFriends(int id) throws DataNotFoundException { - - User user = get(id); - - List friends = new ArrayList<>(); - for (Integer friend : user.getFriendsList()) { - friends.add(get(friend)); - } - return friends; - - } - - public List getCommonFriends(int id, int otherId) throws DataNotFoundException { - List userList = getUserFriends(id); - List otherUserList = getUserFriends(otherId); - List commonFriends = new ArrayList<>(); - for (User user : userList) { - if (otherUserList.contains(user)) { - commonFriends.add(user); - } - } - return commonFriends; - - - } - - - public User get(int userId) throws DataNotFoundException { - - return userStorage.get(userId); - } + List getCommonFriends(int id, int otherId) throws DataNotFoundException; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java new file mode 100644 index 0000000..a90f567 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java @@ -0,0 +1,87 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.DataNotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.repository.JdbcUserRepository; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + + +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + + private final JdbcUserRepository jdbcUserRepository; + + @Override + public Collection getUsers() { + return jdbcUserRepository.getUsers(); + } + + @Override + public User createUser(User user) throws ValidationException { + if (user.getEmail().isEmpty() || !user.getEmail().contains("@")) { + throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @"); + } + if (user.getLogin() == null || user.getLogin().isEmpty() || user.getLogin().contains(" ")) { + throw new ValidationException("Логин не может быть пустым и содержать пробелы"); + } + if (user.getName() == null) { + user.setName(user.getLogin()); + } + if (user.getBirthday().isAfter(LocalDate.now())) { + throw new ValidationException("Дата рождения не может быть в будущем"); + } + return jdbcUserRepository.save(user); + + } + + @Override + public User updateUser(User newUser) throws DataNotFoundException { + jdbcUserRepository.getById(newUser.getId()); //если id не правильный, то после этого метода вернётся нужная ошибка + + return jdbcUserRepository.update(newUser); + + + } + + @Override + public void addFriend(int id, int friendId) throws DataNotFoundException { + jdbcUserRepository.getById(id); + jdbcUserRepository.getById(friendId); + + jdbcUserRepository.addFriend(id, friendId); + } + + @Override + public void deleteFriend(int id, int friendId) throws DataNotFoundException { + jdbcUserRepository.getById(id); + jdbcUserRepository.getById(friendId); + jdbcUserRepository.deleteFriend(id, friendId); + } + + @Override + public List getUserFriends(int id) throws DataNotFoundException { + jdbcUserRepository.getById(id); + return jdbcUserRepository.getUserFriends(id); + + } + + @Override + public List getCommonFriends(int id, int otherId) throws DataNotFoundException { + List userList = getUserFriends(id); + return getUserFriends(otherId).stream() + .filter(userList::contains) + .collect(Collectors.toList()); + + + } + + +} \ No newline at end of file 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 4d0311d..059df2e 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java @@ -16,4 +16,4 @@ public interface FilmStorage { Film updateFilm(Film newFilm) throws DataNotFoundException; Film get(int filmId) throws DataNotFoundException; -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java index b44b130..8ae9c80 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java @@ -69,4 +69,4 @@ private int getNextId() { return nextId++; } -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java index 11bbd05..e7ce22f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java @@ -67,4 +67,4 @@ private int getNextId() { return nextId++; } -} +} \ No newline at end of file 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 860a424..afcb15d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java @@ -14,4 +14,4 @@ public interface UserStorage { User updateUser(User newUser) throws DataNotFoundException; User get(int id) throws DataNotFoundException; -} +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..42020f6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,8 @@ +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 +spring.h2.console.enabled=true +server.servlet.encoding.charset=UTF-8 \ 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..671c878 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,22 @@ +merge into mpa(mpa_id, mpa_name) +values(1,'G'); +merge into mpa(mpa_id, mpa_name) +values(2,'PG'); +merge into mpa(mpa_id, mpa_name) +values(3,'PG-13'); +merge into mpa(mpa_id, mpa_name) +values(4,'R'); +merge into mpa(mpa_id, mpa_name) +values(5,'NC-17'); +merge into genres(genre_id, genre_name) +values(1,'Комедия'); +merge into genres(genre_id, genre_name) +values(2,'Драма'); +merge into genres(genre_id, genre_name) +values(3,'Мультфильм'); +merge into genres(genre_id, genre_name) +values(4,'Триллер'); +merge into genres(genre_id, genre_name) +values(5,'Документальный'); +merge into genres(genre_id, genre_name) +values(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..f8e2400 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,61 @@ + + +CREATE TABLE IF NOT EXISTS users( + user_id int generated by default as identity primary key, + login varchar(255) not null, + email varchar(255) not null, + user_name varchar(255), + birthday date not null +); + + + +CREATE TABLE IF NOT EXISTS mpa ( + mpa_id int generated by default as identity primary key, + mpa_name varchar(255) not NULL unique +); + +CREATE TABLE IF NOT EXISTS films ( + film_id int generated by default as identity primary key, + name varchar(255) not null, + description varchar(255) not null, + release_date date not null, + duration int not null, + mpa_id int not NULL, + FOREIGN KEY (mpa_id) REFERENCES mpa (mpa_id) +); + + +CREATE TABLE IF NOT EXISTS friends +( + user_id int, + friend_id int, + foreign key (user_id) references users (user_id) on delete cascade, + foreign key (friend_id) references users (user_id) on delete cascade, + primary key (user_id, friend_id) +); + + + +CREATE TABLE IF NOT EXISTS genres ( + genre_id int generated by default as identity primary key, + genre_name varchar(255) not null unique +); + +CREATE TABLE IF NOT EXISTS film_genres ( + film_id int, + genre_id int, + foreign key (film_id) references films (film_id) on delete cascade, + foreign key (genre_id) references genres (genre_id) on delete cascade, + primary key (film_id, genre_id) +); + +CREATE TABLE IF NOT EXISTS film_likes ( + user_id int, + film_id int, + foreign key (film_id) references films (film_id) on delete cascade, + foreign key (user_id) references users (user_id) on delete cascade, + primary key (film_id, user_id) +); + + diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java deleted file mode 100644 index a8e13d9..0000000 --- a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package ru.yandex.practicum.filmorate.controller; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import ru.yandex.practicum.filmorate.exception.DataNotFoundException; -import ru.yandex.practicum.filmorate.exception.ValidationException; -import ru.yandex.practicum.filmorate.model.Film; - -import java.time.LocalDate; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.*; - -@SpringBootTest -public class FilmControllerTest { - - @Autowired - private FilmController filmController; - - @Test - public void returnFilmsTest() throws ValidationException { - Film film1 = new Film(); - film1.setName("filmName1"); - film1.setDescription("Descr1"); - film1.setReleaseDate(LocalDate.of(2023, 12, 15)); - film1.setDuration(168L); - - Film film2 = new Film(); - film2.setName("filmName2"); - film2.setDescription("Descr2"); - film2.setReleaseDate(LocalDate.of(2023, 12, 15)); - film2.setDuration(168L); - - filmController.createFilm(film1); - filmController.createFilm(film2); - - Collection films = filmController.getFilms(); - - assertTrue(films.contains(film1)); - assertTrue(films.contains(film2)); - } - - @Test - void filmsValidTest() throws ValidationException { - Film film = new Film(); - film.setName("filmName1"); - film.setDescription("Descr1"); - film.setReleaseDate(LocalDate.of(2023, 12, 15)); - film.setDuration(168L); - - Film savedFilm = filmController.createFilm(film); - - assertNotNull(savedFilm.getId()); - assertEquals("filmName1", savedFilm.getName()); - assertEquals("Descr1", savedFilm.getDescription()); - assertEquals(LocalDate.of(2023, 12, 15), savedFilm.getReleaseDate()); - assertEquals(168L, savedFilm.getDuration()); - - film.setName(null); - assertThrows(ValidationException.class, () -> filmController.createFilm(film)); - - film.setName("filmName1"); - film.setDescription("1111111111111111111111111111111111111111111111111111111111111111111" + "111111111111111111111111111111111111111111111111111111111111111111111111111111111" + "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); - assertThrows(ValidationException.class, () -> filmController.createFilm(film)); - - film.setDescription("Descr1"); - film.setReleaseDate(LocalDate.of(1894, 12, 15)); - assertThrows(ValidationException.class, () -> filmController.createFilm(film)); - - film.setReleaseDate(LocalDate.of(2023, 12, 15)); - film.setDuration(-1L); - assertThrows(ValidationException.class, () -> filmController.createFilm(film)); - } - - @Test - void updateFilmTest() throws ValidationException { - Film film = new Film(); - film.setName("filmName1"); - film.setDescription("Descr1"); - film.setReleaseDate(LocalDate.of(2023, 12, 15)); - film.setDuration(168L); - - filmController.createFilm(film); - - Film filmUpdate = new Film(); - filmUpdate.setId(8); - assertThrows(DataNotFoundException.class, () -> filmController.updateFilm(filmUpdate)); - } -} diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/JdbcUserRopositoryTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/JdbcUserRopositoryTest.java new file mode 100644 index 0000000..a26238b --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/JdbcUserRopositoryTest.java @@ -0,0 +1,46 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.repository.JdbcUserRepository; +import ru.yandex.practicum.filmorate.repository.mappers.UserRowMapper; + +import java.time.LocalDate; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +@Import({JdbcUserRepository.class, UserRowMapper.class}) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class JdbcUserRopositoryTest { + public static final int TEST_USER_ID = 1; + private final JdbcUserRepository jdbcUserRepository; + private final UserRowMapper mapper; + + static User getTestUser() { + User user = new User(); + user.setId(TEST_USER_ID); + user.setLogin("userLogin"); + user.setEmail("email@email.com"); + user.setName("user"); + user.setBirthday(LocalDate.of(2000, 3, 22)); + return user; + } + + @Test + public void find_by_id() { + Optional userOptional = jdbcUserRepository.getById(TEST_USER_ID); + + assertThat(userOptional) + .isPresent() + .get() + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(getTestUser()); + } +} diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java deleted file mode 100644 index dbf950f..0000000 --- a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package ru.yandex.practicum.filmorate.controller; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import ru.yandex.practicum.filmorate.exception.DataNotFoundException; -import ru.yandex.practicum.filmorate.exception.ValidationException; -import ru.yandex.practicum.filmorate.model.User; - -import java.time.LocalDate; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.*; - - -@SpringBootTest -public class UserControllerTest { - - - @Autowired - private UserController userController; - - @Test - public void returnsUsersTest() throws ValidationException { - User user1 = new User(); - user1.setEmail("user1@yandex.ru"); - user1.setLogin("user1"); - user1.setName("User1"); - user1.setBirthday(LocalDate.of(1999, 8, 6)); - - User user2 = new User(); - user2.setEmail("user2@yandex.ru"); - user2.setLogin("user2"); - user2.setName("User2"); - user2.setBirthday(LocalDate.of(1991, 3, 20)); - - userController.createUser(user1); - userController.createUser(user2); - - Collection users = userController.getUsers(); - - assertTrue(users.contains(user1)); - assertTrue(users.contains(user2)); - } - - @Test - void userValidTest() throws ValidationException { - User user = new User(); - user.setEmail("user1@yandex.ru"); - user.setLogin("user1"); - user.setName("user1"); - user.setBirthday(LocalDate.of(1999, 8, 6)); - - User savedUser1 = userController.createUser(user); - - assertNotNull(savedUser1.getId()); - assertEquals("user1@yandex.ru", savedUser1.getEmail()); - assertEquals("user1", savedUser1.getLogin()); - assertEquals("user1", savedUser1.getName()); - assertEquals(LocalDate.of(1999, 8, 6), savedUser1.getBirthday()); - - user.setEmail("12345"); - assertThrows(ValidationException.class, () -> userController.createUser(user)); - - user.setEmail("user3.com"); - assertThrows(ValidationException.class, () -> userController.createUser(user)); - - user.setEmail("user1@yandex.ru"); - user.setLogin(""); - assertThrows(ValidationException.class, () -> userController.createUser(user)); - - user.setLogin(null); - assertThrows(ValidationException.class, () -> userController.createUser(user)); - - user.setLogin("user 1"); - assertThrows(ValidationException.class, () -> userController.createUser(user)); - - user.setBirthday(LocalDate.now().plusMonths(5)); - assertThrows(ValidationException.class, () -> userController.createUser(user)); - } - - @Test - void updateUserTest() throws ValidationException { - User user1 = new User(); - user1.setEmail("user1@yandex.ru"); - user1.setLogin("user1"); - user1.setName(null); - user1.setBirthday(LocalDate.of(1999, 8, 6)); - - userController.createUser(user1); - - User userUpdate = new User(); - userUpdate.setEmail("user1@yandex.ru"); - userUpdate.setLogin("user1"); - userUpdate.setName(null); - userUpdate.setBirthday(LocalDate.of(1999, 8, 6)); - userUpdate.setId(8); - assertThrows(DataNotFoundException.class, () -> userController.updateUser(userUpdate)); - } -} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..c27dcec --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,8 @@ +spring.sql.init.mode=always +spring.datasource.url=jdbc:h2:mem:filmorate +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.sql.init.data-locations=classpath:test-data.sql +spring.h2.console.enabled=true +server.servlet.encoding.charset=UTF-8 \ No newline at end of file diff --git a/src/test/resources/test-data.sql b/src/test/resources/test-data.sql new file mode 100644 index 0000000..e65fef3 --- /dev/null +++ b/src/test/resources/test-data.sql @@ -0,0 +1 @@ +INSERT INTO USERS (LOGIN, EMAIL, USER_NAME, BIRTHDAY) VALUES('userLogin', 'email@email.com', 'user', '2000-3-22'); \ No newline at end of file