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