Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d7cb868
feat: Implement controllers and models for filmorate
Darya346 Dec 11, 2025
87b2811
fix: Correctly handle validation exceptions
Darya346 Dec 11, 2025
0b3aa4c
fix: Add manual validation calls for unit tests
Darya346 Dec 11, 2025
218e5fd
fix: Return 404 on update for unknown entities
Darya346 Dec 11, 2025
c86dd39
Use @FieldDefaults
Darya346 Dec 11, 2025
1021353
feat: add friends and likes functionality
Darya346 Dec 24, 2025
7c42e67
исправлен чекстайл
Darya346 Dec 24, 2025
50611ea
исправление ошибок чекстайла
Darya346 Dec 24, 2025
616e7d5
исправлены ошибки чекстайла
Darya346 Dec 24, 2025
fa739a3
исрпавлены ошибки в тестах
Darya346 Dec 24, 2025
e23d473
добавлен FieldDefaults
Darya346 Dec 25, 2025
60bfcf0
docs: Add ER diagram and update README
Darya346 Jan 13, 2026
6ad5059
исправлено:Переплетение связей в БД, наименования полей в friendship,…
Darya346 Jan 13, 2026
79f5ec0
feat: Implement database storage and pass all tests
Darya346 Jan 20, 2026
4c12378
Merge branch 'add-database' of https://github.com/Darya346/java-filmo…
Darya346 Jan 20, 2026
35306e3
fix: Fix Checkstyle issue in FilmController
Darya346 Jan 20, 2026
dcb9466
fix checkstyle
Darya346 Jan 20, 2026
e950b1d
fix checkstyle
Darya346 Jan 20, 2026
2bebe4f
fix database errors
Darya346 Jan 20, 2026
7d42bdc
fix errors
Darya346 Jan 20, 2026
960bbc5
refactor: Apply review suggestions
Darya346 Jan 21, 2026
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
Binary file added filmorate_database.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 17 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/> <!-- lookup parent from repository -->
<version>3.2.5</version>
<relativePath/>
</parent>
<groupId>ru.yandex.practicum</groupId>
<artifactId>filmorate</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>filmorate</name>
<description>filmorate</description>
<properties>
<java.version>21</java.version>
</properties>
Expand All @@ -21,19 +19,30 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
Expand All @@ -42,5 +51,4 @@
</plugin>
</plugins>
</build>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ public class FilmorateApplication {
public static void main(String[] args) {
SpringApplication.run(FilmorateApplication.class, args);
}

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
package ru.yandex.practicum.filmorate.controller;

import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.service.FilmService;
import java.util.List;

@RestController
@RequestMapping("/films")
@RequiredArgsConstructor
public class FilmController {
}
private final FilmService filmService;

@PostMapping
public Film create(@Valid @RequestBody Film film) {
return filmService.create(film);
}

@PutMapping
public Film update(@Valid @RequestBody Film film) {
return filmService.update(film);
}

@GetMapping
public List<Film> getAll() {
return filmService.getAll();
}

@GetMapping("/{id}")
public Film getById(@PathVariable int id) {
return filmService.getById(id);
}

@PutMapping("/{id}/like/{userId}")
public void addLike(@PathVariable int id, @PathVariable int userId) {
filmService.addLike(id, userId);
}

@DeleteMapping("/{id}/like/{userId}")
public void removeLike(@PathVariable int id, @PathVariable int userId) {
filmService.removeLike(id, userId);
}

@GetMapping("/popular")
public List<Film> getPopularFilms(@RequestParam(defaultValue = "10", required = false) int count) {
return filmService.getPopularFilms(count);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ru.yandex.practicum.filmorate.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.yandex.practicum.filmorate.model.Genre;
import ru.yandex.practicum.filmorate.service.GenreService;

import java.util.List;

@RestController
@RequestMapping("/genres")
@RequiredArgsConstructor
public class GenreController {

private final GenreService genreService;

@GetMapping
public List<Genre> getAll() {
return genreService.getAll();
}

@GetMapping("/{id}")
public Genre getById(@PathVariable int id) {
return genreService.getById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ru.yandex.practicum.filmorate.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.yandex.practicum.filmorate.model.MpaRating;
import ru.yandex.practicum.filmorate.service.MpaService;

import java.util.List;

@RestController
@RequestMapping("/mpa")
@RequiredArgsConstructor
public class MpaController {

private final MpaService mpaService;

@GetMapping
public List<MpaRating> getAll() {
return mpaService.getAll();
}

@GetMapping("/{id}")
public MpaRating getById(@PathVariable int id) {
return mpaService.getById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ru.yandex.practicum.filmorate.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import ru.yandex.practicum.filmorate.model.User;
import ru.yandex.practicum.filmorate.service.UserService;

import java.util.List;

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

private final UserService userService;
private static final String FRIEND_PATH = "/{id}/friends/{friendId}";

@PostMapping
public User create(@Valid @RequestBody User user) {
return userService.create(user);
}

@PutMapping
public User update(@Valid @RequestBody User user) {
return userService.update(user);
}

@GetMapping
public List<User> getAll() {
return userService.getAll();
}

@GetMapping("/{id}")
public User getById(@PathVariable int id) {
return userService.getById(id);
}

@PutMapping(FRIEND_PATH)
public void addFriend(@PathVariable int id, @PathVariable int friendId) {
userService.addFriend(id, friendId);
}

@DeleteMapping(FRIEND_PATH)
public void removeFriend(@PathVariable int id, @PathVariable int friendId) {
userService.removeFriend(id, friendId);
}

@GetMapping("/{id}/friends")
public List<User> getFriends(@PathVariable int id) {
return userService.getFriends(id);
}

@GetMapping("/{id}/friends/common/{otherId}")
public List<User> getCommonFriends(@PathVariable int id, @PathVariable int otherId) {
return userService.getCommonFriends(id, otherId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ru.yandex.practicum.filmorate.error;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;

@RestControllerAdvice
@Slf4j
public class ErrorHandler {

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidationException(final MethodArgumentNotValidException e) {
log.error("Ошибка валидации: {}", e.getMessage());
return new ErrorResponse(
e.getBindingResult().getFieldError().getDefaultMessage()
);
}

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleCustomValidationException(final ValidationException e) {
log.error("Ошибка валидации: {}", e.getMessage());
return new ErrorResponse(e.getMessage());
}

@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFoundException(final NotFoundException e) {
log.error("Объект не найден: {}", e.getMessage());
return new ErrorResponse(e.getMessage());
}

@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleThrowable(final Throwable e) {
log.error("Внутренняя ошибка сервера: ", e);
return new ErrorResponse("Произошла непредвиденная ошибка.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.yandex.practicum.filmorate.error;

public class ErrorResponse {
private final String error;

public ErrorResponse(String error) {
this.error = error;
}

public String getError() {
return error;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.yandex.practicum.filmorate.exception;

public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.yandex.practicum.filmorate.exception;

public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message);
}
}
38 changes: 29 additions & 9 deletions src/main/java/ru/yandex/practicum/filmorate/model/Film.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
package ru.yandex.practicum.filmorate.model;

import lombok.Getter;
import lombok.Setter;

/**
* Film.
*/
@Getter
@Setter
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;

import java.time.LocalDate;
import java.util.LinkedHashSet;
import java.util.Set;

@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Film {
}
int id;

@NotBlank(message = "Название не может быть пустым.")
String name;

@Size(max = 200, message = "Описание не должно превышать 200 символов.")
String description;

LocalDate releaseDate;

@Positive(message = "Продолжительность фильма должна быть положительной.")
int duration;

MpaRating mpa;
Set<Genre> genres = new LinkedHashSet<>(); // Используем LinkedHashSet для сохранения порядка
}
16 changes: 16 additions & 0 deletions src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.yandex.practicum.filmorate.model;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;

@Data
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Genre {
int id;
String name;
}
Loading