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());
+ }
+
+}