diff --git a/pom.xml b/pom.xml index d9cee64..2e002aa 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,11 @@ 3.3.3 test + + org.zalando + logbook-spring-boot-starter + 3.7.2 + diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java index 8616407..3f2baca 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java +++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java @@ -1,6 +1,5 @@ package ru.yandex.practicum.filmorate; - import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; 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 f8170bd..b77547a 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -1,72 +1,84 @@ package ru.yandex.practicum.filmorate.controller; import jakarta.validation.Valid; -import jakarta.validation.ValidationException; +import jakarta.validation.constraints.Positive; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.service.FilmService; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +@Validated @RestController @RequestMapping("/films") public class FilmController { - private final ValidateController validateController = new ValidateController(); - private final Map films = new HashMap<>(); - private final Logger log = LoggerFactory.getLogger(FilmController.class); + private final Logger logger = LoggerFactory.getLogger(FilmController.class); + private final FilmService filmService; + + @Autowired + public FilmController(FilmService filmService) { + this.filmService = filmService; + } + + @GetMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + + public Film getFilmId(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("вывод фильма по ID"); + return filmService.getFilmId(id); + } @GetMapping - public ResponseEntity> filmAll() { - log.info("вывод списка фильмов"); - return ResponseEntity.ok(films.values()); + @ResponseStatus(HttpStatus.OK) + public Collection getFilmAll() { + logger.info("вывод списка фильмов"); + return filmService.getAll(); } @PostMapping - public ResponseEntity create(@Valid @RequestBody Film film) { - validateController.validateFilm(film); - film.setId(getNextId()); - films.put(film.getId(), film); - log.info("фильм добавлен id: " + film.getId()); - return ResponseEntity.status(HttpStatus.CREATED).body(film); + @ResponseStatus(HttpStatus.CREATED) + public Film create(@Valid @RequestBody Film film) { + logger.info("Фильм добавлен"); + return filmService.create(film); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public void delete(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("Удаление id=" + id); + filmService.delete(id); } @PutMapping - public ResponseEntity update(@Valid @RequestBody Film newFilm) { - if (newFilm.getId() == null) { - log.warn("ID пустой"); - throw new ValidationException("пользователь с таким ID не найден"); - // изначально сделал чтоб в теле была ошибка, но не прошло тесты в Postman - // return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("ID пустой"); - } - if (!films.containsKey(newFilm.getId())) { - log.warn("пользователь с таким ID не найден"); - // изначально сделал чтоб в теле была ошибка, но не прошло тесты в Postman - // return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("пользователь с таким ID не найден"); - throw new ValidationException("пользователь с таким ID не найден"); - } - validateController.validateFilm(newFilm); - Film oldFilm = films.get(newFilm.getId()); - oldFilm.setName(newFilm.getName()); - oldFilm.setDescription(newFilm.getDescription()); - oldFilm.setReleaseDate(newFilm.getReleaseDate()); - oldFilm.setDuration(newFilm.getDuration()); - log.info("запись фильма обновлена"); - return ResponseEntity.ok(oldFilm); + @ResponseStatus(HttpStatus.OK) + public Film update(@RequestBody Film newFilm) { + logger.info("запись фильма обновлена"); + return filmService.update(newFilm); } - private long getNextId() { - long currentMaxId = films.keySet() - .stream() - .mapToLong(id -> id) - .max() - .orElse(0); - return ++currentMaxId; + @PutMapping("/{id}/like/{userId}") + @ResponseStatus(HttpStatus.OK) + public Collection likeAdd(@Positive(message = "неверное значение") @PathVariable long id, + @Positive(message = "неверное значение") @PathVariable long userId) { + return filmService.likeAdd(id, userId); + } + + @DeleteMapping("/{id}/like/{userId}") + @ResponseStatus(HttpStatus.OK) + public Collection likeDelete(@Positive(message = "неверное значение") @PathVariable long id, + @Positive(message = "неверное значение") @PathVariable long userId) { + return filmService.likeDelete(id, userId); + } + @GetMapping("/popular") + @ResponseStatus(HttpStatus.OK) + public Collection getPopular(@Positive(message = "неверное значение") @RequestParam(defaultValue = "10") int count) { + return filmService.getPopular(count); } } 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 8b30820..938f2b8 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -2,80 +2,95 @@ import jakarta.validation.Valid; -import jakarta.validation.ValidationException; +import jakarta.validation.constraints.Positive; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.service.UserService; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +@Validated @RestController @RequestMapping("/users") public class UserController { - private final ValidateController validate = new ValidateController(); - private final Map users = new HashMap<>(); + private final UserService userService; + + private final Logger logger = LoggerFactory.getLogger(UserController.class); + + @Autowired + public UserController(UserService userService) { + this.userService = userService; + } - private final Logger log = LoggerFactory.getLogger(UserController.class); + @GetMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public User getUserId(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("вывод пользователя по ID"); + return userService.getUserId(id); + } - @GetMapping //запрос всех пользователей - public ResponseEntity> usersAll() { + @GetMapping + @ResponseStatus(HttpStatus.OK) + public Collection userAll() { + logger.info("вывод списка пользователей"); + return userService.getAll(); + } - log.info("вывод списка пользователей"); - return ResponseEntity.ok(users.values()); + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public User create(@Valid @RequestBody User user) { + logger.info("Пользователь добавлен"); + return userService.create(user); } - @PostMapping //добавление нового пользователя - public ResponseEntity create(@Valid @RequestBody User user) { - validate.validateUser(user); - - if (users.containsValue(user)) { - log.warn("ошибка добавления пользователя, такой пользователь уже есть"); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("ошибка добавления пользователя, такой пользователь уже есть"); - } - user.setId(getNextId()); - users.put(user.getId(), user); - log.info("пользователь создан id: " + user.getId()); - return ResponseEntity.status(HttpStatus.CREATED).body(user); //отрпавляем ответ с статусом и телом + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public void delete(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("Удаление id=" + id); + userService.delete(id); } - @PutMapping //обновление пользователя - public ResponseEntity update(@Valid @RequestBody User newUser) { - if (newUser.getId() == null) { - log.warn("ID пустой"); - throw new ValidationException(" ID пустой"); - // изначально сделал чтоб в теле была ошибка, но не прошло тесты в Postman - // return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("ID пустой"); - } - if (!users.containsKey(newUser.getId())) { - log.warn("пользователь с таким ID не найден"); - throw new ValidationException("пользователь с таким ID не найден"); - // изначально сделал чтоб в теле была ошибка, но не прошло тесты в Postman - // return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("пользователь с таким ID не найден"); - } - validate.validateUser(newUser); - User oldUser = users.get(newUser.getId()); - oldUser.setEmail(newUser.getEmail()); - oldUser.setLogin(newUser.getLogin()); - oldUser.setBirthday(newUser.getBirthday()); - oldUser.setName(newUser.getName()); - log.info("пользователь изменен"); - return ResponseEntity.ok(oldUser); + @PutMapping + @ResponseStatus(HttpStatus.OK) + public User update(@RequestBody User newUser) { + logger.info("запись пользователя обновлена"); + return userService.update(newUser); } - private long getNextId() { - long currentMaxId = users.keySet() - .stream() - .mapToLong(id -> id) - .max() - .orElse(0); - return ++currentMaxId; + @PutMapping("/{id}/friends/{friendsId}") + @ResponseStatus(HttpStatus.OK) + public Collection friendsAdd(@Positive(message = "неверное значение") @PathVariable long id, + @Positive(message = "неверное значение") @PathVariable long friendsId) { + logger.info("добавили в друзья"); + return userService.friendsAdd(id, friendsId); + } + + @DeleteMapping("/{id}/friends/{friendsId}") + @ResponseStatus(HttpStatus.OK) + public Collection friendsDelete(@Positive(message = "неверное значение") @PathVariable long id, + @Positive(message = "неверное значение") @PathVariable long friendsId) { + logger.info("удалили из друзей"); + return userService.friendsDelete(id, friendsId); + } + + @GetMapping("/{id}/friends") + @ResponseStatus(HttpStatus.OK) + public Collection friendsGetList(@Positive(message = "неверное значение") @PathVariable long id) { + logger.info("показывает список друзей"); + return userService.friendsGetList(id); + } + @GetMapping("/{id}/friends/common/{otherId}") + @ResponseStatus(HttpStatus.OK) + public Collection friendsGetCommonList(@Positive(message = "неверное значение") @PathVariable long id, + @Positive(message = "неверное значение") @PathVariable long otherId) { + logger.info("показывает список друзей"); + return userService.friendsGetCommonList(id, otherId); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ValidateController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ValidateController.java index 2f427cc..8c4d32f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/ValidateController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ValidateController.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; import ru.yandex.practicum.filmorate.exception.ValidationException; import ru.yandex.practicum.filmorate.exception.ValidationNullException; import ru.yandex.practicum.filmorate.model.Film; @@ -9,6 +10,7 @@ import java.time.LocalDate; +@Component public class ValidateController { private final Logger log = LoggerFactory.getLogger(ValidateController.class); @@ -96,7 +98,6 @@ public void validateUser(User user) { log.warn("введена дата из будущего"); throw new ValidationException("введена дата из будущего"); } - } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java index 2503b8f..a8404af 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java @@ -5,34 +5,58 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.List; import java.util.stream.Collectors; -/// взято с просторов интернета вывод сообщений при ошибки -@ControllerAdvice +@RestControllerAdvice public class ErrorHandler { + private final Logger logger = LoggerFactory.getLogger(ErrorHandler.class); - private final Logger log = LoggerFactory.getLogger(ErrorHandler.class); + @ExceptionHandler + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse handlerIsNull(final ErrorIsNull e) { + logger.warn(e.getMessage()); + return new ErrorResponse("error", e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleThrowbla(final Throwable e) { + logger.warn(e.getMessage()); + return new ErrorResponse("error", "Произошла непредвиденная ошибка."); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handlerValidationException(final ValidationException e) { + logger.warn(e.getMessage()); + return new ErrorResponse("error", e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handlerValidationNullException(final ValidationNullException e) { + logger.warn(e.getMessage()); + return new ErrorResponse("error", e.getMessage()); + } - @ResponseBody @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ValidationErrorResponse onConstraintValidationException( ConstraintViolationException e ) { - final List violations = e.getConstraintViolations().stream() + final List violations = e.getConstraintViolations().stream() .map( - violation -> new Violation( - violation.getPropertyPath().toString(), - violation.getMessage() + error -> new ErrorResponse( + error.getPropertyPath().toString(), + error.getMessage() ) ) - .peek(violation -> log.warn(violation.getMessage())) + .peek(error -> logger.warn(error.getDescription())) .collect(Collectors.toList()); return new ValidationErrorResponse(violations); @@ -40,13 +64,12 @@ public ValidationErrorResponse onConstraintValidationException( @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - @ResponseBody public ValidationErrorResponse onMethodArgumentNotValidException( MethodArgumentNotValidException e ) { - final List violations = e.getBindingResult().getFieldErrors().stream() - .map(error -> new Violation(error.getField(), error.getDefaultMessage())) - .peek(error -> log.warn(error.getFieldName() + " = " + error.getMessage())) + final List violations = e.getBindingResult().getFieldErrors().stream() + .map(error -> new ErrorResponse(error.getField(), error.getDefaultMessage())) + .peek(error -> logger.warn(error.getError() + " = " + error.getDescription())) .collect(Collectors.toList()); return new ValidationErrorResponse(violations); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorIsNull.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorIsNull.java new file mode 100644 index 0000000..6692ef0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorIsNull.java @@ -0,0 +1,8 @@ +package ru.yandex.practicum.filmorate.exception; + +public class ErrorIsNull extends NullPointerException { + + public ErrorIsNull(String message) { + super(message); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java new file mode 100644 index 0000000..9d8ec03 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.exception; + +import lombok.Getter; + +@Getter +public class ErrorResponse { + + String error; + + String description; + + public ErrorResponse(String error, String description) { + this.error = error; + this.description = description; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationErrorResponse.java index 81443f2..4615672 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationErrorResponse.java +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationErrorResponse.java @@ -5,11 +5,10 @@ import java.util.List; -// взято с просторов интернета вывод сообщений при ошибки @Getter @RequiredArgsConstructor public class ValidationErrorResponse { - private final List violations; + private final List violations; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/Violation.java b/src/main/java/ru/yandex/practicum/filmorate/exception/Violation.java deleted file mode 100644 index 3455b04..0000000 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/Violation.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.yandex.practicum.filmorate.exception; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -// взято с просторов интернета вывод сообщений при ошибки - -@Getter -@RequiredArgsConstructor -public class Violation { - private final String fieldName; - private final String message; - -} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/DataModel.java b/src/main/java/ru/yandex/practicum/filmorate/model/DataModel.java new file mode 100644 index 0000000..14c6581 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/DataModel.java @@ -0,0 +1,8 @@ +package ru.yandex.practicum.filmorate.model; + +public class DataModel { + private Long id; + + private String name; + +} 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 63c7732..297ba77 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -5,6 +5,7 @@ import lombok.*; import java.time.LocalDate; +import java.util.Set; /** * Film. @@ -13,7 +14,7 @@ @Setter @ToString @AllArgsConstructor -public class Film { +public class Film extends DataModel implements Comparable { private Long id; @@ -32,4 +33,14 @@ public class Film { @Positive(message = "неверное значение") @Min(1) private int duration; + + private Set likesId; + + @Override + public int compareTo(Film o) { + if (o.getLikesId() == null) { + return 0; + } + return o.getLikesId().size() - this.getLikesId().size(); + } } \ No newline at end of file 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 4ffa7ed..c8dd66e 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -5,13 +5,14 @@ import lombok.*; import java.time.LocalDate; +import java.util.Set; @Setter @Getter @ToString @AllArgsConstructor -@EqualsAndHashCode(exclude = {"id", "name", "birthday"}) -public class User { +@EqualsAndHashCode(exclude = {"id", "name", "birthday"}, callSuper = false) +public class User extends DataModel { private Long id; @@ -19,7 +20,6 @@ public class User { @Email(message = "email не соотвествует формату") private String email; - @NotNull(message = "логин не заполнен") private String login; @@ -29,4 +29,8 @@ public class User { @Past(message = "дата из будущего") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate birthday; + + private Set friendsId; + + } 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..e9aea22 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -0,0 +1,105 @@ +package ru.yandex.practicum.filmorate.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.controller.ValidateController; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.FilmStorage; +import ru.yandex.practicum.filmorate.storage.UserStorage; + +import java.util.Collection; +import java.util.stream.Collectors; + +@Service +public class FilmService { + + private final FilmStorage filmStorage; + + private final UserStorage userStorage; + private final ValidateController validateController; + private final Logger logger = LoggerFactory.getLogger(FilmService.class); + + @Autowired + public FilmService(FilmStorage filmStorage, UserStorage userStorage, ValidateController validateController) { + this.filmStorage = filmStorage; + this.userStorage = userStorage; + this.validateController = validateController; + + } + + public Film create(Film film) { + validateController.validateFilm(film); + Film returnFilm = filmStorage.create(film); + logger.info("Фильм добавлен id: " + returnFilm.getId()); + return returnFilm; + } + + public Film getFilmId(long id) { + Film getFilm = filmStorage.getId(id); + if (getFilm == null) { + throw new ErrorIsNull("нет такого id"); + } + logger.info("поиск по id=" + id); + return getFilm; + } + + public Collection getAll() { + logger.info("Вернули список"); + Collection returnAll = filmStorage.getAll(); + if (returnAll.isEmpty()) { + throw new ErrorIsNull("список пуст"); + } + return returnAll; + } + + public void delete(long id) { + Film delFilm = filmStorage.delete(id); + if (delFilm == null) { + throw new ErrorIsNull("нет такого id"); + } + logger.info("удаление id=" + id); + } + + public Film update(Film newFilm) { + + if (newFilm.getId() == null) { + logger.warn("ID пустой"); + throw new ValidationException("ID фильма пустой"); + } + Film oldFilm = filmStorage.getId(newFilm.getId()); + + if (oldFilm == null) { + logger.warn("фильм с таким ID не найден"); + throw new ErrorIsNull("фильм с таким ID не найден"); + } + validateController.validateFilm(newFilm); + logger.info("запись фильма обновлена"); + return filmStorage.update(newFilm); + } + + public Collection likeAdd(long id, long userId) { + getFilmId(id); + userStorage.getId(userId); + return filmStorage.setLikeId(id, userId); + } + + public Collection likeDelete(long id, long userId) { + getFilmId(id); + userStorage.getId(userId); + return filmStorage.delLikesId(id, userId); + } + + public Collection getPopular(int count) { + + return getAll().stream() + .sorted(Film::compareTo) + .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..1ecaa82 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -0,0 +1,104 @@ +package ru.yandex.practicum.filmorate.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.controller.ValidateController; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.UserStorage; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class UserService { + + private final UserStorage userStorage; + private final ValidateController validateController; + private final Logger logger = LoggerFactory.getLogger(FilmService.class); + + @Autowired + public UserService(UserStorage userStorage, ValidateController validateController) { + this.userStorage = userStorage; + this.validateController = validateController; + } + + public Collection friendsAdd(long id, long friendsId) { + getUserId(id); + getUserId(friendsId); + return userStorage.setFriendId(id, friendsId); + + } + + public Collection friendsDelete(long id, long friendsId) { + getUserId(id); + getUserId(friendsId); + return userStorage.delFriendId(id, friendsId); + } + + public Collection friendsGetList(long id) { + getUserId(id); + + return userStorage.getFriendId(id).stream().map(this::getUserId).collect(Collectors.toList()); + } + + public User create(User user) { + validateController.validateUser(user); + User returnUser = userStorage.create(user); + logger.info("пользователь добавлен id: " + returnUser.getId()); + return returnUser; + } + + public Collection getAll() { + logger.info("Вернули список"); + Collection returnAll = userStorage.getAll(); + if (returnAll.isEmpty()) { + throw new ErrorIsNull("список пуст"); + } + return returnAll; + } + + public User getUserId(long id) { + User getUser = userStorage.getId(id); + if (getUser == null) { + throw new ErrorIsNull("нет такого id"); + } + logger.info("поиск по id=" + id); + return getUser; + } + + public void delete(long id) { + User delUser = userStorage.delete(id); + if (delUser == null) { + throw new ErrorIsNull("нет такого id"); + } + logger.info("удаление id=" + id); + } + + public User update(User newUser) { + + if (newUser.getId() == null) { + logger.warn("ID пустой"); + throw new ValidationException("ID пользователя пустой"); + } + User oldUser = userStorage.getId(newUser.getId()); + + if (oldUser == null) { + logger.warn("пользователь с таким ID не найден"); + throw new ErrorIsNull("пользователь с таким ID не найден"); + } + validateController.validateUser(newUser); + logger.info("запись пользователя обновлена"); + return userStorage.update(newUser); + } + + public List friendsGetCommonList(long id, long otherId) { + Collection userList1 = friendsGetList(id); + Collection userList2 = friendsGetList(otherId); + return userList1.stream().filter(userList2::contains).collect(Collectors.toList()); + } +} 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..38ac22e --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java @@ -0,0 +1,24 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.DataModel; + +import java.util.Collection; + +public interface FilmStorage { + + T create(T data); + + T update(T data); + + T delete(long id); + + T getId(long id); + + Collection getAll(); + + Collection getLikeId(long id); + + Collection setLikeId(long id, long userId); + + Collection delLikesId(long id, long userId); +} 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..a625616 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java @@ -0,0 +1,73 @@ +package ru.yandex.practicum.filmorate.storage; + +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +@Repository +public class InMemoryFilmStorage extends Storage implements FilmStorage { + + @Override + public Film create(Film film) { + film.setId(getNextId()); + if (film.getLikesId() == null) { + film.setLikesId(new HashSet<>() { + }); + } + dataMap.put(film.getId(), film); + logger.info("Фильм добавлен id: " + film.getId()); + return film; + } + + @Override + public Film update(Film newFilm) { + Film oldFilm = dataMap.get(newFilm.getId()); + oldFilm.setName(newFilm.getName()); + oldFilm.setDescription(newFilm.getDescription()); + oldFilm.setReleaseDate(newFilm.getReleaseDate()); + oldFilm.setDuration(newFilm.getDuration()); + + if (newFilm.getLikesId() == null) { + newFilm.setLikesId(new HashSet<>() { + }); + } + oldFilm.setLikesId(newFilm.getLikesId()); + logger.info("Запись фильма обновлена"); + return oldFilm; + } + + @Override + public Collection getLikeId(long id) { + Film getLikes = dataMap.get(id); + return getLikes.getLikesId(); + } + + @Override + public Collection setLikeId(long id, long userId) { + Film getLikes = dataMap.get(id); + getLikes.getLikesId().add(userId); + return getLikes.getLikesId(); + } + + @Override + public Collection delLikesId(long id, long userId) { + Film getLikes = dataMap.get(id); + Set newLikesList = getLikes.getLikesId(); + newLikesList.remove(id); + getLikes.setLikesId(newLikesList); + return newLikesList; + } + + private long getNextId() { + long currentMaxId = dataMap.keySet() + .stream() + .mapToLong(id -> id) + .max() + .orElse(0); + return ++currentMaxId; + + } +} 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..3fc2128 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java @@ -0,0 +1,78 @@ +package ru.yandex.practicum.filmorate.storage; + +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.*; + +@Repository +public class InMemoryUserStorage extends Storage implements UserStorage { + + @Override + public User create(User user) { + user.setId(getNextId()); + if (user.getFriendsId() == null) { + user.setFriendsId(new HashSet<>() { + }); + } + dataMap.put(user.getId(), user); + logger.info("Пользователь добавлен id: " + user.getId()); + return user; + } + + @Override + public User update(User newUser) { + User oldUser = dataMap.get(newUser.getId()); + oldUser.setName(newUser.getName()); + oldUser.setEmail(newUser.getEmail()); + oldUser.setLogin(newUser.getLogin()); + oldUser.setBirthday(newUser.getBirthday()); + oldUser.setFriendsId(newUser.getFriendsId()); + logger.info("Запись пользователя обновлена"); + return oldUser; + } + + @Override + public Collection getFriendId(long id) { + User getFriends = dataMap.get(id); + return getFriends.getFriendsId(); + } + + @Override + public Collection setFriendId(long id, long friendsId) { + User getFriends = dataMap.get(id); //добавление в друзья + Set newFriendsList = getFriends.getFriendsId(); + newFriendsList.add(friendsId); + getFriends.setFriendsId(newFriendsList); + + User getFriendsTo = dataMap.get(friendsId); //зеркальное добавление в друзья + Set newFriendsListTo = getFriendsTo.getFriendsId(); + newFriendsListTo.add(id); + getFriendsTo.setFriendsId(newFriendsListTo); + return newFriendsList; + } + + @Override + public Collection delFriendId(long id, long friendsId) { + User getFriends = dataMap.get(id); //удаление из друзей + Set newFriendsList = getFriends.getFriendsId(); + newFriendsList.remove(friendsId); + getFriends.setFriendsId(newFriendsList); + + User getFriendsTo = dataMap.get(friendsId); //удаление из друзей + Set newFriendsListTo = getFriendsTo.getFriendsId(); + newFriendsListTo.remove(id); + getFriendsTo.setFriendsId(newFriendsListTo); + + return newFriendsList; + } + + private long getNextId() { + long currentMaxId = dataMap.keySet() + .stream() + .mapToLong(id -> id) + .max() + .orElse(0); + return ++currentMaxId; + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/Storage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/Storage.java new file mode 100644 index 0000000..1be99fc --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/Storage.java @@ -0,0 +1,33 @@ +package ru.yandex.practicum.filmorate.storage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.yandex.practicum.filmorate.exception.ErrorIsNull; +import ru.yandex.practicum.filmorate.model.DataModel; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class Storage { + protected final Map dataMap = new HashMap<>(); + + protected final Logger logger = LoggerFactory.getLogger(Storage.class); + + public T delete(long id) { + return dataMap.remove(id); + } + + public T getId(long id) { + if (!dataMap.containsKey(id)) { + throw new ErrorIsNull("нет такого id"); + } + logger.info("Вернули по id=" + id); + return dataMap.get(id); + } + + public Collection getAll() { + logger.info("Вернули список"); + return dataMap.values(); + } +} 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..3cb17a4 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java @@ -0,0 +1,24 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.DataModel; + +import java.util.Collection; + +public interface UserStorage { + + T create(T data); + + T update(T data); + + T delete(long id); + + T getId(long id); + + Collection getAll(); + + Collection getFriendId(long id); + + Collection setFriendId(long id, long userId); + + Collection delFriendId(long id, long userId); +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..94c6c3e --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,5 @@ +logging: + level: + org: + zalando: + logbook: TRACE \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java index cf7b4b2..78fce0a 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java @@ -10,16 +10,20 @@ import ru.yandex.practicum.filmorate.model.Film; import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; public class FilmControllerTest { Exception exception; private final ValidateController validate = new ValidateController(); - Film film; + private Film film; + Set testList = new HashSet<>() { + }; @BeforeEach void setUp() { - film = new Film(1L, "name", "описание ", LocalDate.now().minusYears(3), 20); + film = new Film(1L, "name", "описание ", LocalDate.now().minusYears(3), 20, testList); } @Test @@ -98,5 +102,4 @@ public void filmIs0Duration() { Assertions.assertEquals("продолжительность <= 0", exception.getMessage()); } - } diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java index 973cfa7..1f633f6 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java @@ -9,17 +9,21 @@ import ru.yandex.practicum.filmorate.model.User; import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; public class UserControllerTest { Exception exception; private final ValidateController validate = new ValidateController(); - User user; + private User user; + Set testList = new HashSet<>() { + }; @BeforeEach void setUp() { - user = new User(1L, "ya@yandex.ru", "login", "name", LocalDate.now().minusYears(3)); + user = new User(1L, "ya@yandex.ru", "login", "name", LocalDate.now().minusYears(3), testList); } @Test @@ -92,4 +96,11 @@ public void userFutureBirthday() { Assertions.assertEquals("введена дата из будущего", exception.getMessage()); } + @Test + public void userFriends() { + User user1 = new User(1L, "ya1@yandex.ru", "login1", "name1", LocalDate.now().minusYears(3), testList); + User user2 = new User(2L, "ya2@yandex.ru", "login2", "name2", LocalDate.now().minusYears(3), testList); + user1.getFriendsId().add(2L); + Assertions.assertEquals(2, user1.getFriendsId().stream().toList().get(0)); + } } diff --git a/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java b/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java index de8c47f..a37520e 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java @@ -10,6 +10,7 @@ import ru.yandex.practicum.filmorate.model.User; import java.time.LocalDate; +import java.util.HashSet; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -19,6 +20,8 @@ public class ValidateTest { private Validator validator; + Set testList = new HashSet<>() { + }; @BeforeEach void setUp() { @@ -27,14 +30,14 @@ void setUp() { @Test void validatorUserOk() { - User user = new User(1L, "name@yabex.ru", "log", "name", LocalDate.now().minusYears(3)); + User user = new User(1L, "name@yabex.ru", "log", "name", LocalDate.now().minusYears(3), testList); Set> violations = validator.validate(user); assertTrue(violations.isEmpty()); } @Test void validatorUserTest() { - User user = new User(1L, "nameyabex.ru", "log", "name", LocalDate.now().plusYears(3)); + User user = new User(1L, "nameyabex.ru", "log", "name", LocalDate.now().plusYears(3), testList); Set> violations = validator.validate(user); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(2); @@ -44,7 +47,7 @@ void validatorUserTest() { @Test void validatorUserNotNull() { - User user = new User(1L, null, null, "name", null); + User user = new User(1L, null, null, "name", null, testList); Set> violations = validator.validate(user); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(3); @@ -54,7 +57,7 @@ void validatorUserNotNull() { @Test void validatorFilmOk() { - Film film = new Film(1L, "kino", "kino o kine", LocalDate.of(1990, 02, 22), 25); + Film film = new Film(1L, "kino", "kino o kine", LocalDate.of(1990, 02, 22), 25, testList); Set> violations = validator.validate(film); assertTrue(violations.isEmpty()); } @@ -62,7 +65,7 @@ void validatorFilmOk() { @Test void validatorFilmTest() { Film film = new Film(1L, "kino", Strings.repeat("*", 220), - LocalDate.now().plusYears(3), 2); + LocalDate.now().plusYears(3), 2, testList); Set> violations = validator.validate(film); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(2); @@ -73,7 +76,7 @@ void validatorFilmTest() { @Test void validatorFilmNotNull() { Film film = new Film(1L, null, null, - null, 2); + null, 2, testList); Set> violations = validator.validate(film); assertFalse(violations.isEmpty()); assertThat(violations).hasSize(3);