Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

## Ключевые возможности на данном этапе

- CRUD-операции для `Film` и `User` (создание, обновление, получение по ID и списком).
- CRUD-операции для `Film` и `User` (создание, обновление, удаление, получение по ID и списком).
- Хранение данных в базе H2 (файловый режим — данные переживают перезапуск),
доступ через `JdbcTemplate` и `RowMapper`.
- Справочники жанров и рейтингов MPA в базе, эндпоинты `/genres` и `/mpa`.
Expand Down Expand Up @@ -67,15 +67,20 @@
- `GET /films/{id}` — получить фильм по ID.
- `POST /films` — добавить новый фильм.
- `PUT /films` — обновить существующий фильм.
- `DELETE /films/{id}` — удалить фильм; связанные с ним записи удаляются каскадно.
- `PUT /films/{id}/like/{userId}` — поставить лайк фильму.
- `DELETE /films/{id}/like/{userId}` — убрать лайк.
- `GET /films/popular?count={count}` — топ-N фильмов по лайкам (по умолчанию 10).
- `GET /films/popular?count={count}&genreId={genreId}&year={year}` — топ-N фильмов по лайкам
(по умолчанию 10); жанр и год — необязательные фильтры.
- `GET /films/common?userId={userId}&friendId={friendId}` — общие фильмы двух пользователей,
по убыванию популярности.

### Пользователи (`/users`)
- `GET /users` — получить список всех пользователей.
- `GET /users/{id}` — получить пользователя по ID.
- `POST /users` — создать нового пользователя.
- `PUT /users` — обновить данные пользователя.
- `DELETE /users/{id}` — удалить пользователя; связанные с ним записи удаляются каскадно.
- `PUT /users/{id}/friends/{friendId}` — добавить в друзья (дружба односторонняя).
- `DELETE /users/{id}/friends/{friendId}` — удалить из друзей.
- `GET /users/{id}/friends` — список друзей пользователя.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public Film update(@Valid @RequestBody Film film) {
return filmService.update(film);
}

@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
log.info("DELETE /films/{}", id);
filmService.delete(id);
}

@PutMapping("/{id}/like/{userId}")
public Film addLike(@PathVariable long id, @PathVariable long userId) {
log.info("PUT /films/{}/like/{}", id, userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public User update(@Valid @RequestBody User user) {
return userService.update(user);
}

@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
log.info("DELETE /users/{}", id);
userService.delete(id);
}

@PutMapping("/{id}/friends/{friendId}")
public User addFriend(@PathVariable long id, @PathVariable long friendId) {
log.info("PUT /users/{}/friends/{}", id, friendId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ public Film update(Film film) {
return filmStorage.update(film);
}

// Лайки, жанры и прочие связи фильма база удаляет сама — каскадом из schema.sql
public void delete(long id) {
filmStorage.delete(id); // нет фильма — хранилище бросит NotFoundException, это 404
log.debug("Фильм id={} удалён", id);
}

public Collection<Film> findAll() {
return filmStorage.findAll();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ public User update(User user) {
return userStorage.update(user);
}

// Дружбу, лайки и прочие связи пользователя база удаляет сама — каскадом из schema.sql
public void delete(long id) {
userStorage.delete(id); // нет пользователя — хранилище бросит NotFoundException, это 404
log.debug("Пользователь id={} удалён", id);
}

public Collection<User> findAll() {
return userStorage.findAll();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.context.annotation.Import;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.model.Genre;
import ru.yandex.practicum.filmorate.model.Mpa;
Expand Down Expand Up @@ -135,6 +136,41 @@ void shouldDeleteFilm() {
assertTrue(filmStorage.findById(film.getId()).isEmpty());
}

// Без ON DELETE CASCADE удаление упало бы на внешних ключах film_genres и film_likes
@Test
void shouldDeleteFilmWithGenresAndLikes() {
Film film = newFilm("С жанром и лайком");
film.getGenres().add(genre(1));
film = filmStorage.add(film);
User anna = createUser("anna");
filmStorage.addLike(film.getId(), anna.getId());

filmStorage.delete(film.getId());

assertTrue(filmStorage.findById(film.getId()).isEmpty());
assertTrue(filmStorage.findPopular(10, null, null).isEmpty());
}

@Test
void shouldNotCountLikesOfDeletedUser() {
Film first = createFilm("Без лайков");
Film liked = createFilm("С лайком");
User boris = createUser("boris");
filmStorage.addLike(liked.getId(), boris.getId());
assertEquals(liked.getId(), filmStorage.findPopular(10, null, null).get(0).getId());

userStorage.delete(boris.getId());

// Лайков не осталось ни у кого — фильмы идут по порядку id
List<Long> ids = filmStorage.findPopular(10, null, null).stream().map(Film::getId).toList();
assertEquals(List.of(first.getId(), liked.getId()), ids);
}

@Test
void shouldThrowWhenDeletingUnknownFilm() {
assertThrows(NotFoundException.class, () -> filmStorage.delete(9999));
}

@Test
void shouldAddAndRemoveLike() {
Film film = createFilm("Любимый");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.context.annotation.Import;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.model.User;
import ru.yandex.practicum.filmorate.storage.mapper.UserRowMapper;

Expand Down Expand Up @@ -80,6 +81,25 @@ void shouldDeleteUser() {
assertTrue(userStorage.findById(user.getId()).isEmpty());
}

// Без ON DELETE CASCADE удаление упало бы на внешнем ключе friendship
@Test
void shouldDeleteUserWithFriendships() {
User anna = createUser("anna");
User boris = createUser("boris");
userStorage.addFriend(anna.getId(), boris.getId());
userStorage.addFriend(boris.getId(), anna.getId());

userStorage.delete(boris.getId());

assertTrue(userStorage.findById(boris.getId()).isEmpty());
assertTrue(userStorage.findFriends(anna.getId()).isEmpty());
}

@Test
void shouldThrowWhenDeletingUnknownUser() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для пользователя есть тест на удаление несуществующего id, а для фильма такого нет. Может, добавить симметричный в FilmDbStorageTest? Не блокирует, на усмотрение 🙂

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сначала специально не стала, чтобы не устроить Роме конфликт в импортах.
Но проверила, что встаёт без конфликта, так что добавила)

assertThrows(NotFoundException.class, () -> userStorage.delete(9999));
}

@Test
void shouldAddFriendOneWay() {
User anna = createUser("anna");
Expand Down
Loading