From 80543c40644ceb65d03c3538f4ef6b48da5e0c29 Mon Sep 17 00:00:00 2001 From: vladimir Date: Sun, 25 May 2025 13:35:05 +0300 Subject: [PATCH 1/2] =?UTF-8?q?controllers-films-users.=20=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B7=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20spring.=20=D0=A1?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D1=82=D1=8C=20=D0=BC=D0=BE=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B8=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=20?= =?UTF-8?q?=D0=BA=D0=BE=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 + .../filmorate/controller/FilmController.java | 64 ++++++++++- .../filmorate/controller/UserController.java | 100 ++++++++++++++++++ .../exception/ValidationException.java | 7 ++ .../practicum/filmorate/model/Film.java | 37 +++++-- .../practicum/filmorate/model/User.java | 31 ++++++ .../yandex/practicum/filmorate/FilmTest.java | 61 +++++++++++ .../yandex/practicum/filmorate/UserTest.java | 33 ++++++ 8 files changed, 328 insertions(+), 9 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/FilmTest.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/UserTest.java diff --git a/pom.xml b/pom.xml index 0cad031..601e44b 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,10 @@ 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 08cf0a1..173eba9 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -1,7 +1,69 @@ package ru.yandex.practicum.filmorate.controller; -import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Film; +import java.time.LocalDate; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +@Slf4j @RestController +@RequestMapping(value = "/films") public class FilmController { + + private final Map films = new HashMap<>(); + + @GetMapping + public Collection findAll() { + return films.values(); + } + + @PostMapping + public Film create(@Valid @RequestBody Film film) { + + if (film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { + log.debug("Дата рождения не может быть в будущем"); + throw new ValidationException("Дата рождения не может быть в будущем"); + } + + film.setId(getNextId()); + + films.put(film.getId(), film); + log.info("Фильм добавлен"); + return film; + } + + @PutMapping + public Film update(@Valid @RequestBody Film newFilm) { + if (newFilm.getId() == null) { + log.debug("Не указан Id при обновлении фильма"); + throw new ValidationException("Не указан Id при обновлении фильма"); + } + if (films.containsKey(newFilm.getId())) { + Film oldFilm = films.get(newFilm.getId()); + oldFilm.setName(newFilm.getName()); + oldFilm.setDescription(newFilm.getDescription()); + oldFilm.setReleaseDate(newFilm.getReleaseDate()); + oldFilm.setDuration(newFilm.getDuration()); + log.info("Фильм с id " + newFilm.getId() + " обновлен"); + return oldFilm; + } + log.debug("Фильм с id = " + newFilm.getId() + " не найден"); + throw new ValidationException("Фильм с id = " + newFilm.getId() + " не найден"); + } + + private long getNextId() { + long currentMaxId = films.keySet() + .stream() + .mapToLong(id -> id) + .max() + .orElse(0); + return ++currentMaxId; + } + } 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..ceb8291 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -0,0 +1,100 @@ +package ru.yandex.practicum.filmorate.controller; + +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping(value = "/users") +public class UserController { + + private final Map users = new HashMap<>(); + + @GetMapping + public Collection findAll() { + return users.values(); + } + + @PostMapping + public User create(@Valid @RequestBody User user) { + + if (checkEmail(user.getEmail())) { + log.debug("Пользователь с почтой " + user.getEmail() + " уже существует"); + throw new ValidationException("Пользователь с почтой " + user.getEmail() + " уже существует"); + } + if (checkLogin(user.getLogin())) { + log.debug("Пользователь с логином " + user.getLogin() + " уже существует"); + throw new ValidationException("Пользователь с логином " + user.getLogin() + " уже существует"); + } + if (user.getLogin().contains(" ")) { + log.debug("Логин должен быть без пробелов"); + throw new ValidationException("Логин должен быть без пробелов"); + } + if (user.getBirthday().isAfter(LocalDate.now())) { + log.debug("Дата рождения не может быть в будущем"); + throw new ValidationException("Дата рождения не может быть в будущем"); + } + + user.setId(getNextId()); + if (user.getName() == null || user.getName().isBlank()) + user.setName(user.getLogin()); + else user.setName(user.getName()); + + users.put(user.getId(), user); + log.info("Фильм добавлен"); + return user; + } + + @PutMapping + public User update(@Valid @RequestBody User newUser) { + // проверяем необходимые условия + if (newUser.getId() == null) { + log.debug("Не указан Id при обновлении пользователя"); + throw new ValidationException("Не указан Id при обновлении пользователя"); + } + if (checkEmail(newUser.getEmail())) { + log.debug("Пользователь с почтой " + newUser.getEmail() + " уже существует"); + throw new ValidationException("Пользователь с почтой " + newUser.getEmail() + " уже существует"); + } + if (users.containsKey(newUser.getId())) { + User oldUser = users.get(newUser.getId()); + oldUser.setEmail(newUser.getEmail()); + oldUser.setName(newUser.getName()); + oldUser.setLogin(newUser.getLogin()); + oldUser.setBirthday(newUser.getBirthday()); + log.info("Пользователь с id " + newUser.getId() + " обновлен"); + return oldUser; + } + log.debug("Пользователь с id = " + newUser.getId() + " не найден"); + throw new ValidationException("Пользователь с id = " + newUser.getId() + " не найден"); + } + + private long getNextId() { + long currentMaxId = users.keySet() + .stream() + .mapToLong(id -> id) + .max() + .orElse(0); + return ++currentMaxId; + } + + private boolean checkEmail(String email) { + return users.values().stream() + .map(User::getEmail) + .anyMatch(email::equals); + } + + private boolean checkLogin(String login) { + return users.values().stream() + .map(User::getEmail) + .anyMatch(login::equals); + } +} 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..52dc49c --- /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); + } +} 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..80e3ee9 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,33 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.Setter; - -/** - * Film. - */ -@Getter -@Setter +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor public class Film { + + private Long id; + + @NotNull + @NotBlank + private String name; + + @Size(max = 200) + private String description; + + private LocalDate releaseDate; + + @Positive + private Integer 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..80eea4c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -0,0 +1,31 @@ +package ru.yandex.practicum.filmorate.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class User { + + private Long id; + + @Email + private String email; + + @NotNull + @NotBlank + private String login; + + private String name; + + private LocalDate birthday; +} diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmTest.java new file mode 100644 index 0000000..ff63b56 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmTest.java @@ -0,0 +1,61 @@ +package ru.yandex.practicum.filmorate; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import ru.yandex.practicum.filmorate.model.Film; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Set; + +public class FilmTest { + + private final Film film = Film.builder() + .name("nisi eiusmod") + .description("adipisicing") + .releaseDate(LocalDate.parse("1895-12-28").plusDays(1)) + .duration(100) + .build(); + private final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); + private final Validator validator = validatorFactory.getValidator(); + + @Test + void shouldCreateFilm() { + Set> violations = validator.validate(film); + + Assertions.assertTrue(violations.isEmpty()); + } + + @Test + void shouldNotCreateFilmIfNameIsEmpty() { + String[] names = {"", " ", " ", null}; + + Arrays.stream(names).forEach(name -> { + Film filmWithIncorrectName = film + .toBuilder() + .name(name) + .build(); + + Set> violations = validator.validate(filmWithIncorrectName); + + Assertions.assertFalse(violations.isEmpty()); + }); + } + + @Test + void shouldNotCreateFilmIfDurationIsWrong() { + Film filmWithIncorrectDuration = film + .toBuilder() + .duration(-100) + .build(); + + Set> violations = validator.validate(filmWithIncorrectDuration); + + Assertions.assertFalse(violations.isEmpty()); + Assertions.assertEquals(1, violations.size()); + } +} diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserTest.java new file mode 100644 index 0000000..b54698b --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/UserTest.java @@ -0,0 +1,33 @@ +package ru.yandex.practicum.filmorate; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import ru.yandex.practicum.filmorate.model.User; + +import java.time.LocalDate; +import java.util.Set; + +public class UserTest { + + private final User user = User + .builder() + .login("a_b") + .name("Nick Name") + .email("mail@mail.ru") + .birthday(LocalDate.now()) + .build(); + private final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); + private final Validator validator = validatorFactory.getValidator(); + + @Test + void shouldCreateUser() { + Set> violations = validator.validate(user); + + Assertions.assertTrue(violations.isEmpty()); + } + +} From 7225eb6c32e8d84f28bba5b593e3b5b5366e22e3 Mon Sep 17 00:00:00 2001 From: vladimir Date: Wed, 28 May 2025 18:55:54 +0300 Subject: [PATCH 2/2] =?UTF-8?q?controllers-films-users.=20=D0=A0=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E.=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20spring.=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=20=D0=BA=D0=BE=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filmorate/controller/FilmController.java | 15 +++-- .../filmorate/controller/UserController.java | 66 ++++++++++--------- .../practicum/filmorate/model/Film.java | 2 + .../practicum/filmorate/model/User.java | 9 ++- 4 files changed, 55 insertions(+), 37 deletions(-) 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 173eba9..a103a20 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -20,16 +20,13 @@ public class FilmController { @GetMapping public Collection findAll() { + log.info("Запрос всех фильмов"); return films.values(); } @PostMapping public Film create(@Valid @RequestBody Film film) { - - if (film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { - log.debug("Дата рождения не может быть в будущем"); - throw new ValidationException("Дата рождения не может быть в будущем"); - } + checkDate(film); film.setId(getNextId()); @@ -44,6 +41,7 @@ public Film update(@Valid @RequestBody Film newFilm) { log.debug("Не указан Id при обновлении фильма"); throw new ValidationException("Не указан Id при обновлении фильма"); } + checkDate(newFilm); if (films.containsKey(newFilm.getId())) { Film oldFilm = films.get(newFilm.getId()); oldFilm.setName(newFilm.getName()); @@ -66,4 +64,11 @@ private long getNextId() { return ++currentMaxId; } + private void checkDate(Film film) { + if (film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { + log.debug("Дата рождения не может быть в будущем"); + throw new ValidationException("Дата рождения не может быть в будущем"); + } + } + } 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 ceb8291..ee546e5 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -20,54 +20,39 @@ public class UserController { @GetMapping public Collection findAll() { + log.info("Запрос всех пользователей"); return users.values(); } @PostMapping public User create(@Valid @RequestBody User user) { - if (checkEmail(user.getEmail())) { - log.debug("Пользователь с почтой " + user.getEmail() + " уже существует"); - throw new ValidationException("Пользователь с почтой " + user.getEmail() + " уже существует"); - } - if (checkLogin(user.getLogin())) { - log.debug("Пользователь с логином " + user.getLogin() + " уже существует"); - throw new ValidationException("Пользователь с логином " + user.getLogin() + " уже существует"); - } - if (user.getLogin().contains(" ")) { - log.debug("Логин должен быть без пробелов"); - throw new ValidationException("Логин должен быть без пробелов"); - } - if (user.getBirthday().isAfter(LocalDate.now())) { - log.debug("Дата рождения не может быть в будущем"); - throw new ValidationException("Дата рождения не может быть в будущем"); - } + checkEmail(user.getEmail()); + checkLogin(user.getLogin()); + checkBirthday(user.getBirthday()); user.setId(getNextId()); - if (user.getName() == null || user.getName().isBlank()) - user.setName(user.getLogin()); - else user.setName(user.getName()); + user.setNameWithCheck(user); users.put(user.getId(), user); - log.info("Фильм добавлен"); + log.info("Пользователь добавлен"); return user; } @PutMapping public User update(@Valid @RequestBody User newUser) { - // проверяем необходимые условия if (newUser.getId() == null) { log.debug("Не указан Id при обновлении пользователя"); throw new ValidationException("Не указан Id при обновлении пользователя"); } - if (checkEmail(newUser.getEmail())) { - log.debug("Пользователь с почтой " + newUser.getEmail() + " уже существует"); - throw new ValidationException("Пользователь с почтой " + newUser.getEmail() + " уже существует"); - } + checkEmail(newUser.getEmail()); + checkLogin(newUser.getLogin()); + checkBirthday(newUser.getBirthday()); + if (users.containsKey(newUser.getId())) { User oldUser = users.get(newUser.getId()); oldUser.setEmail(newUser.getEmail()); - oldUser.setName(newUser.getName()); + oldUser.setNameWithCheck(newUser); oldUser.setLogin(newUser.getLogin()); oldUser.setBirthday(newUser.getBirthday()); log.info("Пользователь с id " + newUser.getId() + " обновлен"); @@ -86,15 +71,34 @@ private long getNextId() { return ++currentMaxId; } - private boolean checkEmail(String email) { - return users.values().stream() + private void checkEmail(String email) { + boolean existEmail = users.values().stream() .map(User::getEmail) .anyMatch(email::equals); + if (existEmail) { + log.debug("Пользователь с почтой " + email + " уже существует"); + throw new ValidationException("Пользователь с почтой " + email + " уже существует"); + } } - private boolean checkLogin(String login) { - return users.values().stream() - .map(User::getEmail) + private void checkLogin(String login) { + if (login.contains(" ")) { + log.debug("Логин должен быть без пробелов"); + throw new ValidationException("Логин должен быть без пробелов"); + } + boolean existEmail = users.values().stream() + .map(User::getLogin) .anyMatch(login::equals); + if (existEmail) { + log.debug("Пользователь с логином " + login + " уже существует"); + throw new ValidationException("Пользователь с логином " + login + " уже существует"); + } + } + + private void checkBirthday(LocalDate birthday) { + if (birthday.isAfter(LocalDate.now())) { + log.debug("Дата рождения не может быть в будущем"); + throw new ValidationException("Дата рождения не может быть в будущем"); + } } } 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 80e3ee9..d03f242 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -26,8 +26,10 @@ public class Film { @Size(max = 200) private String description; + @NotNull private LocalDate releaseDate; @Positive + @NotNull private Integer 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 80eea4c..be4ca32 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -19,13 +19,20 @@ public class User { private Long id; @Email + @NotNull private String email; - @NotNull @NotBlank private String login; private String name; + @NotNull private LocalDate birthday; + + public void setNameWithCheck(User user) { + if (user.getName() == null || user.getName().isBlank()) + this.setName(user.getLogin()); + else this.setName(user.getName()); + } }