diff --git a/README.md b/README.md index 10ba142..f11431e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,17 @@ # java-filmorate Template repository for Filmorate project. + +### Список выполненных задач + +1. Функциональность «Отзывы». 4 SP +2. Функциональность «Поиск». 3 SP +3. Функциональность «Общие фильмы». 1 SP +4. Функциональность «Рекомендации». 3 SP +5. Функциональность «Лента событий». 3 SP +6. Функциональность «Удаление фильмов и пользователей». 2 SP +7. Функциональность «Фильмы по режиссёрам». 4 SP +8. Функциональность «Популярные фильмы». 2 SP + DB scheme - /storage-scheme.jpg +![storage-scheme.jpg](storage-scheme.jpg) \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java index 0b6b985..ace67ab 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java @@ -4,10 +4,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.dto.DirectorDto; -import ru.yandex.practicum.filmorate.model.Director; import ru.yandex.practicum.filmorate.service.director.DirectorService; import java.util.Collection; @@ -16,7 +14,6 @@ @RestController @RequestMapping("/directors") @RequiredArgsConstructor -@Validated public class DirectorController { private final DirectorService directorService; @@ -35,13 +32,13 @@ public DirectorDto getDirectorById(@PathVariable long directorId) { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public DirectorDto addDirector(@RequestBody @Valid Director director) { + public DirectorDto addDirector(@RequestBody @Valid DirectorDto director) { log.info("Received POST /directors request with body: {}", director); return directorService.addDirector(director); } @PutMapping - public DirectorDto updateDirector(@RequestBody @Valid Director director) { + public DirectorDto updateDirector(@RequestBody @Valid DirectorDto director) { log.info("Received PUT /directors request with body: {}", director); return directorService.updateDirector(director); } 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 95cfe67..438c54d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java @@ -11,6 +11,13 @@ @RestControllerAdvice public class ErrorHandler { + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleParameterNotValid(final ParameterNotValidException e) { + log.warn("parameter not valid"); + return new ErrorResponse(e.getMessage()); + } + @ExceptionHandler @ResponseStatus(HttpStatus.NOT_FOUND) public ErrorResponse handleNotFound(final NotFoundException e) { @@ -25,20 +32,6 @@ public ErrorResponse handleEmptyField(final EmptyFieldException e) { return new ErrorResponse(e.getMessage()); } - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleMethodArgumentNotValid(final MethodArgumentNotValidException e) { - log.warn("method argument not valid"); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleConstraintViolation(final ConstraintViolationException e) { - log.warn("constraint violation"); - return new ErrorResponse(e.getMessage()); - } - @ExceptionHandler(InternalServerException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ErrorResponse handleException(Exception e) { diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FeedController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FeedController.java index 1c06dd6..6c69a03 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FeedController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FeedController.java @@ -4,7 +4,7 @@ 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.Event; +import ru.yandex.practicum.filmorate.dto.EventDto; import ru.yandex.practicum.filmorate.service.feed.FeedService; import java.util.List; @@ -20,7 +20,7 @@ public FeedController(FeedService feedService) { } @GetMapping("/{id}/feed") - public List getUserFeed(@PathVariable("id") long userId) { + public List getUserFeed(@PathVariable("id") long userId) { return feedService.getUsersEventFeed(userId); } } \ 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 97843f1..0eed343 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -5,9 +5,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.dto.FilmDto; +import ru.yandex.practicum.filmorate.mappers.FilmMapper; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.service.film.FilmService; @@ -18,7 +18,6 @@ @RestController @RequestMapping("/films") @RequiredArgsConstructor -@Validated public class FilmController { private final FilmService filmService; @@ -31,14 +30,15 @@ public Collection getFilms() { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public FilmDto addFilm(@Valid @RequestBody Film film) { + public FilmDto addFilm(@Valid @RequestBody FilmDto film) { log.info("Received POST /films request with body: {}", film); return filmService.addFilm(film); } @PutMapping - public FilmDto updateFilm(@Valid @RequestBody Film film) { - log.info("Received PUT /films request with body: {}", film); + public FilmDto updateFilm(@Valid @RequestBody FilmDto filmDto) { + log.info("Received PUT /films request with body: {}", filmDto); + Film film = FilmMapper.mapToFilmDto(filmDto); return filmService.updateFilm(film); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java index f4fa2dd..04715d0 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java @@ -24,8 +24,8 @@ public ReviewController(ReviewService reviewService) { @GetMapping public List getFilmReviews(@RequestParam(defaultValue = "0") long filmId, - @RequestParam(defaultValue = "10") - @Positive(message = "Count must be positive") long count) { + @RequestParam(defaultValue = "10") + @Positive(message = "Count must be positive") long count) { log.info("Received GET {} reviews for film with ID = {}", count, filmId); return reviewService.getFilmReviews(filmId, count); } 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 3da7c93..376a858 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -7,7 +7,6 @@ import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.dto.FilmDto; import ru.yandex.practicum.filmorate.dto.UserDto; -import ru.yandex.practicum.filmorate.model.User; import ru.yandex.practicum.filmorate.service.feed.FeedService; import ru.yandex.practicum.filmorate.service.film.FilmService; import ru.yandex.practicum.filmorate.service.user.UserService; @@ -36,13 +35,13 @@ public Collection getUsers() { @ResponseStatus(HttpStatus.CREATED) @PostMapping - public UserDto addUser(@RequestBody @Valid User user) { + public UserDto addUser(@RequestBody @Valid UserDto user) { log.info("addUser"); return userService.addUser(user); } @PutMapping - public UserDto updateUser(@RequestBody @Valid User user) { + public UserDto updateUser(@RequestBody @Valid UserDto user) { log.info("updateUser"); System.out.println(user); return userService.updateUser(user); diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/BaseRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/BaseRepository.java index 7a33e98..f52e326 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/BaseRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/BaseRepository.java @@ -5,7 +5,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.GeneratedKeyHolder; -import ru.yandex.practicum.filmorate.exception.ConstraintViolationException; import java.sql.PreparedStatement; import java.sql.Statement; @@ -38,7 +37,7 @@ protected boolean delete(String query, long id) { protected void update(String query, Object... params) { int rowsUpdated = jdbc.update(query, params); if (rowsUpdated == 0) { - throw new ConstraintViolationException("Не удалось обновить данные"); + throw new IllegalStateException("Не удалось обновить данные"); } } @@ -58,7 +57,7 @@ protected long insert(String query, Object... params) { if (id != null) { return id; } else { - throw new ConstraintViolationException("Не удалось сохранить данные"); + throw new IllegalStateException("Не удалось сохранить данные"); } } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/FeedRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/FeedRepository.java index 96e866d..1b541d9 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/FeedRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/FeedRepository.java @@ -1,31 +1,28 @@ package ru.yandex.practicum.filmorate.dal; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; -import ru.yandex.practicum.filmorate.dal.mapper.EventRowMapper; import ru.yandex.practicum.filmorate.model.Event; import java.util.List; @Repository -public class FeedRepository { +public class FeedRepository extends BaseRepository { - private final JdbcTemplate jdbc; - private final EventRowMapper eventRowMapper; - - public FeedRepository(JdbcTemplate jdbc, EventRowMapper eventRowMapper) { - this.jdbc = jdbc; - this.eventRowMapper = eventRowMapper; + public FeedRepository(JdbcTemplate jdbc, RowMapper mapper) { + super(jdbc, mapper); } public List getUsersEventFeed(long userId) { String sqlQuery = "SELECT * FROM event_feed WHERE user_id = ?;"; - return jdbc.query(sqlQuery, eventRowMapper, userId); + return findMany(sqlQuery, userId); } public void saveEvent(Event event) { - String sqlQuery = "INSERT INTO event_feed (timestamp, user_id, event_type, operation, entity_id) VALUES (?, ?, ?, ?, ?);"; - jdbc.update(sqlQuery, event.getTimestamp(), event.getUserId(), event.getEventType().name(), + String sqlQuery = "INSERT INTO event_feed (timestamp, user_id, event_type, operation, entity_id) " + + "VALUES (?, ?, ?, ?, ?);"; + update(sqlQuery, event.getTimestamp(), event.getUserId(), event.getEventType().name(), event.getOperation().name(), event.getEntityId()); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java index 43328dd..91ffea7 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java @@ -4,7 +4,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; @@ -52,7 +51,7 @@ public class FilmRepository extends BaseRepository { "SELECT fg.film_id, g.id as genre_id, g.name as genre_name " + "FROM genre g " + "JOIN film_genre fg ON g.id = fg.genre_id " + - "WHERE fg.film_id IN (:filmIds)"; + "WHERE fg.film_id"; private static final String DELETE_FILM_QUERY = "DELETE FROM Films WHERE id = ?"; private static final String FIND_DIRECTOR_FOR_FILMS_QUERY = """ @@ -61,7 +60,7 @@ public class FilmRepository extends BaseRepository { d.NAME as DIRECTOR_NAME FROM FILM_DIRECTORS fd JOIN DIRECTORS d ON (fd.DIRECTOR_ID = d.DIRECTOR_ID) - WHERE fd.film_id in (:filmIds) + WHERE fd.film_id """; private static final String FIND_BY_DIRECTOR_SORT_BY_LIKES = """ @@ -96,8 +95,6 @@ ORDER BY (SELECT COUNT(1) FROM LIKES l WHERE l.FILM_ID = f.ID) DESC LEFT JOIN DIRECTORS d ON fd.DIRECTOR_ID = d.DIRECTOR_ID WHERE """; - - private final JdbcTemplate jdbc; private final NamedParameterJdbcTemplate namedJdbcTemplate; private FilmRowMapper filmMapper; @@ -139,7 +136,6 @@ public FilmRepository(JdbcTemplate jdbc, NamedParameterJdbcTemplate namedJdbcTemplate, FilmRowMapper filmMapper) { super(jdbc, filmMapper); - this.jdbc = jdbc; this.namedJdbcTemplate = namedJdbcTemplate; this.filmMapper = filmMapper; } @@ -188,24 +184,28 @@ public Optional findById(long id) { } private void setGenresForFilms(List films) { + if (films == null || films.isEmpty()) { + return; + } List filmIds = films.stream() .map(Film::getId) + .distinct() .collect(Collectors.toList()); if (filmIds.isEmpty()) { return; } - MapSqlParameterSource parameters = new MapSqlParameterSource(); - parameters.addValue("filmIds", filmIds); - + String placeholders = String.join(",", Collections.nCopies(filmIds.size(), "?")); + String finalSql = FIND_GENRES_FOR_FILMS_QUERY + " IN (" + placeholders + ")"; - List relations = namedJdbcTemplate.query( - FIND_GENRES_FOR_FILMS_QUERY, - parameters, - filmGenreRelationRowMapper + List relations = jdbc.query( + finalSql, + filmGenreRelationRowMapper, + filmIds.toArray() ); + Map> genresByFilmId = relations.stream() .collect(groupingBy( relation -> relation.filmId, @@ -387,22 +387,26 @@ private void saveDirector(Film film) { } private void setDirectorForFilm(List films) { + if (films == null || films.isEmpty()) { + return; + } List filmIds = films.stream() .map(Film::getId) + .distinct() .collect(Collectors.toList()); if (filmIds.isEmpty()) { return; } - MapSqlParameterSource parameters = new MapSqlParameterSource(); - parameters.addValue("filmIds", filmIds); + String placeholders = String.join(",", Collections.nCopies(filmIds.size(), "?")); + String finalSql = FIND_DIRECTOR_FOR_FILMS_QUERY + " IN (" + placeholders + ")"; - List relations = namedJdbcTemplate.query( - FIND_DIRECTOR_FOR_FILMS_QUERY, - parameters, - filmDirectorRelationRowMapper + List relations = jdbc.query( + finalSql, + filmDirectorRelationRowMapper, + filmIds.toArray() ); Map> directorsByFilmId = relations.stream() diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/GenreRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/GenreRepository.java index 523d58d..4d7f33b 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/GenreRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/GenreRepository.java @@ -14,17 +14,12 @@ public class GenreRepository extends BaseRepository { private static final String GET_GENRE_BY_ID = "SELECT id, name FROM genre WHERE id = ?"; private static final String FIND_GENRES_BY_FILM_ID_QUERY = "SELECT g.id, g.name " + - "FROM genre g JOIN film_genre fg ON g.id = fg.genre_id " + - "WHERE fg.film_id = ? " + - "ORDER BY g.id ASC"; - - private final JdbcTemplate jdbc; - private final GenreRowMapper genreMapper; + "FROM genre g JOIN film_genre fg ON g.id = fg.genre_id " + + "WHERE fg.film_id = ? " + + "ORDER BY g.id ASC"; public GenreRepository(JdbcTemplate jdbc, GenreRowMapper mapper) { super(jdbc, mapper); - this.jdbc = jdbc; - this.genreMapper = mapper; } public List findAll() { @@ -36,7 +31,6 @@ public Optional findById(int id) { } public List findGenresByFilmId(long filmId) { - List genres = jdbc.query(FIND_GENRES_BY_FILM_ID_QUERY, genreMapper, filmId); - return genres; + return findMany(FIND_GENRES_BY_FILM_ID_QUERY, filmId); } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java new file mode 100644 index 0000000..fc6ee61 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.dto; + +import lombok.Data; +import ru.yandex.practicum.filmorate.model.EventType; +import ru.yandex.practicum.filmorate.model.Operation; + +@Data +public class EventDto { + private long id; + private long timestamp; + private long userId; + private EventType eventType; + private Operation operation; + private long eventId; + private long entityId; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java index b9fe18d..cf668c0 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java @@ -1,7 +1,12 @@ package ru.yandex.practicum.filmorate.dto; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Data; + import java.time.LocalDate; import java.util.ArrayList; import java.util.HashSet; @@ -11,10 +16,14 @@ @Data public class FilmDto { private long id; + @NotBlank(message = "Film name is empty") private String name; + @Size(max = 200, message = "Film description is too long (max 200 chars)") private String description; private LocalDate releaseDate; + @Min(value = 1, message = "Film duration must be positive") private int duration; + @NotNull(message = "Rating cannot be null") private RatingDto mpa; private List genres = new ArrayList<>(); private Set likes = new HashSet<>(); diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/UpdateFilmRequest.java b/src/main/java/ru/yandex/practicum/filmorate/dto/UpdateFilmRequest.java deleted file mode 100644 index 44f24c0..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/dto/UpdateFilmRequest.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.yandex.practicum.filmorate.dto; - -import lombok.Data; -import ru.yandex.practicum.filmorate.model.Director; -import ru.yandex.practicum.filmorate.model.Rating; - -@Data -public class UpdateFilmRequest { - private String name; - private String description; - private String releaseDate; - private String duration; - private Rating ratingId; - private Director directors; - - public boolean hasName() { - return !(name == null || name.isBlank()); - } - - public boolean hasDescription() { - return !(description == null || description.isBlank()); - } - - public boolean hasReleaseDate() { - return !(releaseDate == null); - } - - public boolean hasDuration() { - return !(duration == null); - } - - public boolean hasRaringId() { - return !(ratingId == null); - } -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/UserDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/UserDto.java index 53ff743..5626f2c 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dto/UserDto.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/UserDto.java @@ -1,16 +1,21 @@ package ru.yandex.practicum.filmorate.dto; -import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PastOrPresent; import lombok.Data; import java.time.LocalDate; @Data public class UserDto { - @JsonProperty(access = JsonProperty.Access.READ_ONLY) private long id; + @NotBlank(message = "Invalid email") + @Email(message = "Invalid email") private String email; + @NotBlank(message = "Invalid login") private String login; private String name; + @PastOrPresent(message = "Invalid birthday") private LocalDate birthday; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ConstraintViolationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ConstraintViolationException.java deleted file mode 100644 index 7f5cf2c..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/ConstraintViolationException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.yandex.practicum.filmorate.exception; - -public class ConstraintViolationException extends RuntimeException { - public ConstraintViolationException(String message) { - super(message); - } -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/MethodArgumentNotValidException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/MethodArgumentNotValidException.java deleted file mode 100644 index d35aba1..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/MethodArgumentNotValidException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.yandex.practicum.filmorate.exception; - -public class MethodArgumentNotValidException extends RuntimeException { - public MethodArgumentNotValidException(String message) { - super(message); - } -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/mappers/DirectorMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mappers/DirectorMapper.java index 2e34790..349b966 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/mappers/DirectorMapper.java +++ b/src/main/java/ru/yandex/practicum/filmorate/mappers/DirectorMapper.java @@ -24,6 +24,13 @@ public static DirectorDto mapToDirectorDto(Director director) { return directorDto; } + public static Director toDirectorDTO(DirectorDto directorDto) { + Director director = new Director(); + director.setId(directorDto.getId()); + director.setName(directorDto.getName()); + return director; + } + public static List mapToDirectorDtoList(List directors) { if (directors == null || directors.isEmpty()) { return Collections.emptyList(); @@ -33,4 +40,13 @@ public static List mapToDirectorDtoList(List directors) { .collect(Collectors.toList()); } + public static List mapToDirectorList(List directorDtos) { + if (directorDtos == null || directorDtos.isEmpty()) { + return Collections.emptyList(); + } + return directorDtos.stream() + .map(DirectorMapper::toDirectorDTO) + .collect(Collectors.toList()); + } + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/mappers/EventMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mappers/EventMapper.java new file mode 100644 index 0000000..d0189f6 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/mappers/EventMapper.java @@ -0,0 +1,48 @@ +package ru.yandex.practicum.filmorate.mappers; + +import lombok.experimental.UtilityClass; +import ru.yandex.practicum.filmorate.dto.EventDto; +import ru.yandex.practicum.filmorate.model.Event; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@UtilityClass +public class EventMapper { + public static EventDto mapToEventDto(Event event) { + EventDto eventDto = new EventDto(); + + eventDto.setId(eventDto.getId()); + eventDto.setTimestamp(event.getTimestamp()); + eventDto.setUserId(event.getUserId()); + eventDto.setEventType(event.getEventType()); + eventDto.setOperation(event.getOperation()); + eventDto.setEventId(event.getEventId()); + eventDto.setEntityId(event.getEntityId()); + return eventDto; + } + + public static Event toEventDto(EventDto eventDto) { + Event event = new Event(); + + event.setId(event.getId()); + event.setTimestamp(eventDto.getTimestamp()); + event.setUserId(eventDto.getUserId()); + event.setEventType(eventDto.getEventType()); + event.setOperation(eventDto.getOperation()); + event.setEventId(eventDto.getEventId()); + event.setEntityId(eventDto.getEntityId()); + return event; + } + + public static List mapToEventDtoList(List events) { + if (events == null || events.isEmpty()) { + return Collections.emptyList(); + } + return events.stream() + .map(EventMapper::mapToEventDto) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/mappers/FilmMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mappers/FilmMapper.java index e52c360..73868f7 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/mappers/FilmMapper.java +++ b/src/main/java/ru/yandex/practicum/filmorate/mappers/FilmMapper.java @@ -47,7 +47,6 @@ public static Film mapToFilm(Film requestFilm) { return film; } - public static FilmDto mapToFilmDto(Film film, Set likes) { if (film == null) return null; FilmDto dto = new FilmDto(); @@ -63,6 +62,22 @@ public static FilmDto mapToFilmDto(Film film, Set likes) { return dto; } + + public static Film mapToFilmDto(FilmDto filmDto) { + if (filmDto == null) return null; + Film film = new Film(); + film.setId(filmDto.getId()); + film.setName(filmDto.getName()); + film.setDescription(filmDto.getDescription()); + film.setReleaseDate(filmDto.getReleaseDate()); + film.setDuration(filmDto.getDuration()); + film.setMpa(RatingMapper.mapToRating(filmDto.getMpa())); + film.setGenres(GenreMapper.mapToGenreList(filmDto.getGenres())); + //film.setLikes(Optional.ofNullable(likes).orElse(Collections.emptySet())); + film.setDirectors(DirectorMapper.mapToDirectorList(filmDto.getDirectors())); + return film; + } + public static FilmDto mapToFilmDto(Film film) { return mapToFilmDto(film, Collections.emptySet()); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/mappers/UserMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mappers/UserMapper.java index fa9aa5e..b5c10b2 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/mappers/UserMapper.java +++ b/src/main/java/ru/yandex/practicum/filmorate/mappers/UserMapper.java @@ -28,6 +28,16 @@ public static UserDto mapToUserDto(User user) { return dto; } + public static User mapToUser(UserDto userDto) { + User user = new User(); + user.setId(userDto.getId()); + user.setEmail(userDto.getEmail()); + user.setLogin(userDto.getLogin()); + user.setName(userDto.getName()); + user.setBirthday(userDto.getBirthday()); + return user; + } + public static User updateUserFields(User user, UpdateUserRequest request) { if (request.hasEmail()) { user.setEmail(request.getEmail()); diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Event.java b/src/main/java/ru/yandex/practicum/filmorate/model/Event.java index 5ac5320..eed9158 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Event.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Event.java @@ -11,5 +11,4 @@ public class Event { private Operation operation; private long eventId; private long entityId; -} - +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/director/DirectorService.java b/src/main/java/ru/yandex/practicum/filmorate/service/director/DirectorService.java index 35aebbf..5066ff2 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/director/DirectorService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/director/DirectorService.java @@ -32,15 +32,17 @@ public DirectorDto getDirectorById(long directorId) { return DirectorMapper.mapToDirectorDto(director); } - public DirectorDto addDirector(Director director) { - log.info("Adding new director: {}", director.getName()); + public DirectorDto addDirector(DirectorDto directorDto) { + log.info("Adding new director: {}", directorDto.getName()); + Director director = DirectorMapper.toDirectorDTO(directorDto); Director newDirector = directorStorage.addDirector(director); - log.info("Director {} added with id: {}", director.getName(), director.getId()); - return DirectorMapper.mapToDirectorDto(director); + log.info("Director {} added with id: {}", newDirector.getName(), newDirector.getId()); + return DirectorMapper.mapToDirectorDto(newDirector); } - public DirectorDto updateDirector(Director director) { - log.info("Updating director with id: {}", director.getId()); + public DirectorDto updateDirector(DirectorDto directorDto) { + log.info("Updating director with id: {}", directorDto.getId()); + Director director = DirectorMapper.toDirectorDTO(directorDto); Director updateDirector = directorStorage.updateDirector(director); log.info("Director {} with id: {} updated", updateDirector.getName(), updateDirector.getId()); return DirectorMapper.mapToDirectorDto(updateDirector); diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/feed/FeedService.java b/src/main/java/ru/yandex/practicum/filmorate/service/feed/FeedService.java index 6d5df3f..18c7c62 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/feed/FeedService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/feed/FeedService.java @@ -5,7 +5,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.yandex.practicum.filmorate.dal.FeedRepository; +import ru.yandex.practicum.filmorate.dto.EventDto; import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.mappers.EventMapper; import ru.yandex.practicum.filmorate.model.Event; import ru.yandex.practicum.filmorate.model.EventType; import ru.yandex.practicum.filmorate.model.Operation; @@ -24,14 +26,14 @@ public FeedService(FeedRepository feedRepository) { } @Transactional - public List getUsersEventFeed(long userId) { + public List getUsersEventFeed(long userId) { log.info("Обработка запроса на получение ленты событий пользователя с userId = {}", userId); List events = feedRepository.getUsersEventFeed(userId); if (events.isEmpty()) { log.error("No data found"); throw new NotFoundException("No data found"); } - return events; + return EventMapper.mapToEventDtoList(events); } @Transactional diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java index a3cac25..40d9d36 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java @@ -8,9 +8,9 @@ import ru.yandex.practicum.filmorate.dto.FilmDto; import ru.yandex.practicum.filmorate.dto.GenreDto; import ru.yandex.practicum.filmorate.dto.RatingDto; -import ru.yandex.practicum.filmorate.exception.ConstraintViolationException; import ru.yandex.practicum.filmorate.exception.InternalServerException; import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ParameterNotValidException; import ru.yandex.practicum.filmorate.mappers.FilmMapper; import ru.yandex.practicum.filmorate.mappers.GenreMapper; import ru.yandex.practicum.filmorate.mappers.RatingMapper; @@ -45,8 +45,9 @@ public Collection getFilms() { } @Transactional - public FilmDto addFilm(Film film) { - log.info("Adding new film: {}", film); + public FilmDto addFilm(FilmDto filmDto) { + log.info("Adding new film: {}", filmDto); + Film film = FilmMapper.mapToFilmDto(filmDto); validateFilm(film); Film addedFilm = filmStorage.addFilm(film); log.info("Film added with id: {}", addedFilm.getId()); @@ -137,7 +138,7 @@ public List getCommonFilms(long userId, long friendId) { userExists(userId); userExists(friendId); if (userId == friendId) { - throw new ConstraintViolationException("Id's can't be the same"); + throw new ParameterNotValidException("Data", "Id's can't be the same"); } log.info("Getting MPA common films for users: {}, {}", userId, friendId); Collection films = filmStorage.getCommonFilms(userId, friendId); @@ -173,7 +174,7 @@ public List getRecommendations(long id) { private void validateFilm(Film film) { if (film.getReleaseDate() != null && film.getReleaseDate().isBefore(MIN_RELEASE_DATE)) { log.warn("Validation failed"); - throw new ConstraintViolationException("Film release date cannot be earlier than " + MIN_RELEASE_DATE); + throw new ParameterNotValidException("Data", "Film release date cannot be earlier than " + MIN_RELEASE_DATE); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/review/ReviewService.java b/src/main/java/ru/yandex/practicum/filmorate/service/review/ReviewService.java index b717fdd..d8b90c1 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/review/ReviewService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/review/ReviewService.java @@ -4,13 +4,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import ru.yandex.practicum.filmorate.dto.NewReviewRequest; import ru.yandex.practicum.filmorate.dto.ReviewDto; import ru.yandex.practicum.filmorate.dto.UpdateReviewRequest; import ru.yandex.practicum.filmorate.exception.InternalServerException; -import ru.yandex.practicum.filmorate.exception.MethodArgumentNotValidException; import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ParameterNotValidException; import ru.yandex.practicum.filmorate.mappers.ReviewMapper; import ru.yandex.practicum.filmorate.model.EventType; import ru.yandex.practicum.filmorate.model.Film; @@ -46,7 +45,7 @@ public ReviewService(@Qualifier("reviewRepository") ReviewStorage reviewStorage, public List getFilmReviews(long filmId, long count) { if (count <= 0) { - throw new MethodArgumentNotValidException("Count must be greater than 0"); + throw new ParameterNotValidException("Count", "Must be greater than 0"); } if (filmId == 0) { @@ -76,19 +75,18 @@ public ReviewDto getReviewById(long reviewId) { .orElseThrow(() -> new NotFoundException("Review with ID " + reviewId + " not found")); } - @Transactional public ReviewDto addReview(@Valid NewReviewRequest request) { /* Ручная обработка из-за специфичных postman тестов */ if (request.getUserId() == null) { log.error("Parameter UserId cannot be empty"); - throw new MethodArgumentNotValidException("Parameter UserId cannot be empty"); + throw new ParameterNotValidException("UserId", "Cannot be empty"); } if (request.getFilmId() == null) { log.error("Parameter FilmId cannot be empty"); - throw new MethodArgumentNotValidException("Parameter FilmId cannot be empty"); + throw new ParameterNotValidException("FilmId", "Cannot be empty"); } if (request.getUserId() < 0) { @@ -108,7 +106,6 @@ public ReviewDto addReview(@Valid NewReviewRequest request) { return reviewDto; } - @Transactional public ReviewDto updateReview(@Valid UpdateReviewRequest request) { Review updatedReview = reviewStorage.getReviewById(request.getReviewId()) .map(review -> ReviewMapper.updateReviewFields(review, request)) @@ -118,7 +115,6 @@ public ReviewDto updateReview(@Valid UpdateReviewRequest request) { return ReviewMapper.mapToReviewDto(updatedReview); } - @Transactional public void deleteReview(long reviewId) { ReviewDto reviewDto = getReviewById(reviewId); log.info("Deleting review: {}", reviewDto); @@ -132,7 +128,6 @@ public void deleteReview(long reviewId) { } } - @Transactional public void likeReview(long reviewId, long userId) { checkReviewAndUser(reviewId, userId); log.info("User with userId = {} wants to like review with reviewId = : {}", userId, reviewId); @@ -146,7 +141,6 @@ public void likeReview(long reviewId, long userId) { } } - @Transactional public void removeLike(long reviewId, long userId) { checkReviewAndUser(reviewId, userId); log.info("User with userId = {} wants to remove like from review with reviewId = {}", userId, reviewId); @@ -160,7 +154,6 @@ public void removeLike(long reviewId, long userId) { } } - @Transactional public void dislikeReview(long reviewId, long userId) { checkReviewAndUser(reviewId, userId); log.info("User with userId = {} wants to dislike review with reviewId = {}", userId, reviewId); @@ -174,7 +167,6 @@ public void dislikeReview(long reviewId, long userId) { } } - @Transactional public void removeDislike(long reviewId, long userId) { checkReviewAndUser(reviewId, userId); log.info("User with userId = {} wants to remove dislike from review with reviewId = {}", userId, reviewId); diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java index 1a21d76..8d6d2a4 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java @@ -1,12 +1,10 @@ package ru.yandex.practicum.filmorate.service.user; -import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; import ru.yandex.practicum.filmorate.dto.UserDto; import ru.yandex.practicum.filmorate.exception.EmptyFieldException; import ru.yandex.practicum.filmorate.exception.InternalServerException; @@ -113,7 +111,8 @@ public Collection getUsers() { .collect(Collectors.toList()); } - public UserDto addUser(User user) { + public UserDto addUser(UserDto userDto) { + User user = UserMapper.mapToUser(userDto); if (user.getName() == null || user.getName().isBlank()) { log.debug("Username is empty, using login as name"); user.setName(user.getLogin()); @@ -121,7 +120,8 @@ public UserDto addUser(User user) { return UserMapper.mapToUserDto(userDbStorage.addUser(user)); } - public UserDto updateUser(@RequestBody @Valid User user) { + public UserDto updateUser(UserDto userDto) { + User user = UserMapper.mapToUser(userDto); if (user.getId() == 0) { log.warn("User id is empty"); throw new EmptyFieldException("ID can't be empty");