Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a8611f2
fix: deleted ValidBirthday, BirthdayValidator and BirthdayValidatorTe…
CrodiYa Oct 25, 2025
e810dea
fix: typo
CrodiYa Oct 26, 2025
b0fae1b
deleted: ValidBirthday classes
CrodiYa Oct 26, 2025
1b3d079
fear: add org.zalando.logbook-spring-boot-starter
CrodiYa Oct 29, 2025
f9d09f1
feat: add new dev profile and moved all .properties to src/main/resou…
CrodiYa Oct 29, 2025
80f506f
feat: add abstract class StorageData to represent model classes.
CrodiYa Oct 29, 2025
9f89aed
feat: Add inheritance from StorageData
CrodiYa Oct 29, 2025
bb147bb
feat: Add inheritance from StorageData. Add new read-only field "likes".
CrodiYa Oct 29, 2025
d8597ee
feat: Add interface to represent Storage. Basic CRUD operations.
CrodiYa Oct 29, 2025
0f262c0
feat: Add interfaces with special methods to User and Film.
CrodiYa Oct 29, 2025
07fb18b
feat: Add abstract class AbstractStorage. Implements BasicStorage
CrodiYa Oct 29, 2025
d121baf
feat: Add InMemoryUserStorage. Implements UserStorage. Extends Abstra…
CrodiYa Oct 29, 2025
f3695cc
feat: Add InMemoryFilmStorage. Implements FilmStorage. Extends Abstra…
CrodiYa Oct 29, 2025
2c61d03
feat: Add new ExceptionHandler. Refactored code for DRY principle.
CrodiYa Oct 29, 2025
a81d565
feat: Add FriendShipService. Works with storage and controls al opera…
CrodiYa Oct 29, 2025
3db7ff6
feat: Add UserService. Works with storage and controls all operations…
CrodiYa Oct 29, 2025
c06faec
feat: Add FilmLikeService. Works with storage and controls all operat…
CrodiYa Oct 29, 2025
cbe1aa4
feat: Add FilmService. Works with storage and controls all operations…
CrodiYa Oct 29, 2025
a9a419f
feat: Completely reworked Controller. Deleted everything that control…
CrodiYa Oct 29, 2025
995c9d3
feat: Completely reworked Controller. Deleted everything that control…
CrodiYa Oct 29, 2025
9eb999c
test: Improved ControllerTestes, added more test for previous endpoin…
CrodiYa Oct 29, 2025
ca135bf
test: Wrote tests for methods that have their own logic (not just cal…
CrodiYa Oct 29, 2025
17e1132
test: Wrote tests for AbstractStorage. Tests uses their own TestStora…
CrodiYa Oct 29, 2025
7b70e5f
test: Wrote tests for InMemoryStorages.
CrodiYa Oct 29, 2025
4aea8c5
fix: deleted AbstractStorage.java and all classes connected to it.
CrodiYa Nov 1, 2025
1a5c31a
fix: deleted Like and Friend services
CrodiYa Nov 1, 2025
8c983b4
fix: reworked storage interfaces
CrodiYa Nov 1, 2025
7d40e5b
feat: add FriendShipStorage as main storage for friendship connections
CrodiYa Nov 1, 2025
c8209ca
feat: add LikeStorage as main storage for likes.
CrodiYa Nov 1, 2025
921c5c6
fix: reworked storage with solid principles.
CrodiYa Nov 1, 2025
2bd7c2a
feat: add interfaces for services.
CrodiYa Nov 1, 2025
d8ee649
feat: reworked services, now business logic is only here.
CrodiYa Nov 1, 2025
168cf3c
feat: removed inheritance
CrodiYa Nov 1, 2025
170a5c0
feat: moved business logic to services
CrodiYa Nov 1, 2025
d24b53c
feat: add tests
CrodiYa Nov 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring-boot-starter</artifactId>
<version>3.7.2</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,39 @@
package ru.yandex.practicum.filmorate.controller;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.service.film.FilmServiceInterface;

import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/films")
@Slf4j
public class FilmController {

private final HashMap<Long, Film> films = new HashMap<>();
private final AtomicLong idGenerator = new AtomicLong();
private final FilmServiceInterface filmService;

/**
* Private constructor to initialize ID generator with starting ID = 1.
* Constructor for dependency injection

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вместо многострочного комментария лучше использовать Java Docs

*/
private FilmController() {
idGenerator.set(1);
public FilmController(FilmServiceInterface filmService) {
this.filmService = filmService;
}

/**
* Handles GET method.
* <p>Retrieves all films from the storage.
* <p>Retrieves all filmsStorage from the storage.
*
* @return Collection of all films.
* @return Collection of all filmsStorage.
*/
@GetMapping
public Collection<Film> getFilms() {
log.info("GET /films - returning {} films", films.size());
return films.values();
return filmService.getAllFilms();
}

/**
Expand All @@ -46,13 +44,7 @@ public Collection<Film> getFilms() {
*/
@PostMapping
public Film addFilm(@Valid @RequestBody Film film) {

film.setId(idGenerator.getAndIncrement());
films.put(film.getId(), film);

log.info("POST /films - Film created: {}", film);

return film;
return filmService.addFilm(film);
}

/**
Expand All @@ -75,15 +67,46 @@ public Film updateFilm(@Valid @RequestBody Film newFilm) {
throw new ValidationException("Id должен быть указан");
}

if (films.containsKey(newFilm.getId())) {
Film oldFilm = films.get(newFilm.getId());
return filmService.updateFilm(newFilm);
}

films.put(newFilm.getId(), newFilm);
log.info("PUT /films - Film updated: {}", oldFilm);
/**
* Handles GET method.
* <p>Retrieves top {@code count} popular films based on likes.
*
* @param count must be provided by url params, not required default value is 10.
* @return collection of films.
*/
@GetMapping("/popular")
public Collection<Film> getPopular(@RequestParam(required = false, defaultValue = "10") @Positive Long count) {
return filmService.getTopFilms(count);
}

return newFilm;
}
/**
* Handles PUT method.
* <p>Adds like to specific film by specific user. If like from this user is already set - nothing happens.
*
* @param filmId Film`s id where likes amount should be increased. Must be positive number.
* @param userId User`s id who sets like to the film. Must be positive number.
* @return Film where like was set.
*/
@PutMapping("/{filmId}/like/{userId}")
public Film addLike(@PathVariable @Positive Long filmId,
@PathVariable @Positive Long userId) {
return filmService.addLike(filmId, userId);
}

throw new NotFoundException("Фильм с id = " + newFilm.getId() + " не найден");
/**
* Handles PUT method.
* <p>Deletes like from specific film by specific user. If like was never there - nothing happens.
*
* @param filmId Film`s id where likes amount should be decreased. Must be positive number.
* @param userId User`s id who deletes like to the film. Must be positive number.
* @return Film where like was deleted.
*/
@DeleteMapping("/{filmId}/like/{userId}")
public Film deleteLike(@PathVariable @Positive Long filmId,
@PathVariable @Positive Long userId) {
return filmService.removeLike(filmId, userId);
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
package ru.yandex.practicum.filmorate.controller;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.User;
import ru.yandex.practicum.filmorate.service.user.UserServiceInterface;

import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {

private final HashMap<Long, User> users = new HashMap<>();
private final AtomicLong idGenerator = new AtomicLong();
private final UserServiceInterface userService;

/**
* Private constructor to initialize ID generator with starting ID = 1.
* Constructor for dependency injection
*/
private UserController() {
idGenerator.set(1);
public UserController(UserServiceInterface userService) {
this.userService = userService;
}

/**
Expand All @@ -34,8 +34,7 @@ private UserController() {
*/
@GetMapping
public Collection<User> getUsers() {
log.info("GET /users - returning {} users", users.size());
return users.values();
return userService.getAllUsers();
}

/**
Expand All @@ -46,17 +45,7 @@ public Collection<User> getUsers() {
*/
@PostMapping
public User addUser(@Valid @RequestBody User user) {

user.setId(idGenerator.getAndIncrement());

if (user.getName() == null) {
user.setName(user.getLogin());
}

log.info("POST /users - User created: {}", user);
users.put(user.getId(), user);

return user;
return userService.addUser(user);
}

/**
Expand All @@ -71,24 +60,70 @@ public User addUser(@Valid @RequestBody User user) {
*
* @return updated user.
* @throws ValidationException if ID is null or not valid
* @throws NotFoundException if user is not found
* @throws NotFoundException if user is not found
*/
@PutMapping
public User updateUser(@Valid @RequestBody User newUser) {
if (newUser.getId() == null) {
throw new ValidationException("Id должен быть указан");
}

if (users.containsKey(newUser.getId())) {
User oldUser = users.get(newUser.getId());
return userService.updateUser(newUser);
}

users.put(newUser.getId(), newUser);
/**
* Handles GET method.
* <p>Return collection of user`s friends.
*
* @param id user`s id. Must be positive number.
* @return collection of users.
* @throws NotFoundException if user is not found
*/
@GetMapping("/{id}/friends")
public Collection<User> getFriends(@PathVariable @Positive Long id) {
return userService.getFriends(id);
}

log.info("PUT /users - User updated: {}", oldUser);
/**
* Handles GET method.
* <p>Return collection of users that are common between two users.
*
* @param id user`s id. Must be positive number.
* @param otherId other user`s id. Must be positive number.
* @return collection of users.
* @throws NotFoundException if user is not found
*/
@GetMapping("/{id}/friends/common/{otherId}")
public Collection<User> getCommonFriends(@PathVariable @Positive Long id,
@PathVariable @Positive Long otherId) {
return userService.getCommonFriends(id, otherId);
}

return newUser;
}
/**
* Handles PUT method.
* <p> Creates friendship between two users.
* User`s consent is not required.
*
* @param id user`s id. Sender. Must be positive number.
* @param friendId friend`s id. Receiver. Must be positive number.
* @throws NotFoundException if user is not found
* @throws ValidationException if id equals friendId
*/
@PutMapping("/{id}/friends/{friendId}")
public void addFriend(@PathVariable @Positive Long id,
@PathVariable @Positive Long friendId) {
userService.addFriend(id, friendId);
}

throw new NotFoundException("Пользователь с id = " + newUser.getId() + " не найден");
/**
* Handles DELETE method.
* <p> Breaks friendship between two users.
* User`s consent is not required.
*
* @param id user`s id. Sender. Must be positive number.
* @param friendId friend`s id. Receiver. Must be positive number.
* @throws NotFoundException if user is not found
*/
@DeleteMapping("/{id}/friends/{friendId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteFriend(@PathVariable @Positive Long id,
@PathVariable @Positive Long friendId) {
userService.deleteFriend(id, friendId);
}
}
Loading