Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -27,11 +27,17 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

@SpringBootApplication
public class FilmorateApplication {
public static void main(String[] args) {
SpringApplication.run(FilmorateApplication.class, args);
}

public static void main(String[] args) {
SpringApplication.run(FilmorateApplication.class, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,68 @@

import org.springframework.web.bind.annotation.RestController;

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

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/films")
@Slf4j
public class FilmController {
private final Map<Long, Film> films = new HashMap<>();
private Long idCounter = 1L;

private void validateFilm(Film film) {
if (film.getName() == null || film.getName().isBlank()) {
log.warn("Название фильма не может быть пустым");
throw new ValidationException("Название фильма не может быть пустым");
}
if (film.getDescription() != null && film.getDescription().length() > 200) {
log.warn("Максимальная длина описания — 200 символов");
throw new ValidationException("Максимальная длина описания — 200 символов");
}
if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) {
log.warn("Дата релиза должна быть не раньше 28 декабря 1895 года");
throw new ValidationException("Дата релиза должна быть не раньше 28 декабря 1895 года");
}
if (film.getDuration() <= 0) {
log.warn("Продолжительность фильма должна быть положительным числом");
throw new ValidationException("Продолжительность фильма должна быть положительным числом");
}
}

@PostMapping
public Film addFilm(@Valid @RequestBody Film film) {
validateFilm(film);
film.setId(idCounter++);
films.put(film.getId(), film);
log.info("Добавлен фильм с ID {}: {}", film.getId(), film);
return film;
}

@PutMapping
public Film updateFilm(@Valid @RequestBody Film film) {
if (film.getId() == null || !films.containsKey(film.getId())) {
log.warn("Фильм с ID {} не найден", film.getId());
throw new ValidationException("Фильм с указанным ID не существует");
}
validateFilm(film);
films.put(film.getId(), film);
log.info("Обновлен фильм с ID {}: {}", film.getId(), film);
return film;
}

@GetMapping
public List<Film> getAllFilms() {
log.info("Получен запрос всех фильмов. Текущее количество: {}", films.size());
return new ArrayList<>(films.values());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package ru.yandex.practicum.filmorate.controller;

import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.User;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
private final Map<Long, User> users = new HashMap<>();
private Long idCounter = 1L;

private void validateUser(User user) {
if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) {
log.warn("Электронная почта не может быть пустой и должна содержать символ @");
throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @");
}
if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) {
log.warn("Логин не может быть пустым и содержать пробелы");
throw new ValidationException("Логин не может быть пустым и содержать пробелы");
}
if (user.getName() == null || user.getName().isBlank()) {
user.setName(user.getLogin());
log.info("Для пользователя {} установлено имя из логина", user.getLogin());
}
if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) {
log.warn("Дата рождения не может быть в будущем");
throw new ValidationException("Дата рождения не может быть в будущем");
}
}

@PostMapping
public User createUser(@Valid @RequestBody User user) {
validateUser(user);
user.setId(idCounter++);
users.put(user.getId(), user);
log.info("Создан пользователь: {}", user);
return user;
}

@PutMapping
public User updateUser(@Valid @RequestBody User user) {
if (user.getId() == null || !users.containsKey(user.getId())) {
log.warn("Пользователь с id {} не найден", user.getId());
throw new ValidationException("Пользователь с указанным id не существует");
}
validateUser(user);
users.put(user.getId(), user);
log.info("Обновлен пользователь: {}", user);
return user;
}

@GetMapping
public List<User> getAllUsers() {
log.info("Получен запрос всех пользователей. Текущее количество: {}", users.size());
return new ArrayList<>(users.values());
}
}
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);
}
}
32 changes: 24 additions & 8 deletions src/main/java/ru/yandex/practicum/filmorate/model/Film.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
package ru.yandex.practicum.filmorate.model;

import lombok.Getter;
import lombok.Setter;

/**
* Film.
*/
@Getter
@Setter
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;

import java.time.LocalDate;


@Data
public class Film {

Choose a reason for hiding this comment

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

Необязательно, на Ваше усмотрение. Для этих полей можно было бы добавить ограничительные аннотации, например:

public class Film
...
	@NotBlank
	private String name;
	@Size(min = 1, max = 200)
	private String description;
	@NotNull
	private LocalDate releaseDate;
	@Min(1)
	private long duration; // minutes

Тогда можно будет избавиться от части ручных проверок.

private Long id;

@NotBlank
private String name;

@NotBlank
@Size(min = 1, max = 200)
private String description;

@NotNull
private LocalDate releaseDate;

@Min(1)
private int duration;
}
21 changes: 21 additions & 0 deletions src/main/java/ru/yandex/practicum/filmorate/model/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ru.yandex.practicum.filmorate.model;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

import java.time.LocalDate;

@Data
public class User {

Choose a reason for hiding this comment

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

Необязательно, на Ваше усмотрение.
Здесь такая же ситуация, что и c классом фильм - Можно добавить ограничительные аннотации на поля  класса. Тогда часть проверок будет выполняться средствами Spring в  методе контроллера.

public class User {  
    @NotBlank  
    @Email   
    private String email;  
    @NotBlank  
    private String login;
    ............

private Long id;

@NotBlank
@Email
private String email;

Choose a reason for hiding this comment

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

Обратите внимание, что для автоматической проверки нужны не только аннотации над полями класса, но и добавить аннотацию @Valid в соответствующих методах контроллера User
public User create(@Valid @RequestBody final User user) {


@NotBlank
private String login;
private String name;
private LocalDate birthday;
}
56 changes: 56 additions & 0 deletions src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ru.yandex.practicum.filmorate;

import ru.yandex.practicum.filmorate.controller.FilmController;
import ru.yandex.practicum.filmorate.controller.UserController;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.model.User;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import static org.junit.jupiter.api.Assertions.*;

class BoundaryTests {
private FilmController filmController = new FilmController();
private UserController userController = new UserController();

@Test
void filmValidation_BoundaryValues() {
// Проверка минимально допустимой даты релиза
Film film1 = new Film();
film1.setName("Boundary Film");
film1.setDescription("Desc");
film1.setReleaseDate(LocalDate.of(1895, 12, 28));
film1.setDuration(1);
assertDoesNotThrow(() -> filmController.addFilm(film1));

// Проверка максимальной длины описания
Film film2 = new Film();
film2.setName("Boundary Film");
film2.setDescription("a".repeat(200));
film2.setReleaseDate(LocalDate.of(2000, 1, 1));
film2.setDuration(1);
assertDoesNotThrow(() -> filmController.addFilm(film2));
}

@Test
void userValidation_BoundaryValues() {
// Проверка сегодняшней даты рождения
User user1 = new User();
user1.setEmail("test@example.com");
user1.setLogin("testlogin");
user1.setBirthday(LocalDate.now());
assertDoesNotThrow(() -> userController.createUser(user1));

// Проверка минимально допустимого логина
User user2 = new User();
user2.setEmail("test@example.com");
user2.setLogin("a"); // Минимальный логин
user2.setBirthday(LocalDate.of(1990, 1, 1));
assertDoesNotThrow(() -> userController.createUser(user2));
}

@Test
void nullRequest_ThrowsException() {
assertThrows(Exception.class, () -> filmController.addFilm(null));
assertThrows(Exception.class, () -> userController.createUser(null));
}
}
Loading
Loading