From 3aaba6019c6805612f67a1dcfa9a2c62d1846e43 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 26 Mar 2025 18:02:36 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=20=D0=9C=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=20REST-?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filmorate/FilmorateApplication.java | 7 +- .../filmorate/controller/FilmController.java | 60 ++++++++ .../filmorate/controller/UserController.java | 65 +++++++++ .../exception/ValidationException.java | 7 + .../practicum/filmorate/model/Film.java | 16 ++- .../practicum/filmorate/model/User.java | 13 ++ .../practicum/filmorate/BoundaryTests.java | 56 ++++++++ .../filmorate/FilmControllerTest.java | 113 +++++++++++++++ .../filmorate/UserControllerTest.java | 129 ++++++++++++++++++ 9 files changed, 455 insertions(+), 11 deletions(-) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/model/User.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java index dca451b..3f2baca 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java +++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java @@ -5,8 +5,7 @@ @SpringBootApplication public class FilmorateApplication { - public static void main(String[] args) { - SpringApplication.run(FilmorateApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(FilmorateApplication.class, args); + } } 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 08cf0a1..f5a1938 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,66 @@ import org.springframework.web.bind.annotation.RestController; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import lombok.extern.slf4j.Slf4j; +import ru.yandex.practicum.filmorate.model.Film; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + @RestController +@RequestMapping("/films") +@Slf4j public class FilmController { + private final Map films = new HashMap<>(); + private Long idCounter = 1L; + + private void validateFilm(Film film) { + if (film.getName() == null || film.getName().isBlank()) { + log.warn("Название фильма не может быть пустым"); + throw new ValidationException("Название фильма не может быть пустым"); + } + if (film.getDescription() != null && film.getDescription().length() > 200) { + log.warn("Максимальная длина описания — 200 символов"); + throw new ValidationException("Максимальная длина описания — 200 символов"); + } + if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { + log.warn("Дата релиза должна быть не раньше 28 декабря 1895 года"); + throw new ValidationException("Дата релиза должна быть не раньше 28 декабря 1895 года"); + } + if (film.getDuration() <= 0) { + log.warn("Продолжительность фильма должна быть положительным числом"); + throw new ValidationException("Продолжительность фильма должна быть положительным числом"); + } + } + + @PostMapping + public Film addFilm(@RequestBody Film film) { + validateFilm(film); + film.setId(idCounter++); + films.put(film.getId(), film); + log.info("Добавлен фильм с ID {}: {}", film.getId(), film); + return film; + } + + @PutMapping + public Film updateFilm(@RequestBody Film film) { + if (film.getId() == null || !films.containsKey(film.getId())) { + log.warn("Фильм с ID {} не найден", film.getId()); + throw new ValidationException("Фильм с указанным ID не существует"); + } + validateFilm(film); + films.put(film.getId(), film); + log.info("Обновлен фильм с ID {}: {}", film.getId(), film); + return film; + } + + @GetMapping + public List getAllFilms() { + log.info("Получен запрос всех фильмов. Текущее количество: {}", films.size()); + return new ArrayList<>(films.values()); + } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java new file mode 100644 index 0000000..2e77f19 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -0,0 +1,65 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.extern.slf4j.Slf4j; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/users") +@Slf4j +public class UserController { + private final Map users = new HashMap<>(); + private Long idCounter = 1L; + + private void validateUser(User user) { + if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) { + log.warn("Электронная почта не может быть пустой и должна содержать символ @"); + throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @"); + } + if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { + log.warn("Логин не может быть пустым и содержать пробелы"); + throw new ValidationException("Логин не может быть пустым и содержать пробелы"); + } + if (user.getName() == null || user.getName().isBlank()) { + user.setName(user.getLogin()); + log.info("Для пользователя {} установлено имя из логина", user.getLogin()); + } + if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) { + log.warn("Дата рождения не может быть в будущем"); + throw new ValidationException("Дата рождения не может быть в будущем"); + } + } + + @PostMapping + public User createUser(@RequestBody User user) { + validateUser(user); + user.setId(idCounter++); + users.put(user.getId(), user); + log.info("Создан пользователь: {}", user); + return user; + } + + @PutMapping + public User updateUser(@RequestBody User user) { + if (user.getId() == null || !users.containsKey(user.getId())) { + log.warn("Пользователь с id {} не найден", user.getId()); + throw new ValidationException("Пользователь с указанным id не существует"); + } + validateUser(user); + users.put(user.getId(), user); + log.info("Обновлен пользователь: {}", user); + return user; + } + + @GetMapping + public List getAllUsers() { + log.info("Получен запрос всех пользователей. Текущее количество: {}", users.size()); + return new ArrayList<>(users.values()); + } +} \ 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 new file mode 100644 index 0000000..532f9bd --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} \ 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 3614a44..06dd12b 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -1,12 +1,14 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.Setter; -/** - * Film. - */ -@Getter -@Setter +import lombok.Data; +import java.time.LocalDate; + +@Data public class Film { + private Long id; + private String name; + private String description; + private LocalDate releaseDate; + private int duration; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java new file mode 100644 index 0000000..8a3fbe0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; +import java.time.LocalDate; + +@Data +public class User { + private Long id; + private String email; + private String login; + private String name; + private LocalDate birthday; +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java b/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java new file mode 100644 index 0000000..48e3c78 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java @@ -0,0 +1,56 @@ +package ru.yandex.practicum.filmorate; + +import ru.yandex.practicum.filmorate.controller.FilmController; +import ru.yandex.practicum.filmorate.controller.UserController; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.User; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import static org.junit.jupiter.api.Assertions.*; + +class BoundaryTests { + private FilmController filmController = new FilmController(); + private UserController userController = new UserController(); + + @Test + void filmValidation_BoundaryValues() { + // Проверка минимально допустимой даты релиза + Film film1 = new Film(); + film1.setName("Boundary Film"); + film1.setDescription("Desc"); + film1.setReleaseDate(LocalDate.of(1895, 12, 28)); + film1.setDuration(1); + assertDoesNotThrow(() -> filmController.addFilm(film1)); + + // Проверка максимальной длины описания + Film film2 = new Film(); + film2.setName("Boundary Film"); + film2.setDescription("a".repeat(200)); + film2.setReleaseDate(LocalDate.of(2000, 1, 1)); + film2.setDuration(1); + assertDoesNotThrow(() -> filmController.addFilm(film2)); + } + + @Test + void userValidation_BoundaryValues() { + // Проверка сегодняшней даты рождения + User user1 = new User(); + user1.setEmail("test@example.com"); + user1.setLogin("testlogin"); + user1.setBirthday(LocalDate.now()); + assertDoesNotThrow(() -> userController.createUser(user1)); + + // Проверка минимально допустимого логина + User user2 = new User(); + user2.setEmail("test@example.com"); + user2.setLogin("a"); // Минимальный логин + user2.setBirthday(LocalDate.of(1990, 1, 1)); + assertDoesNotThrow(() -> userController.createUser(user2)); + } + + @Test + void nullRequest_ThrowsException() { + assertThrows(Exception.class, () -> filmController.addFilm(null)); + assertThrows(Exception.class, () -> userController.createUser(null)); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java new file mode 100644 index 0000000..50c0ac7 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java @@ -0,0 +1,113 @@ +package ru.yandex.practicum.filmorate; + +import ru.yandex.practicum.filmorate.controller.FilmController; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Film; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class FilmControllerTest { + private FilmController filmController; + + @BeforeEach + void setUp() { + filmController = new FilmController(); + } + + @Test + void addFilm_ValidFilm_ReturnsFilmWithId() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + Film result = filmController.addFilm(film); + + assertNotNull(result.getId()); + assertEquals(film.getName(), result.getName()); + } + + @Test + void addFilm_EmptyName_ThrowsValidationException() { + Film film = new Film(); + film.setName(""); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void addFilm_TooLongDescription_ThrowsValidationException() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("a".repeat(201)); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void addFilm_TooEarlyReleaseDate_ThrowsValidationException() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(1895, 12, 27)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void addFilm_NegativeDuration_ThrowsValidationException() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(-1); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void updateFilm_NonExistingId_ThrowsValidationException() { + Film film = new Film(); + film.setId(999L); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.updateFilm(film)); + } + + @Test + void getAllFilms_EmptyList_ReturnsEmptyList() { + List films = filmController.getAllFilms(); + assertTrue(films.isEmpty()); + } + + @Test + void getAllFilms_WithFilms_ReturnsAllFilms() { + Film film1 = new Film(); + film1.setName("Film 1"); + film1.setReleaseDate(LocalDate.of(2000, 1, 1)); + film1.setDuration(120); + filmController.addFilm(film1); + + Film film2 = new Film(); + film2.setName("Film 2"); + film2.setReleaseDate(LocalDate.of(2001, 1, 1)); + film2.setDuration(90); + filmController.addFilm(film2); + + List films = filmController.getAllFilms(); + assertEquals(2, films.size()); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java new file mode 100644 index 0000000..527cfa5 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java @@ -0,0 +1,129 @@ +package ru.yandex.practicum.filmorate; + +import ru.yandex.practicum.filmorate.controller.UserController; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class UserControllerTest { + private UserController userController; + + @BeforeEach + void setUp() { + userController = new UserController(); + } + + @Test + void createUser_ValidUser_ReturnsUserWithId() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + User result = userController.createUser(user); + + assertNotNull(result.getId()); + assertEquals(user.getEmail(), result.getEmail()); + } + + @Test + void createUser_EmptyEmail_ThrowsValidationException() { + User user = new User(); + user.setEmail(""); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_InvalidEmailFormat_ThrowsValidationException() { + User user = new User(); + user.setEmail("invalid-email"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_EmptyLogin_ThrowsValidationException() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin(""); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_LoginWithSpaces_ThrowsValidationException() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("test login"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_EmptyName_SetsNameFromLogin() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setName(""); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + User result = userController.createUser(user); + assertEquals(user.getLogin(), result.getName()); + } + + @Test + void createUser_FutureBirthday_ThrowsValidationException() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.now().plusDays(1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void updateUser_NonExistingId_ThrowsValidationException() { + User user = new User(); + user.setId(999L); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.updateUser(user)); + } + + @Test + void getAllUsers_EmptyList_ReturnsEmptyList() { + List users = userController.getAllUsers(); + assertTrue(users.isEmpty()); + } + + @Test + void getAllUsers_WithUsers_ReturnsAllUsers() { + User user1 = new User(); + user1.setEmail("user1@example.com"); + user1.setLogin("user1"); + user1.setBirthday(LocalDate.of(1990, 1, 1)); + userController.createUser(user1); + + User user2 = new User(); + user2.setEmail("user2@example.com"); + user2.setLogin("user2"); + user2.setBirthday(LocalDate.of(1991, 1, 1)); + userController.createUser(user2); + + List users = userController.getAllUsers(); + assertEquals(2, users.size()); + } +} \ No newline at end of file From 297dd1194a4c414a153609ae9e1f638481bcb8d2 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Mar 2025 12:18:14 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=87=D0=B8=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B0=D0=BD=D0=BD=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B8=20=D0=B0=D0=BD=D0=BD=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20@Valid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 ++ .../filmorate/controller/FilmController.java | 6 +- .../filmorate/controller/UserController.java | 6 +- .../practicum/filmorate/model/Film.java | 16 +++- .../practicum/filmorate/model/User.java | 8 ++ .../filmorate/FilmControllerTest.java | 78 ++++++++-------- .../filmorate/UserControllerTest.java | 90 ++++++++----------- 7 files changed, 111 insertions(+), 99 deletions(-) diff --git a/pom.xml b/pom.xml index 0cad031..00a0703 100644 --- a/pom.xml +++ b/pom.xml @@ -27,11 +27,17 @@ lombok provided + org.springframework.boot spring-boot-starter-test test + + + org.springframework.boot + spring-boot-starter-validation + 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 f5a1938..f51bdc0 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -2,10 +2,12 @@ import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; import ru.yandex.practicum.filmorate.exception.ValidationException; import lombok.extern.slf4j.Slf4j; import ru.yandex.practicum.filmorate.model.Film; import org.springframework.web.bind.annotation.*; + import java.time.LocalDate; import java.util.ArrayList; import java.util.HashMap; @@ -39,7 +41,7 @@ private void validateFilm(Film film) { } @PostMapping - public Film addFilm(@RequestBody Film film) { + public Film addFilm(@Valid @RequestBody Film film) { validateFilm(film); film.setId(idCounter++); films.put(film.getId(), film); @@ -48,7 +50,7 @@ public Film addFilm(@RequestBody Film film) { } @PutMapping - public Film updateFilm(@RequestBody Film film) { + public Film updateFilm(@Valid @RequestBody Film film) { if (film.getId() == null || !films.containsKey(film.getId())) { log.warn("Фильм с ID {} не найден", film.getId()); throw new ValidationException("Фильм с указанным ID не существует"); diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java index 2e77f19..acff70f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -1,9 +1,11 @@ package ru.yandex.practicum.filmorate.controller; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.User; import org.springframework.web.bind.annotation.*; + import java.time.LocalDate; import java.util.ArrayList; import java.util.HashMap; @@ -37,7 +39,7 @@ private void validateUser(User user) { } @PostMapping - public User createUser(@RequestBody User user) { + public User createUser(@Valid @RequestBody User user) { validateUser(user); user.setId(idCounter++); users.put(user.getId(), user); @@ -46,7 +48,7 @@ public User createUser(@RequestBody User user) { } @PutMapping - public User updateUser(@RequestBody User user) { + public User updateUser(@Valid @RequestBody User user) { if (user.getId() == null || !users.containsKey(user.getId())) { log.warn("Пользователь с id {} не найден", user.getId()); throw new ValidationException("Пользователь с указанным id не существует"); 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 06dd12b..ae6de4f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -1,14 +1,28 @@ package ru.yandex.practicum.filmorate.model; - +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; + @Data public class Film { private Long id; + + @NotBlank private String name; + + @NotBlank + @Size(min = 1, max = 200) private String description; + + @NotNull private LocalDate releaseDate; + + @Min(1) private int duration; } 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 8a3fbe0..15af210 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -1,12 +1,20 @@ package ru.yandex.practicum.filmorate.model; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; import lombok.Data; + import java.time.LocalDate; @Data public class User { private Long id; + + @NotBlank + @Email private String email; + + @NotBlank private String login; private String name; private LocalDate birthday; diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java index 50c0ac7..0c95ae8 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java @@ -1,12 +1,13 @@ package ru.yandex.practicum.filmorate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ru.yandex.practicum.filmorate.controller.FilmController; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.Film; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + import java.time.LocalDate; -import java.util.List; + import static org.junit.jupiter.api.Assertions.*; class FilmControllerTest { @@ -18,24 +19,23 @@ void setUp() { } @Test - void addFilm_ValidFilm_ReturnsFilmWithId() { + void addValidFilm() { Film film = new Film(); film.setName("Valid Film"); - film.setDescription("Valid description"); + film.setDescription("Valid Description"); film.setReleaseDate(LocalDate.of(2000, 1, 1)); film.setDuration(120); - Film result = filmController.addFilm(film); - - assertNotNull(result.getId()); - assertEquals(film.getName(), result.getName()); + Film addedFilm = filmController.addFilm(film); + assertNotNull(addedFilm.getId()); + assertEquals(1, filmController.getAllFilms().size()); } @Test - void addFilm_EmptyName_ThrowsValidationException() { + void addFilmWithEmptyName() { Film film = new Film(); film.setName(""); - film.setDescription("Valid description"); + film.setDescription("Description"); film.setReleaseDate(LocalDate.of(2000, 1, 1)); film.setDuration(120); @@ -43,10 +43,10 @@ void addFilm_EmptyName_ThrowsValidationException() { } @Test - void addFilm_TooLongDescription_ThrowsValidationException() { + void addFilmWithLongDescription() { Film film = new Film(); - film.setName("Valid Film"); - film.setDescription("a".repeat(201)); + film.setName("Name"); + film.setDescription("A".repeat(201)); film.setReleaseDate(LocalDate.of(2000, 1, 1)); film.setDuration(120); @@ -54,10 +54,10 @@ void addFilm_TooLongDescription_ThrowsValidationException() { } @Test - void addFilm_TooEarlyReleaseDate_ThrowsValidationException() { + void addFilmWithEarlyReleaseDate() { Film film = new Film(); - film.setName("Valid Film"); - film.setDescription("Valid description"); + film.setName("Name"); + film.setDescription("Description"); film.setReleaseDate(LocalDate.of(1895, 12, 27)); film.setDuration(120); @@ -65,22 +65,22 @@ void addFilm_TooEarlyReleaseDate_ThrowsValidationException() { } @Test - void addFilm_NegativeDuration_ThrowsValidationException() { + void addFilmWithNegativeDuration() { Film film = new Film(); - film.setName("Valid Film"); - film.setDescription("Valid description"); + film.setName("Name"); + film.setDescription("Description"); film.setReleaseDate(LocalDate.of(2000, 1, 1)); - film.setDuration(-1); + film.setDuration(-120); assertThrows(ValidationException.class, () -> filmController.addFilm(film)); } @Test - void updateFilm_NonExistingId_ThrowsValidationException() { + void updateFilmWithInvalidId() { Film film = new Film(); film.setId(999L); - film.setName("Valid Film"); - film.setDescription("Valid description"); + film.setName("Name"); + film.setDescription("Description"); film.setReleaseDate(LocalDate.of(2000, 1, 1)); film.setDuration(120); @@ -88,26 +88,22 @@ void updateFilm_NonExistingId_ThrowsValidationException() { } @Test - void getAllFilms_EmptyList_ReturnsEmptyList() { - List films = filmController.getAllFilms(); - assertTrue(films.isEmpty()); - } + void getAllFilms() { + Film film1 = createTestFilm("Film 1"); + Film film2 = createTestFilm("Film 2"); - @Test - void getAllFilms_WithFilms_ReturnsAllFilms() { - Film film1 = new Film(); - film1.setName("Film 1"); - film1.setReleaseDate(LocalDate.of(2000, 1, 1)); - film1.setDuration(120); filmController.addFilm(film1); - - Film film2 = new Film(); - film2.setName("Film 2"); - film2.setReleaseDate(LocalDate.of(2001, 1, 1)); - film2.setDuration(90); filmController.addFilm(film2); - List films = filmController.getAllFilms(); - assertEquals(2, films.size()); + assertEquals(2, filmController.getAllFilms().size()); + } + + private Film createTestFilm(String name) { + Film film = new Film(); + film.setName(name); + film.setDescription("Description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + return film; } } \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java index 527cfa5..a1a9f16 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java @@ -1,12 +1,13 @@ package ru.yandex.practicum.filmorate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ru.yandex.practicum.filmorate.controller.UserController; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.User; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + import java.time.LocalDate; -import java.util.List; + import static org.junit.jupiter.api.Assertions.*; class UserControllerTest { @@ -18,42 +19,42 @@ void setUp() { } @Test - void createUser_ValidUser_ReturnsUserWithId() { + void createValidUser() { User user = new User(); - user.setEmail("test@example.com"); + user.setEmail("test@mail.com"); user.setLogin("testlogin"); user.setBirthday(LocalDate.of(1990, 1, 1)); - User result = userController.createUser(user); - - assertNotNull(result.getId()); - assertEquals(user.getEmail(), result.getEmail()); + User createdUser = userController.createUser(user); + assertNotNull(createdUser.getId()); + assertEquals("testlogin", createdUser.getName()); + assertEquals(1, userController.getAllUsers().size()); } @Test - void createUser_EmptyEmail_ThrowsValidationException() { + void createUserWithEmptyEmail() { User user = new User(); user.setEmail(""); - user.setLogin("testlogin"); + user.setLogin("login"); user.setBirthday(LocalDate.of(1990, 1, 1)); assertThrows(ValidationException.class, () -> userController.createUser(user)); } @Test - void createUser_InvalidEmailFormat_ThrowsValidationException() { + void createUserWithInvalidEmail() { User user = new User(); user.setEmail("invalid-email"); - user.setLogin("testlogin"); + user.setLogin("login"); user.setBirthday(LocalDate.of(1990, 1, 1)); assertThrows(ValidationException.class, () -> userController.createUser(user)); } @Test - void createUser_EmptyLogin_ThrowsValidationException() { + void createUserWithEmptyLogin() { User user = new User(); - user.setEmail("test@example.com"); + user.setEmail("test@mail.com"); user.setLogin(""); user.setBirthday(LocalDate.of(1990, 1, 1)); @@ -61,69 +62,52 @@ void createUser_EmptyLogin_ThrowsValidationException() { } @Test - void createUser_LoginWithSpaces_ThrowsValidationException() { + void createUserWithLoginContainingSpaces() { User user = new User(); - user.setEmail("test@example.com"); - user.setLogin("test login"); + user.setEmail("test@mail.com"); + user.setLogin("login with spaces"); user.setBirthday(LocalDate.of(1990, 1, 1)); assertThrows(ValidationException.class, () -> userController.createUser(user)); } @Test - void createUser_EmptyName_SetsNameFromLogin() { + void createUserWithFutureBirthday() { User user = new User(); - user.setEmail("test@example.com"); - user.setLogin("testlogin"); - user.setName(""); - user.setBirthday(LocalDate.of(1990, 1, 1)); - - User result = userController.createUser(user); - assertEquals(user.getLogin(), result.getName()); - } - - @Test - void createUser_FutureBirthday_ThrowsValidationException() { - User user = new User(); - user.setEmail("test@example.com"); - user.setLogin("testlogin"); + user.setEmail("test@mail.com"); + user.setLogin("login"); user.setBirthday(LocalDate.now().plusDays(1)); assertThrows(ValidationException.class, () -> userController.createUser(user)); } @Test - void updateUser_NonExistingId_ThrowsValidationException() { + void updateUserWithInvalidId() { User user = new User(); user.setId(999L); - user.setEmail("test@example.com"); - user.setLogin("testlogin"); + user.setEmail("test@mail.com"); + user.setLogin("login"); user.setBirthday(LocalDate.of(1990, 1, 1)); assertThrows(ValidationException.class, () -> userController.updateUser(user)); } @Test - void getAllUsers_EmptyList_ReturnsEmptyList() { - List users = userController.getAllUsers(); - assertTrue(users.isEmpty()); - } + void getAllUsers() { + User user1 = createTestUser("user1@mail.com", "user1"); + User user2 = createTestUser("user2@mail.com", "user2"); - @Test - void getAllUsers_WithUsers_ReturnsAllUsers() { - User user1 = new User(); - user1.setEmail("user1@example.com"); - user1.setLogin("user1"); - user1.setBirthday(LocalDate.of(1990, 1, 1)); userController.createUser(user1); - - User user2 = new User(); - user2.setEmail("user2@example.com"); - user2.setLogin("user2"); - user2.setBirthday(LocalDate.of(1991, 1, 1)); userController.createUser(user2); - List users = userController.getAllUsers(); - assertEquals(2, users.size()); + assertEquals(2, userController.getAllUsers().size()); + } + + private User createTestUser(String email, String login) { + User user = new User(); + user.setEmail(email); + user.setLogin(login); + user.setBirthday(LocalDate.of(1990, 1, 1)); + return user; } } \ No newline at end of file From e5a69d56cae3e4524ee5223ccd3de7cc517c4c86 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 22 Apr 2025 10:17:48 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=D0=B0=20=D0=B0=D1=80=D1=85=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B0=20=D0=B8=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 ++ .../filmorate/controller/FilmController.java | 104 +++++++++++------- .../filmorate/controller/UserController.java | 104 +++++++++++------- .../exception/NotFoundException.java | 20 ++++ .../filmorate/service/FilmService.java | 48 ++++++++ .../filmorate/service/UserService.java | 49 +++++++++ .../filmorate/storage/FilmStorage.java | 15 +++ .../storage/InMemoryFilmStorage.java | 79 +++++++++++++ .../storage/InMemoryUserStorage.java | 89 +++++++++++++++ .../filmorate/storage/UserStorage.java | 16 +++ src/main/resources/application.properties | 2 +- 11 files changed, 451 insertions(+), 82 deletions(-) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/service/UserService.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java diff --git a/pom.xml b/pom.xml index 00a0703..9c95881 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,13 @@ org.springframework.boot spring-boot-starter-validation + + + org.zalando + logbook-spring-boot-starter + 3.7.2 + + 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 f51bdc0..d7dd0ee 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -1,69 +1,91 @@ package ru.yandex.practicum.filmorate.controller; -import org.springframework.web.bind.annotation.RestController; - +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; import ru.yandex.practicum.filmorate.exception.ValidationException; -import lombok.extern.slf4j.Slf4j; import ru.yandex.practicum.filmorate.model.Film; -import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.service.FilmService; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RestController @RequestMapping("/films") @Slf4j public class FilmController { - private final Map films = new HashMap<>(); - private Long idCounter = 1L; + private final FilmService filmService; - private void validateFilm(Film film) { - if (film.getName() == null || film.getName().isBlank()) { - log.warn("Название фильма не может быть пустым"); - throw new ValidationException("Название фильма не может быть пустым"); - } - if (film.getDescription() != null && film.getDescription().length() > 200) { - log.warn("Максимальная длина описания — 200 символов"); - throw new ValidationException("Максимальная длина описания — 200 символов"); - } - if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { - log.warn("Дата релиза должна быть не раньше 28 декабря 1895 года"); - throw new ValidationException("Дата релиза должна быть не раньше 28 декабря 1895 года"); - } - if (film.getDuration() <= 0) { - log.warn("Продолжительность фильма должна быть положительным числом"); - throw new ValidationException("Продолжительность фильма должна быть положительным числом"); - } + public FilmController(FilmService filmService) { + this.filmService = filmService; + } + + @GetMapping + public List getAllFilms() { + log.info("Fetching all films"); + return filmService.getAllFilms(); + } + + @GetMapping("/{id}") + public Film getFilm(@PathVariable Long id) { + log.info("Fetching film with id {}", id); + return filmService.getFilmById(id); } @PostMapping public Film addFilm(@Valid @RequestBody Film film) { + log.info("Adding film: {}", film); validateFilm(film); - film.setId(idCounter++); - films.put(film.getId(), film); - log.info("Добавлен фильм с ID {}: {}", film.getId(), film); - return film; + return filmService.addFilm(film); } @PutMapping public Film updateFilm(@Valid @RequestBody Film film) { - if (film.getId() == null || !films.containsKey(film.getId())) { - log.warn("Фильм с ID {} не найден", film.getId()); - throw new ValidationException("Фильм с указанным ID не существует"); - } + log.info("Updating film: {}", film); validateFilm(film); - films.put(film.getId(), film); - log.info("Обновлен фильм с ID {}: {}", film.getId(), film); - return film; + return filmService.updateFilm(film); } - @GetMapping - public List getAllFilms() { - log.info("Получен запрос всех фильмов. Текущее количество: {}", films.size()); - return new ArrayList<>(films.values()); + @DeleteMapping("/{id}") + public void deleteFilm(@PathVariable Long id) { + log.info("Deleting film with id {}", id); + filmService.removeFilm(id); + } + + @PutMapping("/{id}/like/{userId}") + public void addLike(@PathVariable Long id, @PathVariable Long userId) { + log.info("User {} likes film {}", userId, id); + filmService.addLike(id, userId); + } + + @DeleteMapping("/{id}/like/{userId}") + public void removeLike(@PathVariable Long id, @PathVariable Long userId) { + log.info("User {} removes like from film {}", userId, id); + filmService.removeLike(id, userId); + } + + @GetMapping("/popular") + public List getPopularFilms(@RequestParam(defaultValue = "10") int count) { + log.info("Fetching top {} popular films", count); + return filmService.getPopularFilms(count); + } + + private void validateFilm(Film film) { + if (film.getName() == null || film.getName().isBlank()) { + log.warn("Film name is invalid"); + throw new ValidationException("Film name is invalid"); + } + if (film.getDescription() != null && film.getDescription().length() > 200) { + log.warn("Film description is too long"); + throw new ValidationException("Description length exceeds 200 characters"); + } + if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { + log.warn("Film release date is invalid"); + throw new ValidationException("Release date is invalid"); + } + if (film.getDuration() <= 0) { + log.warn("Film duration is invalid"); + throw new ValidationException("Duration must be positive"); + } } } 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 acff70f..30209bd 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -1,67 +1,91 @@ package ru.yandex.practicum.filmorate.controller; -import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import jakarta.validation.Valid; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.model.User; -import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.service.UserService; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RestController @RequestMapping("/users") @Slf4j public class UserController { - private final Map users = new HashMap<>(); - private Long idCounter = 1L; + private final UserService userService; - private void validateUser(User user) { - if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) { - log.warn("Электронная почта не может быть пустой и должна содержать символ @"); - throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @"); - } - if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { - log.warn("Логин не может быть пустым и содержать пробелы"); - throw new ValidationException("Логин не может быть пустым и содержать пробелы"); - } - if (user.getName() == null || user.getName().isBlank()) { - user.setName(user.getLogin()); - log.info("Для пользователя {} установлено имя из логина", user.getLogin()); - } - if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) { - log.warn("Дата рождения не может быть в будущем"); - throw new ValidationException("Дата рождения не может быть в будущем"); - } + public UserController(UserService userService) { + this.userService = userService; + } + + @GetMapping + public List getAllUsers() { + log.info("Fetching all users"); + return userService.getAllUsers(); + } + + @GetMapping("/{id}") + public User getUser(@PathVariable Long id) { + log.info("Fetching user with id {}", id); + return userService.getUserById(id); } @PostMapping public User createUser(@Valid @RequestBody User user) { + log.info("Creating user: {}", user); validateUser(user); - user.setId(idCounter++); - users.put(user.getId(), user); - log.info("Создан пользователь: {}", user); - return user; + return userService.createUser(user); } @PutMapping public User updateUser(@Valid @RequestBody User user) { - if (user.getId() == null || !users.containsKey(user.getId())) { - log.warn("Пользователь с id {} не найден", user.getId()); - throw new ValidationException("Пользователь с указанным id не существует"); - } + log.info("Updating user: {}", user); validateUser(user); - users.put(user.getId(), user); - log.info("Обновлен пользователь: {}", user); - return user; + return userService.updateUser(user); } - @GetMapping - public List getAllUsers() { - log.info("Получен запрос всех пользователей. Текущее количество: {}", users.size()); - return new ArrayList<>(users.values()); + @PutMapping("/{id}/friends/{friendId}") + public void addFriend(@PathVariable Long id, @PathVariable Long friendId) { + log.info("User {} adds friend {}", id, friendId); + userService.addFriend(id, friendId); + } + + @DeleteMapping("/{id}/friends/{friendId}") + public void removeFriend(@PathVariable Long id, @PathVariable Long friendId) { + log.info("User {} removes friend {}", id, friendId); + userService.removeFriend(id, friendId); + } + + @GetMapping("/{id}/friends") + public List getFriends(@PathVariable Long id) { + log.info("Fetching friends of user {}", id); + return userService.getFriends(id); + } + + @GetMapping("/{id}/friends/common/{otherId}") + public List getCommonFriends(@PathVariable Long id, @PathVariable Long otherId) { + log.info("Fetching common friends of users {} and {}", id, otherId); + return userService.getCommonFriends(id, otherId); + } + + private void validateUser(User user) { + if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) { + log.warn("Email is invalid"); + throw new ValidationException("Email is invalid"); + } + if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { + log.warn("Login is invalid"); + throw new ValidationException("Login is invalid"); + } + if (user.getName() == null || user.getName().isBlank()) { + user.setName(user.getLogin()); + log.info("Name set to login for user {}", user.getLogin()); + } + if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) { + log.warn("Birthday is in the future"); + throw new ValidationException("Birthday is invalid"); + } } -} \ No newline at end of file +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java new file mode 100644 index 0000000..ad7129c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.exception; + + +public class NotFoundException extends RuntimeException { + public NotFoundException() { + super(); + } + + public NotFoundException(String message) { + super(message); + } + + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public NotFoundException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java new file mode 100644 index 0000000..ecea46f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -0,0 +1,48 @@ +package ru.yandex.practicum.filmorate.service; + +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.storage.FilmStorage; + +import java.util.List; + +@Service +public class FilmService { + private final FilmStorage filmStorage; + + public FilmService(FilmStorage filmStorage) { + this.filmStorage = filmStorage; + } + + public Film addFilm(Film film) { + return filmStorage.addFilm(film); + } + + public Film updateFilm(Film film) { + return filmStorage.updateFilm(film); + } + + public Film getFilmById(Long id) { + return filmStorage.getFilmById(id); + } + + public List getAllFilms() { + return filmStorage.getAllFilms(); + } + + public void removeFilm(Long id) { + filmStorage.removeFilm(id); + } + + public void addLike(Long filmId, Long userId) { + filmStorage.addLike(filmId, userId); + } + + public void removeLike(Long filmId, Long userId) { + filmStorage.removeLike(filmId, userId); + } + + public List getPopularFilms(int count) { + return filmStorage.getPopularFilms(count); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java new file mode 100644 index 0000000..64b1bfb --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -0,0 +1,49 @@ +package ru.yandex.practicum.filmorate.service; + +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.UserStorage; + +import java.util.List; + +@Service +public class UserService { + private final UserStorage userStorage; + + public UserService(UserStorage userStorage) { + this.userStorage = userStorage; + } + + public User createUser(User user) { + return userStorage.addUser(user); + } + + public User updateUser(User user) { + return userStorage.updateUser(user); + } + + public User getUserById(Long id) { + return userStorage.getUserById(id); + } + + public List getAllUsers() { + return userStorage.getAllUsers(); + } + + public void addFriend(Long userId, Long friendId) { + userStorage.addFriend(userId, friendId); + } + + public void removeFriend(Long userId, Long friendId) { + userStorage.removeFriend(userId, friendId); + } + + public List getFriends(Long userId) { + return userStorage.getFriends(userId); + } + + public List getCommonFriends(Long userId, Long otherId) { + return userStorage.getCommonFriends(userId, otherId); + } +} + diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java new file mode 100644 index 0000000..1c0d68e --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java @@ -0,0 +1,15 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.Film; +import java.util.List; + +public interface FilmStorage { + Film addFilm(Film film); + Film updateFilm(Film film); + Film getFilmById(Long id); + List getAllFilms(); + void removeFilm(Long id); + void addLike(Long filmId, Long userId); + void removeLike(Long filmId, Long userId); + List getPopularFilms(int count); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java new file mode 100644 index 0000000..57f74b4 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java @@ -0,0 +1,79 @@ +package ru.yandex.practicum.filmorate.storage; + +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.*; + +@Component +public class InMemoryFilmStorage implements FilmStorage { + private final Map films = new HashMap<>(); + private final Map> likes = new HashMap<>(); + private long idCounter = 1L; + + @Override + public Film addFilm(Film film) { + film.setId(idCounter++); + films.put(film.getId(), film); + likes.put(film.getId(), new HashSet<>()); + return film; + } + + @Override + public Film updateFilm(Film film) { + if (film.getId() == null || !films.containsKey(film.getId())) { + throw new NotFoundException("Film not found"); + } + films.put(film.getId(), film); + return film; + } + + @Override + public Film getFilmById(Long id) { + Film film = films.get(id); + if (film == null) { + throw new NotFoundException("Film not found"); + } + return film; + } + + @Override + public List getAllFilms() { + return new ArrayList<>(films.values()); + } + + @Override + public void removeFilm(Long id) { + if (!films.containsKey(id)) { + throw new NotFoundException("Film not found"); + } + films.remove(id); + likes.remove(id); + } + + @Override + public void addLike(Long filmId, Long userId) { + if (!films.containsKey(filmId)) { + throw new NotFoundException("Film not found"); + } + likes.get(filmId).add(userId); + } + + @Override + public void removeLike(Long filmId, Long userId) { + if (!films.containsKey(filmId)) { + throw new NotFoundException("Film not found"); + } + likes.get(filmId).remove(userId); + } + + @Override + public List getPopularFilms(int count) { + return films.values().stream() + .sorted((f1, f2) -> Integer.compare( + likes.get(f2.getId()).size(), likes.get(f1.getId()).size())) + .limit(count) + .toList(); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java new file mode 100644 index 0000000..620aa36 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java @@ -0,0 +1,89 @@ +package ru.yandex.practicum.filmorate.storage; + +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.exception.NotFoundException; + +import java.util.*; + +@Component +public class InMemoryUserStorage implements UserStorage { + private final Map users = new HashMap<>(); + private final Map> friends = new HashMap<>(); + private long idCounter = 1L; + + @Override + public User addUser(User user) { + user.setId(idCounter++); + users.put(user.getId(), user); + friends.put(user.getId(), new HashSet<>()); + return user; + } + + @Override + public User updateUser(User user) { + if (user.getId() == null || !users.containsKey(user.getId())) { + throw new NotFoundException("User not found"); + } + users.put(user.getId(), user); + return user; + } + + @Override + public User getUserById(Long id) { + User user = users.get(id); + if (user == null) { + throw new NotFoundException("User not found"); + } + return user; + } + + @Override + public List getAllUsers() { + return new ArrayList<>(users.values()); + } + + @Override + public void removeUser(Long id) { + if (!users.containsKey(id)) { + throw new NotFoundException("User not found"); + } + users.remove(id); + friends.remove(id); + } + + @Override + public void addFriend(Long userId, Long friendId) { + getUserById(userId); + getUserById(friendId); + friends.get(userId).add(friendId); + friends.get(friendId).add(userId); + } + + @Override + public void removeFriend(Long userId, Long friendId) { + getUserById(userId); + getUserById(friendId); + friends.get(userId).remove(friendId); + friends.get(friendId).remove(userId); + } + + @Override + public List getFriends(Long userId) { + getUserById(userId); + return friends.get(userId).stream() + .map(users::get) + .toList(); + } + + @Override + public List getCommonFriends(Long userId, Long otherId) { + getUserById(userId); + getUserById(otherId); + Set common = new HashSet<>(friends.get(userId)); + common.retainAll(friends.get(otherId)); + return common.stream() + .map(users::get) + .toList(); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java new file mode 100644 index 0000000..97a3dad --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.User; +import java.util.List; + +public interface UserStorage { + User addUser(User user); + User updateUser(User user); + User getUserById(Long id); + List getAllUsers(); + void removeUser(Long id); + void addFriend(Long userId, Long friendId); + void removeFriend(Long userId, Long friendId); + List getFriends(Long userId); + List getCommonFriends(Long userId, Long otherId); +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..1d1b4bf 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1 @@ - +logging.level.org.zalando.logbook: TRACE