diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml
index 842aa8c..5d57533 100644
--- a/.github/workflows/api-tests.yml
+++ b/.github/workflows/api-tests.yml
@@ -1,8 +1,4 @@
+#file: noinspection SpellCheckingInspection,SpellCheckingInspection
name: Films API Tests
-on:
- pull_request:
-
-jobs:
- build:
- uses: yandex-praktikum/java-filmorate/.github/workflows/api-tests.yml@ci
\ No newline at end of file
+on:
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0cad031..3b9445e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,6 @@
org.springframework.boot
spring-boot-starter-web
-
org.projectlombok
lombok
@@ -32,6 +31,35 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ javax.validation
+ validation-api
+ 1.1.0.Final
+
+
+ org.zalando
+ logbook-spring-boot-starter
+ 3.7.2
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 8.0.2.Final
+
+
+ org.glassfish.expressly
+ expressly
+ 5.0.0
+
+
+ junit
+ junit
+ test
+
@@ -41,6 +69,7 @@
spring-boot-maven-plugin
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
index dca451b..5fd9353 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
@@ -6,6 +6,7 @@
@SpringBootApplication
public class FilmorateApplication {
public static void main(String[] args) {
+
SpringApplication.run(FilmorateApplication.class, args);
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
new file mode 100644
index 0000000..8a64a9c
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
@@ -0,0 +1,45 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import ru.yandex.practicum.filmorate.exception.DuplicatedDataException;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+
+@RestController
+@ControllerAdvice
+@Slf4j
+public class ErrorHandler {
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ErrorResponse handleDuplicatedDataException(final DuplicatedDataException e) {
+ log.error("Duplicated: {}", e.getMessage());
+ return new ErrorResponse(e.getMessage());
+ }
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public ErrorResponse handleNotFoundException(final NotFoundException e) {
+ log.error("NotFound: {}", e.getMessage());
+ return new ErrorResponse(e.getMessage());
+ }
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ErrorResponse handleValidationException(final ValidationException e) {
+ log.error("Validation: {}", e.getMessage());
+ return new ErrorResponse(e.getMessage());
+ }
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public ErrorResponse handleException(final Exception e) {
+ log.error("ServerError: {}", e.getMessage());
+ return new ErrorResponse(e.getMessage());
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorResponse.java
new file mode 100644
index 0000000..ae25d9b
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorResponse.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.Getter;
+
+@Getter
+public class ErrorResponse {
+ String error;
+
+ public ErrorResponse(String error) {
+ this.error = error;
+ }
+
+}
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..c5e508f 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,66 @@
package ru.yandex.practicum.filmorate.controller;
-import org.springframework.web.bind.annotation.RestController;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import ru.yandex.practicum.filmorate.exception.DuplicatedDataException;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.service.FilmService;
+
+import java.util.Collection;
@RestController
+@RequestMapping("/films")
+@Slf4j
+@RequiredArgsConstructor
+@Validated
public class FilmController {
-}
+ private final FilmService filmService;
+
+ @PostMapping
+ public Film createFilm(@Valid @RequestBody Film film) {
+ Film created = filmService.createFilm(film);
+ log.debug("Фильм успешно добавлен - {} \n", created);
+ return created;
+ }
+
+ @PutMapping
+ public Film updateFilm(@Valid @RequestBody Film newFilm) throws DuplicatedDataException, NotFoundException {
+ Film updated = filmService.updateFilm(newFilm);
+ log.debug("Фильм успешно обновлен - {} \n", updated);
+ return updated;
+ }
+
+ @GetMapping
+ public Collection getFilmsList() {
+ return filmService.getFilmsList();
+ }
+
+ @GetMapping("/{id}")
+ public Film getFilmById(@PathVariable int id) throws NotFoundException {
+ return filmService.getFilmById(id);
+ }
+
+ @PutMapping("{id}/like/{userId}")
+ public void addLikes(@PathVariable int id,
+ @PathVariable int userId) {
+ filmService.addLikes(id, userId);
+ log.debug("Like успешно добавлен.");
+ }
+
+ @DeleteMapping("{id}/like/{userId}")
+ public void deleteFilmLikes(@PathVariable int id,
+ @PathVariable int userId) {
+ filmService.deleteLikes(id, userId);
+ log.debug("Like успешно удален.");
+ }
+
+ @GetMapping("popular")
+ public Collection getPopularFilmList(@RequestParam(defaultValue = "10") int count) {
+ return filmService.getPopularFilms(count);
+ }
+
+}
\ No newline at end of file
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..a7c101a
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -0,0 +1,71 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import ru.yandex.practicum.filmorate.exception.DuplicatedDataException;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.service.UserService;
+
+import java.util.Collection;
+
+@RestController
+@RequestMapping("/users")
+@Slf4j
+@RequiredArgsConstructor
+@Validated
+public class UserController {
+ private final UserService userService;
+
+ @PostMapping
+ public User createUser(@Valid @RequestBody User user) {
+ User created = userService.createUser(user);
+ log.debug("Пользователь успешно добавлен - {} \n.", created);
+ return created;
+ }
+
+ @PutMapping
+ public User updateUser(@Valid @RequestBody User newUser) throws DuplicatedDataException, NotFoundException {
+ User updated = userService.updateUser(newUser);
+ log.debug("Пользователь успешно обновлен - {} \n.", updated);
+ return updated;
+ }
+
+ @GetMapping
+ public Collection getUsersList() {
+ return userService.getUsersList();
+ }
+
+ @GetMapping("/{id}")
+ public User getUserById(@PathVariable int id) throws NotFoundException {
+ return userService.getUserById(id);
+ }
+
+ @GetMapping("/{id}/friends")
+ public Collection getUsersFriends(@PathVariable int id) {
+ return userService.getFriendsList(id);
+ }
+
+ @GetMapping("/{id}/friends/common/{friendId}")
+ public Collection getCommonFriends(@PathVariable int id,
+ @PathVariable int friendId) {
+ return userService.getCommonFriends(id, friendId);
+ }
+
+ @PutMapping("/{id}/friends/{friendId}")
+ public void addFriend(@PathVariable int id,
+ @PathVariable int friendId) throws NotFoundException {
+ userService.addFriend(id, friendId);
+ log.debug("Друг успешно добавлен.");
+ }
+
+ @DeleteMapping("/{id}/friends/{friendId}")
+ public void deleteFriend(@PathVariable int id,
+ @PathVariable int friendId) throws NotFoundException {
+ userService.deleteFriend(id, friendId);
+ log.debug("Друг успешно удален.");
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/DuplicatedDataException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/DuplicatedDataException.java
new file mode 100644
index 0000000..614872d
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/DuplicatedDataException.java
@@ -0,0 +1,12 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class DuplicatedDataException extends RuntimeException {
+
+ public DuplicatedDataException() {
+ super();
+ }
+
+ public DuplicatedDataException(String message) {
+ super(message);
+ }
+}
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..1df8f8a
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class NotFoundException extends RuntimeException {
+
+ public NotFoundException() {
+ super();
+ }
+
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+}
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..32bb593
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java
@@ -0,0 +1,12 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class ValidationException extends RuntimeException {
+
+ public ValidationException() {
+ super();
+ }
+
+ 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..44d2ca2 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,44 @@
package ru.yandex.practicum.filmorate.model;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * Film.
- */
-@Getter
-@Setter
+import javax.validation.constraints.Past;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import org.hibernate.validator.constraints.Length;
+
+import java.time.LocalDate;
+
+@Data
+@Slf4j
public class Film {
+
+ @NotBlank(message = "Название не должно быть пустым или иметь пробелы.")
+ private String name;
+
+ @NotNull(message = "Описание фильма не может быть пустым.")
+ @Length(max = 200, message = "Описание фильма не может превышать {max} знаков.")
+ private String description;
+
+ @Past(message = "Дата выхода фильма не может быть в будущем.")
+ @NotNull(message = "Дата выхода фильма не может быть пустой.")
+ @FilmStartDate
+ private LocalDate releaseDate;
+
+ @NotNull(message = "Продолжительность фильма не может быть пустой.")
+ @Positive(message = "Продолжительность фильма не может быть меньше 0.")
+ Integer duration;
+
+ private Integer id;
+
+ public Film() {
+
+ }
+
+ public Film(String name) {
+ this.name = name;
+ }
+
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/FilmStartDate.java b/src/main/java/ru/yandex/practicum/filmorate/model/FilmStartDate.java
new file mode 100644
index 0000000..6035696
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/FilmStartDate.java
@@ -0,0 +1,23 @@
+package ru.yandex.practicum.filmorate.model;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({ FIELD })
+@Retention(RUNTIME)
+@Constraint(validatedBy = FilmStartDateValidator.class)
+@Documented
+@interface FilmStartDate {
+ String message() default "{CapitalLetter.invalid}";
+
+ Class>[] groups() default { };
+
+ Class extends Payload>[] payload() default { };
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/FilmStartDateValidator.java b/src/main/java/ru/yandex/practicum/filmorate/model/FilmStartDateValidator.java
new file mode 100644
index 0000000..a91acb4
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/FilmStartDateValidator.java
@@ -0,0 +1,18 @@
+package ru.yandex.practicum.filmorate.model;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+import java.time.LocalDate;
+
+public class FilmStartDateValidator implements ConstraintValidator {
+ LocalDate localDate = LocalDate.of(1895, 12, 28);
+
+ @Override
+ public boolean isValid(LocalDate value, ConstraintValidatorContext context) {
+ if (value != null) {
+ return value.isAfter(localDate);
+ }
+ return true;
+ }
+}
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..cc65836
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -0,0 +1,36 @@
+package ru.yandex.practicum.filmorate.model;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Past;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+public class User {
+ @NotNull(message = "Email не может быть пустым.")
+ @Email(message = "Email должен содержать знак @.")
+ private String email;
+
+ @NotBlank(message = "Логин не должен быть пустым или иметь пробелы.")
+ private String login;
+
+ private Integer id;
+ private String name;
+
+ @NotNull(message = "Дата рождения не может быть пустой.")
+ @Past(message = "Дата рождения не может быть в будущем.")
+ private LocalDate birthday;
+
+ public User() {
+
+ }
+
+ public User(String email, String login) {
+ this.email = email;
+ this.login = login;
+ }
+
+}
\ No newline at end of file
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..bb08ec9
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -0,0 +1,69 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+import ru.yandex.practicum.filmorate.exception.DuplicatedDataException;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.InMemoryFilmStorage;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Map.Entry.comparingByValue;
+
+@Service
+@RequiredArgsConstructor
+@Validated
+public class FilmService {
+ private final FilmStorage filmStorage = new InMemoryFilmStorage();
+
+ public Film createFilm(Film film) {
+ return filmStorage.createFilm(film);
+ }
+
+ public Film getFilmById(int id) throws NotFoundException {
+ return filmStorage.getFilmById(id)
+ .orElseThrow(() -> new NotFoundException("Фильм с таким id = " + id + " не найден."));
+ }
+
+ public Film updateFilm(Film newFilm) throws DuplicatedDataException, NotFoundException {
+ getFilmById(newFilm.getId());
+
+ filmStorage.getFilms().values().stream()
+ .filter(film -> !film.getName().equals(newFilm.getName()))
+ .findFirst()
+ .orElseThrow(() -> new DuplicatedDataException("Фильм с названием \"" + newFilm.getName() + "\" уже существует."));
+
+ return filmStorage.updateFilm(newFilm);
+ }
+
+ public Collection getFilmsList() {
+ return filmStorage.getFilms().values();
+ }
+
+ public void addLikes(int filmId, int userId) {
+ filmStorage.addLikes(filmId, userId);
+ }
+
+ public void deleteLikes(int filmId, int userId) {
+ filmStorage.deleteLikes(filmId, userId);
+ }
+
+ public Collection getPopularFilms(int count) {
+ Comparator> compareSet = Comparator.comparingInt(Set::size);
+
+ Map> collected = filmStorage.getPopularFilms(count).entrySet().stream().sorted(comparingByValue(compareSet))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (entry1, entry2) -> entry2, LinkedHashMap::new));
+
+ HashMap films = (HashMap) filmStorage.getFilms();
+
+ return collected.keySet().stream()
+ .map(films::get)
+ .map(Film::getName)
+ .limit(count)
+ .collect(Collectors.toList());
+ }
+}
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..3cb8260
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -0,0 +1,74 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+import ru.yandex.practicum.filmorate.exception.DuplicatedDataException;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.InMemoryUserStorage;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+@Service
+@Validated
+public class UserService {
+ private final UserStorage userStorage = new InMemoryUserStorage();
+
+ public User createUser(User user) {
+ if (user.getName() == null || user.getName().isBlank()) {
+ user.setName(user.getLogin());
+ }
+ return userStorage.createUser(user);
+ }
+
+ public User getUserById(int id) throws NotFoundException {
+ return userStorage.getUserById(id)
+ .orElseThrow(() -> new NotFoundException("Пользователь с Id = " + id + " не найден."));
+ }
+
+ public User updateUser(User newUser) throws DuplicatedDataException, NotFoundException {
+ getUserById(newUser.getId());
+
+ userStorage.getUsersList().stream()
+ .filter(user -> !user.getEmail().equals(newUser.getEmail()))
+ .findAny()
+ .orElseThrow(() -> new DuplicatedDataException("Еmail: " + newUser.getEmail() + " уже используется."));
+
+ return userStorage.updateUser(newUser);
+ }
+
+ public Collection getUsersList() {
+ return userStorage.getUsersList();
+ }
+
+
+ public void addFriend(int userId, int friendId) throws NotFoundException {
+ User user = getUserById(userId);
+ User friend = getUserById(friendId);
+ userStorage.addFriend(user, friend);
+ }
+
+ public Collection getFriendsList(int id) {
+ return userStorage.getFriendsList(id).stream()
+ .map(User::getName)
+ .collect(Collectors.toList());
+ }
+
+ public Collection getCommonFriends(int userId, int friendId) {
+ Collection friendsList = userStorage.getFriendsList(friendId);
+ return userStorage.getFriendsList(userId).stream()
+ .filter(friendsList::contains)
+ .map(User::getName)
+ .collect(Collectors.toList());
+ }
+
+ public void deleteFriend(int id, int friendId) throws NotFoundException {
+ User user = getUserById(id);
+ User friend = getUserById(friendId);
+
+ userStorage.deleteFriend(user, friend);
+ }
+
+}
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..a2d436a
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
@@ -0,0 +1,26 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+public interface FilmStorage {
+ Map films = Map.of();
+
+ Film createFilm(Film film);
+
+ Film updateFilm(Film newFilm);
+
+ Map getFilms();
+
+ Optional getFilmById(int id);
+
+ void addLikes(int filmId, int userId);
+
+ void deleteLikes(int filmId, int userId);
+
+ Map> 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..c819234
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
@@ -0,0 +1,64 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.Film;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.*;
+
+@Slf4j
+@Component
+public class InMemoryFilmStorage implements FilmStorage {
+ private int nextFilmId = 1;
+
+ private final Map films = new HashMap<>();
+ private final Map> filmLikes = new HashMap<>();
+
+ @Override
+ public Film createFilm(Film film) {
+ film.setId(nextFilmId++);
+ films.put(film.getId(), film);
+ return film;
+ }
+
+ @Override
+ public Film updateFilm(Film newFilm) {
+ Film oldFilm = films.get(newFilm.getId());
+
+ oldFilm.setName(newFilm.getName());
+ oldFilm.setDescription(newFilm.getDescription());
+ oldFilm.setReleaseDate(newFilm.getReleaseDate());
+ oldFilm.setDuration(newFilm.getDuration());
+
+ films.put(oldFilm.getId(), newFilm);
+ return oldFilm;
+ }
+
+ @Override
+ public Map getFilms() {
+ return films;
+ }
+
+ @Override
+ public Optional getFilmById(int id) {
+ return Optional.ofNullable(films.get(id));
+ }
+
+ @Override
+ public void addLikes(int filmId, int userId) {
+ filmLikes.computeIfAbsent(filmId, f -> new HashSet<>()).add(userId);
+ }
+
+ @Override
+ public void deleteLikes(int filmId, int userId) {
+ filmLikes.get(filmId).remove(userId);
+ if (filmLikes.get(filmId).isEmpty()) {
+ filmLikes.remove(filmId);
+ }
+ }
+
+ public Map> getPopularFilms(int count) {
+ return filmLikes;
+
+ }
+}
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..0601d7f
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
@@ -0,0 +1,63 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.*;
+
+@Slf4j
+@Component
+public class InMemoryUserStorage implements UserStorage {
+ private int nextUserId = 1;
+
+ private final Map users = new HashMap<>();
+ private final Map> userFriends = new HashMap<>();
+
+ @Override
+ public User createUser(User user) {
+ user.setId(nextUserId++);
+ users.put(user.getId(), user);
+ return user;
+ }
+
+ @Override
+ public User updateUser(User newUser) {
+ User oldUser = users.get(newUser.getId());
+
+ oldUser.setEmail(newUser.getEmail());
+ oldUser.setLogin(newUser.getLogin());
+ oldUser.setName(newUser.getName());
+ oldUser.setBirthday(newUser.getBirthday());
+
+ users.put(oldUser.getId(), newUser);
+ return oldUser;
+ }
+
+ @Override
+ public Collection getUsersList() {
+ return users.values();
+ }
+
+ @Override
+ public Optional getUserById(int id) {
+ return Optional.ofNullable(users.get(id));
+ }
+
+ @Override
+ public void addFriend(User user, User friend) {
+ userFriends.computeIfAbsent(user.getId(), u -> new HashSet<>()).add(friend);
+ userFriends.computeIfAbsent(friend.getId(), f -> new HashSet<>()).add(user);
+ }
+
+ @Override
+ public Collection getFriendsList(int id) {
+ return userFriends.get(id);
+ }
+
+ @Override
+ public void deleteFriend(User user, User friend) {
+ userFriends.get(user.getId()).remove(friend);
+ }
+}
+
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..252e6f9
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
@@ -0,0 +1,25 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+
+public interface UserStorage {
+ Map users = Map.of();
+
+ User createUser(User user);
+
+ User updateUser(User newUser);
+
+ Collection getUsersList();
+
+ Optional getUserById(int id);
+
+ void addFriend(User user, User friend);
+
+ Collection getFriendsList(int id);
+
+ void deleteFriend(User user, User friend);
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
index 660412e..de095d5 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
@@ -2,12 +2,255 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
+import ru.yandex.practicum.filmorate.controller.FilmController;
+import ru.yandex.practicum.filmorate.controller.UserController;
+import ru.yandex.practicum.filmorate.exception.DuplicatedDataException;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.service.FilmService;
+import ru.yandex.practicum.filmorate.service.UserService;
+
+import java.time.LocalDate;
+import java.util.Collection;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
class FilmorateApplicationTests {
+ private final UserService userService = new UserService();
+ private final FilmService filmService = new FilmService();
+ private final UserController userController = new UserController(userService);
+ private final FilmController filmController = new FilmController(filmService);
+
+ private User user = new User("user@test", "testLogin");
+ private Film film = new Film("testFilm");
+
+ @Test
+ void getUsersListTest() {
+ assertEquals(0, userController.getUsersList().size());
+
+ userController.createUser(user);
+
+ assertEquals(1, userController.getUsersList().size());
+
+ User user1 = new User("user1@test", "test1Login");
+ userController.createUser(user1);
+
+ assertEquals(2, userController.getUsersList().size());
+ }
+
+ @Test
+ void createUserTest() {
+ assertEquals(0, userController.getUsersList().size());
+
+ userController.createUser(user);
+
+ assertEquals(1, userController.getUsersList().size());
+
+ User user1 = new User("user1@test", "testLogin1");
+ LocalDate date = LocalDate.of(1985, 3,12);
+ user1.setBirthday(date);
+
+ userController.createUser(user1);
+
+ }
+
+ @Test
+ void userEmailTest() {
+ assertEquals(0, userController.getUsersList().size());
+
+ userController.createUser(user);
+
+ assertEquals(1, userController.getUsersList().size());
+
+ User user1 = new User("testMail", "testLogin1");
+
+ assertEquals(1, userController.getUsersList().size());
+ }
+
+ @Test
+ void userLoginTest() {
+ assertEquals(0, userController.getUsersList().size());
+
+ userController.createUser(user);
+
+ assertEquals(1, userController.getUsersList().size());
+
+ User user1 = new User("test@mail", "test Login1");
+
+ assertEquals(1, userController.getUsersList().size());
+ }
+
+ @Test
+ void userBirthdayTest() {
+ assertEquals(0, userController.getUsersList().size());
+
+ LocalDate date = LocalDate.of(1985,3, 12);
+ user.setBirthday(date);
+ userController.createUser(user);
+
+ assertEquals(1, userController.getUsersList().size());
+
+ User user1 = new User("test@mail", "testLogin1");
+ LocalDate date1 = LocalDate.of(2026,3, 12);
+ user1.setBirthday(date1);
+
+ assertEquals(1, userController.getUsersList().size());
+ }
+
+ @Test
+ void updateUserTest() throws NotFoundException, DuplicatedDataException {
+ userController.createUser(user);
+
+ assertEquals(1, userController.getUsersList().size());
+
+ User user1 = new User("user1@test", "test1Login");
+ user1.setId(user.getId());
+ LocalDate date = LocalDate.of(1985,3, 12);
+ user1.setBirthday(date);
+ userController.updateUser(user1);
+
+ assertEquals(user.getEmail(), user1.getEmail());
+ assertEquals(user.getLogin(), user1.getLogin());
+ assertEquals(user.getBirthday(), user1.getBirthday());
+
+ assertEquals(1, userController.getUsersList().size());
+ }
+
+ @Test
+ void getUserById() throws NotFoundException {
+ userController.createUser(user);
+ User user1 = new User("user@test1", "testLogin1");
+ userController.createUser(user1);
+
+ assertEquals(2, userController.getUsersList().size());
+
+ assertEquals(user1, userController.getUserById(2));
+ }
+
+ @Test
+ void getFilmsListTest() {
+ assertEquals(0, filmController.getFilmsList().size());
+
+ filmController.createFilm(film);
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ Film film1 = new Film("filmTest1");
+ filmController.createFilm(film1);
+
+ assertEquals(2, filmController.getFilmsList().size());
+ }
+
+ @Test
+ void createFilmTest() {
+ assertEquals(0, filmController.getFilmsList().size());
+
+ filmController.createFilm(film);
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ Film film1 = new Film("Film1Test");
+ film1.setDuration(90);
+ LocalDate date = LocalDate.of(1997, 2,12);
+ film1.setReleaseDate(date);
+ film1.setDescription("Description1");
+ filmController.createFilm(film1);
+
+ assertEquals(2, filmController.getFilmsList().size());
+ }
+
+ @Test
+ void filmDescriptionTest() {
+ filmController.createFilm(film);
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ Film film1 = new Film("filmTest1");
+ film1.setDescription("DescriptionDescriptionDescriptionDescriptionDescriptionDescription" +
+ "DescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescription" +
+ "DescriptionDescriptionDescriptionDescriptionDescriptionDescriptionDescription");
+
+ assertEquals(1, filmController.getFilmsList().size());
+ }
+
+ @Test
+ void filmReleaseDateTest() {
+ filmController.createFilm(film);
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ Film film1 = new Film("filmTest1");
+ LocalDate date = LocalDate.of(1895, 12, 25);
+ film1.setReleaseDate(date);
+
+ assertEquals(1, filmController.getFilmsList().size());
+ }
+
+ @Test
+ void filmDurationTest() {
+ filmController.createFilm(film);
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ Film film1 = new Film("filmTest1");
+ film1.setDuration(-15);
+
+ assertEquals(1, filmController.getFilmsList().size());
+ }
+
+ @Test
+ void updateFilmTest() throws NotFoundException, DuplicatedDataException {
+ filmController.createFilm(film);
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ Film film1 = new Film("filmTest1");
+ film1.setDuration(120);
+ film1.setDescription("TestDescription");
+ film1.setId(film.getId());
+
+ filmController.updateFilm(film1);
+
+ assertEquals(film.getDescription(), film1.getDescription());
+ assertEquals(film.getDuration(), film1.getDuration());
+ assertEquals(film.getName(), film1.getName());
+
+ assertEquals(1, filmController.getFilmsList().size());
+
+ }
@Test
- void contextLoads() {
+ void getFriendsListTest() throws NotFoundException {
+ userController.createUser(user);
+ User user1 = userController.createUser(new User("name@l", "login"));
+ User user2 = userController.createUser(new User("name@lm", "logins"));
+
+ userController.addFriend(user.getId(), user1.getId());
+ userController.addFriend(user.getId(), user2.getId());
+ assertEquals(2, userController.getUsersFriends(user.getId()).size());
+
+ userController.deleteFriend(user.getId(), 2);
+
+ assertEquals(1, userController.getUsersFriends(user.getId()).size());
+
+ }
+
+ @Test
+ void addLikesTest() {
+ filmController.createFilm(film);
+ userController.createUser(user);
+
+ filmController.addLikes(film.getId(), user.getId());
+
+ Collection filmNames = filmController.getPopularFilmList(film.getId());
+
+ assertEquals(1, filmController.getPopularFilmList(film.getId()).size());
+
+ filmController.deleteFilmLikes(film.getId(), user.getId());
+
+ assertEquals(0, filmController.getPopularFilmList(film.getId()).size());
}
}