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..a103a20 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,74 @@ 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() { + log.info("Запрос всех фильмов"); + return films.values(); + } + + @PostMapping + public Film create(@Valid @RequestBody Film film) { + checkDate(film); + + 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 при обновлении фильма"); + } + checkDate(newFilm); + 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; + } + + 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 new file mode 100644 index 0000000..ee546e5 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -0,0 +1,104 @@ +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() { + log.info("Запрос всех пользователей"); + return users.values(); + } + + @PostMapping + public User create(@Valid @RequestBody User user) { + + checkEmail(user.getEmail()); + checkLogin(user.getLogin()); + checkBirthday(user.getBirthday()); + + user.setId(getNextId()); + user.setNameWithCheck(user); + + 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 при обновлении пользователя"); + } + 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.setNameWithCheck(newUser); + 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 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 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/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..d03f242 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,35 @@ 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; + + @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 new file mode 100644 index 0000000..be4ca32 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -0,0 +1,38 @@ +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 + @NotNull + private String email; + + @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()); + } +} 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()); + } + +}