From 99d644b7de2a123b926f2e1ceeb4debeebc77003 Mon Sep 17 00:00:00 2001 From: alexk11 Date: Tue, 3 Feb 2026 22:02:43 +0300 Subject: [PATCH 01/10] initial commit --- HELP.md | 16 +++++ README.md | 2 +- pom.xml | 64 +++++++++++++++++++ .../com/example/demo/DemoApplication.java | 13 ++++ .../demo/controller/PetController.java | 4 ++ .../demo/controller/UserController.java | 16 +++++ .../exception/GlobalExceptionHandler.java | 22 +++++++ src/main/resources/application.properties | 1 + .../example/demo/DemoApplicationTests.java | 13 ++++ .../com/example/demo/UserServiceTest.java | 47 ++++++++++++++ 10 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 HELP.md create mode 100644 pom.xml create mode 100644 src/main/java/com/example/demo/DemoApplication.java create mode 100644 src/main/java/com/example/demo/controller/PetController.java create mode 100644 src/main/java/com/example/demo/controller/UserController.java create mode 100644 src/main/java/com/example/demo/exception/GlobalExceptionHandler.java create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/com/example/demo/DemoApplicationTests.java create mode 100644 src/test/java/com/example/demo/UserServiceTest.java diff --git a/HELP.md b/HELP.md new file mode 100644 index 0000000..763be44 --- /dev/null +++ b/HELP.md @@ -0,0 +1,16 @@ +# Getting Started + +### Reference Documentation +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.3-SNAPSHOT/maven-plugin) +* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.3-SNAPSHOT/maven-plugin/build-image.html) + +### Maven Parent overrides + +Due to Maven's design, elements are inherited from the parent POM to the project POM. +While most of the inheritance is fine, it also inherits unwanted elements like `` and `` from the parent. +To prevent this, the project POM contains empty overrides for these elements. +If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides. + diff --git a/README.md b/README.md index 8419acd..1bcd147 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # SpringBootMvc -SorokinP + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..238407f --- /dev/null +++ b/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 4.0.3-SNAPSHOT + + + + com.example + demo + 0.0.1-SNAPSHOT + demo + Spring Boot MVC demo + + + 21 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + diff --git a/src/main/java/com/example/demo/DemoApplication.java b/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..64b538a --- /dev/null +++ b/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/src/main/java/com/example/demo/controller/PetController.java b/src/main/java/com/example/demo/controller/PetController.java new file mode 100644 index 0000000..5a45792 --- /dev/null +++ b/src/main/java/com/example/demo/controller/PetController.java @@ -0,0 +1,4 @@ +package com.example.demo.controller; + +public class PetController { +} diff --git a/src/main/java/com/example/demo/controller/UserController.java b/src/main/java/com/example/demo/controller/UserController.java new file mode 100644 index 0000000..ac9a954 --- /dev/null +++ b/src/main/java/com/example/demo/controller/UserController.java @@ -0,0 +1,16 @@ +package com.example.demo.controller; + +@RestController +@RequestMapping("/users") +public class UserController { + + private final UserService userService; + private final UserDtoMapper userDtoMapper; + + @PostMapping + public ResponseEntity createUser(@Valid @RequestBody UserDto userDto) { + var user = userDtoMapper.map(userDto); + return new ResponseEntity<>(userService.createUser(user), HttpStatus.CREATED); + } + +} diff --git a/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java b/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..8c8ac8e --- /dev/null +++ b/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java @@ -0,0 +1,22 @@ +package com.example.demo.exception; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) { + var error = new ErrorMessageResponse("Неверный аргумент", ex.getMessage()); + return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex) { + // Обработка ошибки + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) { + // Обработка ошибки + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..2109a44 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=demo diff --git a/src/test/java/com/example/demo/DemoApplicationTests.java b/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 0000000..2778a6a --- /dev/null +++ b/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/src/test/java/com/example/demo/UserServiceTest.java b/src/test/java/com/example/demo/UserServiceTest.java new file mode 100644 index 0000000..804e21e --- /dev/null +++ b/src/test/java/com/example/demo/UserServiceTest.java @@ -0,0 +1,47 @@ +package com.example.demo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; + + +@AutoConfigureMockMvc +@SpringBootTest +class UserServiceTest { + + @Autowired + private UserService userService; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void shouldCreateNewUser() throws Exception { + + var userDto = new UserDto(null, "Pavel", "test@example.com", 25, List.of()); + String newUserJson = objectMapper.writeValueAsString(userDto); + + var jsonResponse = mockMvc.perform(post("/users") + .contentType(MediaType.APPLICATION_JSON) + .content(newUserJson)) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + var userDtoResponse = objectMapper.readValue(jsonResponse, UserDto.class); + + Assertions.assertEquals(userDto.name(), userDtoResponse.name()); + Assertions.assertEquals(userDto.age(), userDtoResponse.age()); + Assertions.assertEquals(userDto.email(), userDtoResponse.email()); + Assertions.assertEquals(userDto.pets(), userDtoResponse.pets()); + Assertions.assertDoesNotThrow(() -> userService.getUser(userDtoResponse.id())); + } + +} From 2d188633b6543a611be348ec4a4c9f1c8339e5a6 Mon Sep 17 00:00:00 2001 From: alexk11 Date: Tue, 3 Feb 2026 22:07:44 +0300 Subject: [PATCH 02/10] add maven dependencies --- pom.xml | 14 ++++++++++++++ .../example/demo/controller/UserController.java | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/pom.xml b/pom.xml index 238407f..19b82d9 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,20 @@ org.springframework.boot spring-boot-starter + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-devtools + 4.0.2 + compile + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/com/example/demo/controller/UserController.java b/src/main/java/com/example/demo/controller/UserController.java index ac9a954..8df2b36 100644 --- a/src/main/java/com/example/demo/controller/UserController.java +++ b/src/main/java/com/example/demo/controller/UserController.java @@ -1,5 +1,14 @@ package com.example.demo.controller; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + @RestController @RequestMapping("/users") public class UserController { From c4303c487250df4523c590f963065b7879b70b1f Mon Sep 17 00:00:00 2001 From: Alexey Kryuchkov Date: Thu, 5 Feb 2026 16:59:38 +0300 Subject: [PATCH 03/10] implementation --- pom.xml | 5 + .../demo/controller/UserController.java | 38 ++++++-- .../demo/exception/ErrorMessageResponse.java | 20 ++++ .../exception/GlobalExceptionHandler.java | 9 +- .../exception/ResourceNotFoundException.java | 21 +++++ .../java/com/example/demo/model/PetDto.java | 13 +++ .../java/com/example/demo/model/UserDto.java | 18 ++++ .../com/example/demo/service/PetService.java | 78 +++++++++++++++ .../com/example/demo/service/UserService.java | 56 +++++++++++ .../demo/service/inter/IPetService.java | 15 +++ .../demo/service/inter/IUserService.java | 15 +++ .../com/example/demo/UserServiceTest.java | 94 +++++++++---------- 12 files changed, 325 insertions(+), 57 deletions(-) create mode 100644 src/main/java/com/example/demo/exception/ErrorMessageResponse.java create mode 100644 src/main/java/com/example/demo/exception/ResourceNotFoundException.java create mode 100644 src/main/java/com/example/demo/model/PetDto.java create mode 100644 src/main/java/com/example/demo/model/UserDto.java create mode 100644 src/main/java/com/example/demo/service/PetService.java create mode 100644 src/main/java/com/example/demo/service/UserService.java create mode 100644 src/main/java/com/example/demo/service/inter/IPetService.java create mode 100644 src/main/java/com/example/demo/service/inter/IUserService.java diff --git a/pom.xml b/pom.xml index 19b82d9..1397d3b 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,11 @@ spring-boot-starter-test test + + org.projectlombok + lombok + true + diff --git a/src/main/java/com/example/demo/controller/UserController.java b/src/main/java/com/example/demo/controller/UserController.java index 8df2b36..c212b5b 100644 --- a/src/main/java/com/example/demo/controller/UserController.java +++ b/src/main/java/com/example/demo/controller/UserController.java @@ -1,25 +1,45 @@ package com.example.demo.controller; +import com.example.demo.model.UserDto; +import com.example.demo.service.UserService; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController +@Slf4j +@RequiredArgsConstructor @RequestMapping("/users") public class UserController { private final UserService userService; - private final UserDtoMapper userDtoMapper; + //private final UserDtoMapper userDtoMapper; - @PostMapping - public ResponseEntity createUser(@Valid @RequestBody UserDto userDto) { - var user = userDtoMapper.map(userDto); - return new ResponseEntity<>(userService.createUser(user), HttpStatus.CREATED); + @GetMapping(path = "/{id}") + public ResponseEntity getUser(@PathVariable long id) { + //var user = userDtoMapper.map(userDto); + return ResponseEntity.status(HttpStatus.OK).body(userService.getUser(id)); + } + + @PostMapping(path = "/add") + public ResponseEntity createUser(@Valid @RequestBody UserDto userDto) { + //var user = userDtoMapper.map(userDto); + return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(userDto)); + } + + @PutMapping(path = "/update") + public ResponseEntity updateUser(@Valid @RequestBody UserDto userDto) { + //var user = userDtoMapper.map(userDto); + return ResponseEntity.status(HttpStatus.OK).body(userService.updateUser(userDto)); + } + + @DeleteMapping(path = "/delete/{id}") + public ResponseEntity deleteUser(@PathVariable long id) { + return ResponseEntity.status(HttpStatus.NO_CONTENT).body(userService.deleteUser(id)); } } diff --git a/src/main/java/com/example/demo/exception/ErrorMessageResponse.java b/src/main/java/com/example/demo/exception/ErrorMessageResponse.java new file mode 100644 index 0000000..6c17f16 --- /dev/null +++ b/src/main/java/com/example/demo/exception/ErrorMessageResponse.java @@ -0,0 +1,20 @@ +package com.example.demo.exception; + +import lombok.Getter; +import java.time.Instant; + + +@Getter +public class ErrorMessageResponse { + + private final String error; + private final Instant timestamp; + private final String operation; + + public ErrorMessageResponse(String error, String operation) { + this.error = error; + this.timestamp = Instant.now(); + this.operation = operation; + } + +} diff --git a/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java b/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java index 8c8ac8e..fa15d09 100644 --- a/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java @@ -1,6 +1,13 @@ package com.example.demo.exception; -@ControllerAdvice +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + + +@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) diff --git a/src/main/java/com/example/demo/exception/ResourceNotFoundException.java b/src/main/java/com/example/demo/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..77ac55b --- /dev/null +++ b/src/main/java/com/example/demo/exception/ResourceNotFoundException.java @@ -0,0 +1,21 @@ +package com.example.demo.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + + +@Getter +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException { + + private final Long resourceId; + private final String operation; + + public ResourceNotFoundException(Long resourceId, String message, String operation) { + super(message); + this.resourceId = resourceId; + this.operation = operation; + } + +} diff --git a/src/main/java/com/example/demo/model/PetDto.java b/src/main/java/com/example/demo/model/PetDto.java new file mode 100644 index 0000000..a838447 --- /dev/null +++ b/src/main/java/com/example/demo/model/PetDto.java @@ -0,0 +1,13 @@ +package com.example.demo.model; + +import lombok.Getter; +import lombok.Setter; + + +@Getter +@Setter +public class PetDto { + private Long id; + private String name; + private Long userId; +} diff --git a/src/main/java/com/example/demo/model/UserDto.java b/src/main/java/com/example/demo/model/UserDto.java new file mode 100644 index 0000000..867a7b6 --- /dev/null +++ b/src/main/java/com/example/demo/model/UserDto.java @@ -0,0 +1,18 @@ +package com.example.demo.model; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class UserDto { + private Long id; + private String name; + private String email; + private Integer age; + private List pets; +} + +//public record UserDto(Long id, String name, String email, Integer age, List pets) {} diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java new file mode 100644 index 0000000..260f4a2 --- /dev/null +++ b/src/main/java/com/example/demo/service/PetService.java @@ -0,0 +1,78 @@ +package com.example.demo.service; + +import com.example.demo.exception.ResourceNotFoundException; +import com.example.demo.model.PetDto; +import com.example.demo.model.UserDto; +import com.example.demo.service.inter.IPetService; +import org.springframework.stereotype.Service; + +import java.util.*; + + +@Service +public class PetService implements IPetService { + + private final UserService userService; + + public PetService(UserService userService) { + this.userService = userService; + } + + @Override + public PetDto getPet(Long id) { + final Set petSet = new HashSet<>(); + this.userService.getUserMap().values().stream() + .map(UserDto::getPets) + .forEach(petSet::addAll); + + return petSet.stream() + .filter(p -> (long)p.getId() == id) + .findFirst() + .orElseThrow(() -> new ResourceNotFoundException(id, "Pet not found", "Get pet")); + } + + @Override + public PetDto createPet(PetDto pet) { + UserDto user = this.userService.getUserMap() + .values() + .stream() + .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) + .findFirst() + .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Create pet")); + pet.setId((long) (user.getPets().size() + 1)); + user.getPets().add(pet); + + return pet; + } + + @Override + public Long updatePet(PetDto pet) { + this.userService.getUserMap() + .values() + .stream() + .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) + .findFirst() + .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Update pet")); + + PetDto petDto = this.getPet(pet.getId()); + petDto.setName(pet.getName()); + petDto.setUserId(pet.getUserId()); + + return pet.getId(); + } + + @Override + public Long deletePet(Long id) { + PetDto petDto = this.userService.getUserMap() + .values() + .forEach(user -> { + List pets = user.getPets(); + for (PetDto pet : pets) { + if (id.compareTo(pet.getId()) == 0) { + return pet; + } + } + }); + } + +} diff --git a/src/main/java/com/example/demo/service/UserService.java b/src/main/java/com/example/demo/service/UserService.java new file mode 100644 index 0000000..84b1fc1 --- /dev/null +++ b/src/main/java/com/example/demo/service/UserService.java @@ -0,0 +1,56 @@ +package com.example.demo.service; + +import com.example.demo.exception.ResourceNotFoundException; +import com.example.demo.model.UserDto; +import com.example.demo.service.inter.IUserService; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + + +@Service +public class UserService implements IUserService { + + private final Map userMap;// = new HashMap<>(); + + public UserService() { + this.userMap = new HashMap<>(); + } + + @Override + public UserDto getUser(Long id) { + return this.userMap.get(id); + } + + @Override + public UserDto createUser(UserDto dto) { + Long key = (long) (this.userMap.size() + 1); + return this.userMap.put(key, dto); + } + + @Override + public Long updateUser(UserDto dto) { + UserDto user = Optional.ofNullable(this.userMap.get(dto.getId())) + .orElseThrow(() -> new ResourceNotFoundException(dto.getId(), "User not found", "PUT")); + user.setName(dto.getName()); + user.setAge(dto.getAge()); + user.setEmail(dto.getEmail()); + user.setPets(dto.getPets()); + + return user.getId(); + } + + @Override + public Long deleteUser(Long id) { + UserDto user = Optional.ofNullable(this.userMap.get(id)) + .orElseThrow(() -> new ResourceNotFoundException(id, "User not found", "DELETE")); + return this.userMap.remove(user.getId()).getId(); + } + + public Map getUserMap() { + return this.userMap; + } + +} diff --git a/src/main/java/com/example/demo/service/inter/IPetService.java b/src/main/java/com/example/demo/service/inter/IPetService.java new file mode 100644 index 0000000..8a207ef --- /dev/null +++ b/src/main/java/com/example/demo/service/inter/IPetService.java @@ -0,0 +1,15 @@ +package com.example.demo.service.inter; + +import com.example.demo.model.PetDto; + + +public interface IPetService { + + PetDto getPet(Long id); + + PetDto createPet(PetDto dto); + + Long updatePet(PetDto dto); + + Long deletePet(Long id); +} diff --git a/src/main/java/com/example/demo/service/inter/IUserService.java b/src/main/java/com/example/demo/service/inter/IUserService.java new file mode 100644 index 0000000..eb26d76 --- /dev/null +++ b/src/main/java/com/example/demo/service/inter/IUserService.java @@ -0,0 +1,15 @@ +package com.example.demo.service.inter; + +import com.example.demo.model.UserDto; + + +public interface IUserService { + + UserDto getUser(Long id); + + UserDto createUser(UserDto dto); + + Long updateUser(UserDto dto); + + Long deleteUser(Long id); +} diff --git a/src/test/java/com/example/demo/UserServiceTest.java b/src/test/java/com/example/demo/UserServiceTest.java index 804e21e..c71c9dd 100644 --- a/src/test/java/com/example/demo/UserServiceTest.java +++ b/src/test/java/com/example/demo/UserServiceTest.java @@ -1,47 +1,47 @@ -package com.example.demo; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.servlet.MockMvc; - -import java.util.List; - - -@AutoConfigureMockMvc -@SpringBootTest -class UserServiceTest { - - @Autowired - private UserService userService; - - @Autowired - private MockMvc mockMvc; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Test - public void shouldCreateNewUser() throws Exception { - - var userDto = new UserDto(null, "Pavel", "test@example.com", 25, List.of()); - String newUserJson = objectMapper.writeValueAsString(userDto); - - var jsonResponse = mockMvc.perform(post("/users") - .contentType(MediaType.APPLICATION_JSON) - .content(newUserJson)) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - - var userDtoResponse = objectMapper.readValue(jsonResponse, UserDto.class); - - Assertions.assertEquals(userDto.name(), userDtoResponse.name()); - Assertions.assertEquals(userDto.age(), userDtoResponse.age()); - Assertions.assertEquals(userDto.email(), userDtoResponse.email()); - Assertions.assertEquals(userDto.pets(), userDtoResponse.pets()); - Assertions.assertDoesNotThrow(() -> userService.getUser(userDtoResponse.id())); - } - -} +//package com.example.demo; +// +//import org.junit.jupiter.api.Assertions; +//import org.junit.jupiter.api.Test; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.test.web.servlet.MockMvc; +// +//import java.util.List; +// +// +//@AutoConfigureMockMvc +//@SpringBootTest +//class UserServiceTest { +// +// @Autowired +// private UserService userService; +// +// @Autowired +// private MockMvc mockMvc; +// +// private final ObjectMapper objectMapper = new ObjectMapper(); +// +// @Test +// public void shouldCreateNewUser() throws Exception { +// +// var userDto = new UserDto(null, "Pavel", "test@example.com", 25, List.of()); +// String newUserJson = objectMapper.writeValueAsString(userDto); +// +// var jsonResponse = mockMvc.perform(post("/users") +// .contentType(MediaType.APPLICATION_JSON) +// .content(newUserJson)) +// .andExpect(status().isOk()) +// .andReturn() +// .getResponse() +// .getContentAsString(); +// +// var userDtoResponse = objectMapper.readValue(jsonResponse, UserDto.class); +// +// Assertions.assertEquals(userDto.name(), userDtoResponse.name()); +// Assertions.assertEquals(userDto.age(), userDtoResponse.age()); +// Assertions.assertEquals(userDto.email(), userDtoResponse.email()); +// Assertions.assertEquals(userDto.pets(), userDtoResponse.pets()); +// Assertions.assertDoesNotThrow(() -> userService.getUser(userDtoResponse.id())); +// } +// +//} From 3d83760939076be4a1bf98c730af320fd69f3480 Mon Sep 17 00:00:00 2001 From: Alexey Kryuchkov Date: Thu, 5 Feb 2026 17:16:55 +0300 Subject: [PATCH 04/10] implemented delete method --- .../com/example/demo/service/PetService.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java index 260f4a2..603e9b9 100644 --- a/src/main/java/com/example/demo/service/PetService.java +++ b/src/main/java/com/example/demo/service/PetService.java @@ -63,16 +63,26 @@ public Long updatePet(PetDto pet) { @Override public Long deletePet(Long id) { - PetDto petDto = this.userService.getUserMap() + final Set petIds = new HashSet<>(); + this.userService.getUserMap().values().stream() + .map(UserDto::getPets) + .forEach(pets -> { + for (PetDto pet : pets) { + petIds.add(pet.getId()); + } + }); + + if (!petIds.contains(id)) { + throw new ResourceNotFoundException(id, "Pet not found", "Delete pet"); + } + + this.userService.getUserMap() .values() .forEach(user -> { List pets = user.getPets(); - for (PetDto pet : pets) { - if (id.compareTo(pet.getId()) == 0) { - return pet; - } - } + pets.removeIf(pet -> id.compareTo(pet.getId()) == 0); }); + return id; } } From 7575f2d4bfe2a0116e3d1014a4b7473b5c908318 Mon Sep 17 00:00:00 2001 From: alexk11 Date: Thu, 5 Feb 2026 23:17:28 +0300 Subject: [PATCH 05/10] implemented service and exception methods --- .../demo/controller/PetController.java | 44 +++++++++++++++++++ .../demo/exception/BadRequestException.java | 21 +++++++++ .../exception/GlobalExceptionHandler.java | 24 +++++++--- .../java/com/example/demo/model/PetDto.java | 5 +++ .../java/com/example/demo/model/UserDto.java | 8 ++++ .../com/example/demo/service/PetService.java | 40 +++++++++++------ .../com/example/demo/service/UserService.java | 18 +++++--- 7 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/example/demo/exception/BadRequestException.java diff --git a/src/main/java/com/example/demo/controller/PetController.java b/src/main/java/com/example/demo/controller/PetController.java index 5a45792..97865f2 100644 --- a/src/main/java/com/example/demo/controller/PetController.java +++ b/src/main/java/com/example/demo/controller/PetController.java @@ -1,4 +1,48 @@ package com.example.demo.controller; +import com.example.demo.model.PetDto; +import com.example.demo.model.UserDto; +import com.example.demo.service.PetService; +import com.example.demo.service.UserService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/pets") public class PetController { + + private final UserService userService; + private final PetService petService; + //private final UserDtoMapper userDtoMapper; + + @GetMapping(path = "/{id}") + public ResponseEntity getUser(@PathVariable long id) { + //var user = userDtoMapper.map(userDto); + return ResponseEntity.status(HttpStatus.OK).body(petService.getPet(id)); + } + + @PostMapping(path = "/add") + public ResponseEntity createUser(@Valid @RequestBody PetDto petDto) { + //var user = userDtoMapper.map(userDto); + return ResponseEntity.status(HttpStatus.CREATED).body(petService.createPet(petDto)); + } + + @PutMapping(path = "/update") + public ResponseEntity updatePet(@Valid @RequestBody PetDto petDto) { + //var user = userDtoMapper.map(userDto); + return ResponseEntity.status(HttpStatus.OK).body(petService.updatePet(petDto)); + } + + @DeleteMapping(path = "/delete/{id}") + public ResponseEntity deletePet(@PathVariable long id) { + return ResponseEntity.status(HttpStatus.NO_CONTENT).body(petService.deletePet(id)); + } + } diff --git a/src/main/java/com/example/demo/exception/BadRequestException.java b/src/main/java/com/example/demo/exception/BadRequestException.java new file mode 100644 index 0000000..0a3e63a --- /dev/null +++ b/src/main/java/com/example/demo/exception/BadRequestException.java @@ -0,0 +1,21 @@ +package com.example.demo.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + + +@Getter +@ResponseStatus(HttpStatus.BAD_REQUEST) +public class BadRequestException extends RuntimeException { + + private final Long resourceId; + private final String operation; + + public BadRequestException(Long resourceId, String message, String operation) { + super(message); + this.resourceId = resourceId; + this.operation = operation; + } + +} diff --git a/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java b/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java index fa15d09..eecec95 100644 --- a/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -11,19 +12,30 @@ public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) { - var error = new ErrorMessageResponse("Неверный аргумент", ex.getMessage()); - return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); + public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) { + var error = new ErrorMessageResponse("Неверный аргумент: " + ex.getMessage(), "Controller input"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex) { - // Обработка ошибки + var error = new ErrorMessageResponse(ex.getMessage(), ex.getOperation()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); + } + + @ExceptionHandler(BadRequestException.class) + public ResponseEntity handleBadRequestException(BadRequestException ex) { + var error = new ErrorMessageResponse(ex.getMessage(), ex.getOperation()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } @ExceptionHandler(Exception.class) - public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) { - // Обработка ошибки + public ResponseEntity handleException(Exception ex) { + var error = new ErrorMessageResponse(ex.getMessage(), "Generic"); + if (ex instanceof HttpMessageNotReadableException) { + error = new ErrorMessageResponse(ex.getMessage(), "Controller input"); + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } } diff --git a/src/main/java/com/example/demo/model/PetDto.java b/src/main/java/com/example/demo/model/PetDto.java index a838447..2c7d70d 100644 --- a/src/main/java/com/example/demo/model/PetDto.java +++ b/src/main/java/com/example/demo/model/PetDto.java @@ -1,5 +1,7 @@ package com.example.demo.model; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import lombok.Getter; import lombok.Setter; @@ -7,7 +9,10 @@ @Getter @Setter public class PetDto { + @NotNull private Long id; + @NotNull private String name; + @Positive private Long userId; } diff --git a/src/main/java/com/example/demo/model/UserDto.java b/src/main/java/com/example/demo/model/UserDto.java index 867a7b6..ac26e51 100644 --- a/src/main/java/com/example/demo/model/UserDto.java +++ b/src/main/java/com/example/demo/model/UserDto.java @@ -1,5 +1,8 @@ package com.example.demo.model; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import lombok.Getter; import lombok.Setter; @@ -8,10 +11,15 @@ @Getter @Setter public class UserDto { + @NotNull private Long id; + @NotNull private String name; + @Email private String email; + @Positive private Integer age; + @NotNull private List pets; } diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java index 603e9b9..0516587 100644 --- a/src/main/java/com/example/demo/service/PetService.java +++ b/src/main/java/com/example/demo/service/PetService.java @@ -20,12 +20,8 @@ public PetService(UserService userService) { @Override public PetDto getPet(Long id) { - final Set petSet = new HashSet<>(); - this.userService.getUserMap().values().stream() - .map(UserDto::getPets) - .forEach(petSet::addAll); - - return petSet.stream() + return this.getAllPets() + .stream() .filter(p -> (long)p.getId() == id) .findFirst() .orElseThrow(() -> new ResourceNotFoundException(id, "Pet not found", "Get pet")); @@ -39,7 +35,7 @@ public PetDto createPet(PetDto pet) { .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) .findFirst() .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Create pet")); - pet.setId((long) (user.getPets().size() + 1)); + pet.setId((long) (this.getAllPets().size() + 1)); user.getPets().add(pet); return pet; @@ -47,12 +43,13 @@ public PetDto createPet(PetDto pet) { @Override public Long updatePet(PetDto pet) { - this.userService.getUserMap() - .values() - .stream() - .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) - .findFirst() - .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Update pet")); +// this.userService.getUserMap() +// .values() +// .stream() +// .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) +// .findFirst() +// .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Update pet")); + UserDto userDto = this.getUserById(pet.getUserId(), "Update pet"); PetDto petDto = this.getPet(pet.getId()); petDto.setName(pet.getName()); @@ -85,4 +82,21 @@ public Long deletePet(Long id) { return id; } + private Set getAllPets() { + final Set petSet = new HashSet<>(); + this.userService.getUserMap().values().stream() + .map(UserDto::getPets) + .forEach(petSet::addAll); + return petSet; + } + + private UserDto getUserById(Long id, String operation) { + return this.userService.getUserMap() + .values() + .stream() + .filter(u -> u.getId().compareTo(id) == 0) + .findFirst() + .orElseThrow(() -> new ResourceNotFoundException(id, "User not found", operation)); + } + } diff --git a/src/main/java/com/example/demo/service/UserService.java b/src/main/java/com/example/demo/service/UserService.java index 84b1fc1..cb96b2a 100644 --- a/src/main/java/com/example/demo/service/UserService.java +++ b/src/main/java/com/example/demo/service/UserService.java @@ -1,5 +1,6 @@ package com.example.demo.service; +import com.example.demo.exception.BadRequestException; import com.example.demo.exception.ResourceNotFoundException; import com.example.demo.model.UserDto; import com.example.demo.service.inter.IUserService; @@ -13,7 +14,7 @@ @Service public class UserService implements IUserService { - private final Map userMap;// = new HashMap<>(); + private final Map userMap; public UserService() { this.userMap = new HashMap<>(); @@ -21,19 +22,24 @@ public UserService() { @Override public UserDto getUser(Long id) { - return this.userMap.get(id); + return Optional.ofNullable(this.userMap.get(id)) + .orElseThrow(() -> new ResourceNotFoundException(id, "User not found", "Get user")); } @Override public UserDto createUser(UserDto dto) { - Long key = (long) (this.userMap.size() + 1); - return this.userMap.put(key, dto); + if (this.userMap.get(dto.getId()) != null) { + throw new BadRequestException(dto.getId(), "User already exists", "Create user"); + } + //Long key = (long) (this.userMap.size() + 1); + this.userMap.put(dto.getId(), dto); + return dto; } @Override public Long updateUser(UserDto dto) { UserDto user = Optional.ofNullable(this.userMap.get(dto.getId())) - .orElseThrow(() -> new ResourceNotFoundException(dto.getId(), "User not found", "PUT")); + .orElseThrow(() -> new ResourceNotFoundException(dto.getId(), "User not found", "Update user")); user.setName(dto.getName()); user.setAge(dto.getAge()); user.setEmail(dto.getEmail()); @@ -45,7 +51,7 @@ public Long updateUser(UserDto dto) { @Override public Long deleteUser(Long id) { UserDto user = Optional.ofNullable(this.userMap.get(id)) - .orElseThrow(() -> new ResourceNotFoundException(id, "User not found", "DELETE")); + .orElseThrow(() -> new ResourceNotFoundException(id, "User not found", "Delete user")); return this.userMap.remove(user.getId()).getId(); } From d9816e2ec70c20b6f76ab37f000c0ca67ca46181 Mon Sep 17 00:00:00 2001 From: Alexey Kryuchkov Date: Fri, 6 Feb 2026 14:17:02 +0300 Subject: [PATCH 06/10] implemented pet update method --- .../demo/controller/PetController.java | 9 +-- .../demo/controller/UserController.java | 4 - .../com/example/demo/service/PetService.java | 73 +++++++++---------- .../demo/service/inter/IPetService.java | 4 +- .../demo/service/inter/IUserService.java | 4 +- 5 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/main/java/com/example/demo/controller/PetController.java b/src/main/java/com/example/demo/controller/PetController.java index 97865f2..383e9a5 100644 --- a/src/main/java/com/example/demo/controller/PetController.java +++ b/src/main/java/com/example/demo/controller/PetController.java @@ -18,25 +18,20 @@ @RequestMapping("/pets") public class PetController { - private final UserService userService; private final PetService petService; - //private final UserDtoMapper userDtoMapper; @GetMapping(path = "/{id}") - public ResponseEntity getUser(@PathVariable long id) { - //var user = userDtoMapper.map(userDto); + public ResponseEntity getPet(@PathVariable long id) { return ResponseEntity.status(HttpStatus.OK).body(petService.getPet(id)); } @PostMapping(path = "/add") - public ResponseEntity createUser(@Valid @RequestBody PetDto petDto) { - //var user = userDtoMapper.map(userDto); + public ResponseEntity createPet(@Valid @RequestBody PetDto petDto) { return ResponseEntity.status(HttpStatus.CREATED).body(petService.createPet(petDto)); } @PutMapping(path = "/update") public ResponseEntity updatePet(@Valid @RequestBody PetDto petDto) { - //var user = userDtoMapper.map(userDto); return ResponseEntity.status(HttpStatus.OK).body(petService.updatePet(petDto)); } diff --git a/src/main/java/com/example/demo/controller/UserController.java b/src/main/java/com/example/demo/controller/UserController.java index c212b5b..cedc7f7 100644 --- a/src/main/java/com/example/demo/controller/UserController.java +++ b/src/main/java/com/example/demo/controller/UserController.java @@ -17,23 +17,19 @@ public class UserController { private final UserService userService; - //private final UserDtoMapper userDtoMapper; @GetMapping(path = "/{id}") public ResponseEntity getUser(@PathVariable long id) { - //var user = userDtoMapper.map(userDto); return ResponseEntity.status(HttpStatus.OK).body(userService.getUser(id)); } @PostMapping(path = "/add") public ResponseEntity createUser(@Valid @RequestBody UserDto userDto) { - //var user = userDtoMapper.map(userDto); return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(userDto)); } @PutMapping(path = "/update") public ResponseEntity updateUser(@Valid @RequestBody UserDto userDto) { - //var user = userDtoMapper.map(userDto); return ResponseEntity.status(HttpStatus.OK).body(userService.updateUser(userDto)); } diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java index 0516587..76700d7 100644 --- a/src/main/java/com/example/demo/service/PetService.java +++ b/src/main/java/com/example/demo/service/PetService.java @@ -24,7 +24,7 @@ public PetDto getPet(Long id) { .stream() .filter(p -> (long)p.getId() == id) .findFirst() - .orElseThrow(() -> new ResourceNotFoundException(id, "Pet not found", "Get pet")); + .orElseThrow(() -> new ResourceNotFoundException(id, "Pet with not found", "Get pet")); } @Override @@ -37,51 +37,59 @@ public PetDto createPet(PetDto pet) { .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Create pet")); pet.setId((long) (this.getAllPets().size() + 1)); user.getPets().add(pet); - return pet; } @Override public Long updatePet(PetDto pet) { -// this.userService.getUserMap() -// .values() -// .stream() -// .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) -// .findFirst() -// .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Update pet")); - UserDto userDto = this.getUserById(pet.getUserId(), "Update pet"); - - PetDto petDto = this.getPet(pet.getId()); - petDto.setName(pet.getName()); - petDto.setUserId(pet.getUserId()); - - return pet.getId(); + PetDto foundPet = this.getPet(pet.getId()); + // pet owner has changed + if (foundPet.getUserId().compareTo(pet.getUserId()) != 0) { + // remove pet from the previous owner + this.userService.getUserMap().values().stream() + .filter(u -> u.getId().compareTo(foundPet.getUserId()) == 0) + .findFirst() + .map(u -> u.getPets().remove(foundPet)) + .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "Current pet owner not found", "Update pet")); + // add pet to the new pet owner + foundPet.setName(pet.getName()); + foundPet.setUserId(pet.getUserId()); + this.userService.getUserMap().values().stream() + .filter(u -> u.getId().compareTo(foundPet.getUserId()) == 0) + .findFirst() + .map(u -> u.getPets().add(foundPet)) + .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "New pet owner not found", "Update pet")); + } + return foundPet.getId(); } @Override public Long deletePet(Long id) { + // collect all pet id final Set petIds = new HashSet<>(); this.userService.getUserMap().values().stream() - .map(UserDto::getPets) - .forEach(pets -> { - for (PetDto pet : pets) { - petIds.add(pet.getId()); - } - }); - + .map(UserDto::getPets) + .forEach(pets -> { + for (PetDto pet : pets) { + petIds.add(pet.getId()); + } + }); + // raise exception if the pet with id doesn't exist if (!petIds.contains(id)) { throw new ResourceNotFoundException(id, "Pet not found", "Delete pet"); } - + // iterate users and remove the pet this.userService.getUserMap() - .values() - .forEach(user -> { - List pets = user.getPets(); - pets.removeIf(pet -> id.compareTo(pet.getId()) == 0); - }); + .values() + .forEach(user -> { + user.getPets().removeIf(pet -> id.compareTo(pet.getId()) == 0); + }); return id; } + /** + * Get all Pets from all Users + */ private Set getAllPets() { final Set petSet = new HashSet<>(); this.userService.getUserMap().values().stream() @@ -90,13 +98,4 @@ private Set getAllPets() { return petSet; } - private UserDto getUserById(Long id, String operation) { - return this.userService.getUserMap() - .values() - .stream() - .filter(u -> u.getId().compareTo(id) == 0) - .findFirst() - .orElseThrow(() -> new ResourceNotFoundException(id, "User not found", operation)); - } - } diff --git a/src/main/java/com/example/demo/service/inter/IPetService.java b/src/main/java/com/example/demo/service/inter/IPetService.java index 8a207ef..6fec18f 100644 --- a/src/main/java/com/example/demo/service/inter/IPetService.java +++ b/src/main/java/com/example/demo/service/inter/IPetService.java @@ -5,10 +5,10 @@ public interface IPetService { - PetDto getPet(Long id); - PetDto createPet(PetDto dto); + PetDto getPet(Long id); + Long updatePet(PetDto dto); Long deletePet(Long id); diff --git a/src/main/java/com/example/demo/service/inter/IUserService.java b/src/main/java/com/example/demo/service/inter/IUserService.java index eb26d76..81c17ac 100644 --- a/src/main/java/com/example/demo/service/inter/IUserService.java +++ b/src/main/java/com/example/demo/service/inter/IUserService.java @@ -5,10 +5,10 @@ public interface IUserService { - UserDto getUser(Long id); - UserDto createUser(UserDto dto); + UserDto getUser(Long id); + Long updateUser(UserDto dto); Long deleteUser(Long id); From 942af022216e18cf623c40b7a1c31b98982fce56 Mon Sep 17 00:00:00 2001 From: Alexey Kryuchkov Date: Fri, 6 Feb 2026 16:51:57 +0300 Subject: [PATCH 07/10] added mockmvc tests --- pom.xml | 15 +- .../java/com/example/demo/model/UserDto.java | 5 + .../com/example/demo/service/PetService.java | 4 +- .../example/demo/DemoApplicationTests.java | 13 -- .../com/example/demo/UserServiceTest.java | 141 ++++++++++++------ 5 files changed, 114 insertions(+), 64 deletions(-) delete mode 100644 src/test/java/com/example/demo/DemoApplicationTests.java diff --git a/pom.xml b/pom.xml index 1397d3b..16706ec 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 4.0.3-SNAPSHOT + 3.5.9 @@ -36,7 +36,6 @@ org.springframework.boot spring-boot-devtools - 4.0.2 compile @@ -44,11 +43,23 @@ spring-boot-starter-test test + org.projectlombok lombok true + + com.fasterxml.jackson.core + jackson-databind + 2.20.1 + compile + diff --git a/src/main/java/com/example/demo/model/UserDto.java b/src/main/java/com/example/demo/model/UserDto.java index ac26e51..89738bc 100644 --- a/src/main/java/com/example/demo/model/UserDto.java +++ b/src/main/java/com/example/demo/model/UserDto.java @@ -3,13 +3,18 @@ import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; import java.util.List; + @Getter @Setter +@RequiredArgsConstructor +@AllArgsConstructor public class UserDto { @NotNull private Long id; diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java index 76700d7..337d22b 100644 --- a/src/main/java/com/example/demo/service/PetService.java +++ b/src/main/java/com/example/demo/service/PetService.java @@ -24,7 +24,7 @@ public PetDto getPet(Long id) { .stream() .filter(p -> (long)p.getId() == id) .findFirst() - .orElseThrow(() -> new ResourceNotFoundException(id, "Pet with not found", "Get pet")); + .orElseThrow(() -> new ResourceNotFoundException(id, "Pet not found", "Get pet")); } @Override @@ -51,7 +51,7 @@ public Long updatePet(PetDto pet) { .findFirst() .map(u -> u.getPets().remove(foundPet)) .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "Current pet owner not found", "Update pet")); - // add pet to the new pet owner + // add pet to the new owner foundPet.setName(pet.getName()); foundPet.setUserId(pet.getUserId()); this.userService.getUserMap().values().stream() diff --git a/src/test/java/com/example/demo/DemoApplicationTests.java b/src/test/java/com/example/demo/DemoApplicationTests.java deleted file mode 100644 index 2778a6a..0000000 --- a/src/test/java/com/example/demo/DemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class DemoApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/com/example/demo/UserServiceTest.java b/src/test/java/com/example/demo/UserServiceTest.java index c71c9dd..09cc9c2 100644 --- a/src/test/java/com/example/demo/UserServiceTest.java +++ b/src/test/java/com/example/demo/UserServiceTest.java @@ -1,47 +1,94 @@ -//package com.example.demo; -// -//import org.junit.jupiter.api.Assertions; -//import org.junit.jupiter.api.Test; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.test.context.SpringBootTest; -//import org.springframework.test.web.servlet.MockMvc; -// -//import java.util.List; -// -// -//@AutoConfigureMockMvc -//@SpringBootTest -//class UserServiceTest { -// -// @Autowired -// private UserService userService; -// -// @Autowired -// private MockMvc mockMvc; -// -// private final ObjectMapper objectMapper = new ObjectMapper(); -// -// @Test -// public void shouldCreateNewUser() throws Exception { -// -// var userDto = new UserDto(null, "Pavel", "test@example.com", 25, List.of()); -// String newUserJson = objectMapper.writeValueAsString(userDto); -// -// var jsonResponse = mockMvc.perform(post("/users") -// .contentType(MediaType.APPLICATION_JSON) -// .content(newUserJson)) -// .andExpect(status().isOk()) -// .andReturn() -// .getResponse() -// .getContentAsString(); -// -// var userDtoResponse = objectMapper.readValue(jsonResponse, UserDto.class); -// -// Assertions.assertEquals(userDto.name(), userDtoResponse.name()); -// Assertions.assertEquals(userDto.age(), userDtoResponse.age()); -// Assertions.assertEquals(userDto.email(), userDtoResponse.email()); -// Assertions.assertEquals(userDto.pets(), userDtoResponse.pets()); -// Assertions.assertDoesNotThrow(() -> userService.getUser(userDtoResponse.id())); -// } -// -//} +package com.example.demo; + +import com.example.demo.model.UserDto; +import com.example.demo.service.UserService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@AutoConfigureMockMvc +@SpringBootTest +class UserServiceTest { + + @Autowired + private UserService userService; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void shouldCreateNewUser() throws Exception { + var userDto = new UserDto(0L, + "Pavel", + "test@example.com", + 25, + List.of()); + + String newUserJson = objectMapper.writeValueAsString(userDto); + + var jsonResponse = mockMvc.perform(post("/users/add") + .contentType(MediaType.APPLICATION_JSON) + .content(newUserJson)) + .andExpect(status().isCreated()) + .andReturn() + .getResponse() + .getContentAsString(); + + var userDtoResponse = objectMapper.readValue(jsonResponse, UserDto.class); + + Assertions.assertEquals(userDto.getId(), userDtoResponse.getId()); + Assertions.assertEquals(userDto.getName(), userDtoResponse.getName()); + Assertions.assertEquals(userDto.getAge(), userDtoResponse.getAge()); + Assertions.assertEquals(userDto.getEmail(), userDtoResponse.getEmail()); + Assertions.assertEquals(userDto.getPets(), userDtoResponse.getPets()); + Assertions.assertDoesNotThrow(() -> userService.getUser(userDtoResponse.getId())); + } + + @Test + public void shouldGetUserById() throws Exception { + + var userDto = new UserDto(1L, + "Alex", + "test@example.com", + 35, + List.of()); + + String userJson = objectMapper.writeValueAsString(userDto); + + when(userService.getUser(anyLong())).thenReturn(userDto); + + var jsonResponse = mockMvc.perform(get("/users/1") + .contentType(MediaType.APPLICATION_JSON) + .content(userJson)) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + var userDtoResponse = objectMapper.readValue(jsonResponse, UserDto.class); + + Assertions.assertEquals(userDto.getId(), userDtoResponse.getId()); + Assertions.assertEquals(userDto.getName(), userDtoResponse.getName()); + Assertions.assertEquals(userDto.getAge(), userDtoResponse.getAge()); + Assertions.assertEquals(userDto.getEmail(), userDtoResponse.getEmail()); + Assertions.assertEquals(userDto.getPets(), userDtoResponse.getPets()); + Assertions.assertDoesNotThrow(() -> userService.getUser(userDtoResponse.getId())); + } + +} From aabc9cfafe724974131096d69e255284f77cb172 Mon Sep 17 00:00:00 2001 From: alexk11 Date: Fri, 6 Feb 2026 20:33:38 +0300 Subject: [PATCH 08/10] updated service and test --- .../example/demo/controller/UserController.java | 2 +- .../com/example/demo/service/PetService.java | 7 +++++-- .../com/example/demo/service/UserService.java | 5 ++--- .../example/demo/service/inter/IUserService.java | 2 +- .../java/com/example/demo/UserServiceTest.java | 16 ++++++---------- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/example/demo/controller/UserController.java b/src/main/java/com/example/demo/controller/UserController.java index cedc7f7..c2fb026 100644 --- a/src/main/java/com/example/demo/controller/UserController.java +++ b/src/main/java/com/example/demo/controller/UserController.java @@ -29,7 +29,7 @@ public ResponseEntity createUser(@Valid @RequestBody UserDto userDto) { } @PutMapping(path = "/update") - public ResponseEntity updateUser(@Valid @RequestBody UserDto userDto) { + public ResponseEntity updateUser(@Valid @RequestBody UserDto userDto) { return ResponseEntity.status(HttpStatus.OK).body(userService.updateUser(userDto)); } diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java index 337d22b..9377601 100644 --- a/src/main/java/com/example/demo/service/PetService.java +++ b/src/main/java/com/example/demo/service/PetService.java @@ -45,13 +45,13 @@ public Long updatePet(PetDto pet) { PetDto foundPet = this.getPet(pet.getId()); // pet owner has changed if (foundPet.getUserId().compareTo(pet.getUserId()) != 0) { - // remove pet from the previous owner + // remove pet from previous owner this.userService.getUserMap().values().stream() .filter(u -> u.getId().compareTo(foundPet.getUserId()) == 0) .findFirst() .map(u -> u.getPets().remove(foundPet)) .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "Current pet owner not found", "Update pet")); - // add pet to the new owner + // add pet to new owner foundPet.setName(pet.getName()); foundPet.setUserId(pet.getUserId()); this.userService.getUserMap().values().stream() @@ -59,6 +59,9 @@ public Long updatePet(PetDto pet) { .findFirst() .map(u -> u.getPets().add(foundPet)) .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "New pet owner not found", "Update pet")); + } else { // pet owner has not changed + foundPet.setName(pet.getName()); + foundPet.setUserId(pet.getUserId()); } return foundPet.getId(); } diff --git a/src/main/java/com/example/demo/service/UserService.java b/src/main/java/com/example/demo/service/UserService.java index cb96b2a..6abef1c 100644 --- a/src/main/java/com/example/demo/service/UserService.java +++ b/src/main/java/com/example/demo/service/UserService.java @@ -31,13 +31,12 @@ public UserDto createUser(UserDto dto) { if (this.userMap.get(dto.getId()) != null) { throw new BadRequestException(dto.getId(), "User already exists", "Create user"); } - //Long key = (long) (this.userMap.size() + 1); this.userMap.put(dto.getId(), dto); return dto; } @Override - public Long updateUser(UserDto dto) { + public UserDto updateUser(UserDto dto) { UserDto user = Optional.ofNullable(this.userMap.get(dto.getId())) .orElseThrow(() -> new ResourceNotFoundException(dto.getId(), "User not found", "Update user")); user.setName(dto.getName()); @@ -45,7 +44,7 @@ public Long updateUser(UserDto dto) { user.setEmail(dto.getEmail()); user.setPets(dto.getPets()); - return user.getId(); + return user; } @Override diff --git a/src/main/java/com/example/demo/service/inter/IUserService.java b/src/main/java/com/example/demo/service/inter/IUserService.java index 81c17ac..294913b 100644 --- a/src/main/java/com/example/demo/service/inter/IUserService.java +++ b/src/main/java/com/example/demo/service/inter/IUserService.java @@ -9,7 +9,7 @@ public interface IUserService { UserDto getUser(Long id); - Long updateUser(UserDto dto); + UserDto updateUser(UserDto dto); Long deleteUser(Long id); } diff --git a/src/test/java/com/example/demo/UserServiceTest.java b/src/test/java/com/example/demo/UserServiceTest.java index 09cc9c2..ca12da1 100644 --- a/src/test/java/com/example/demo/UserServiceTest.java +++ b/src/test/java/com/example/demo/UserServiceTest.java @@ -6,18 +6,17 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import java.util.List; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @AutoConfigureMockMvc @@ -69,13 +68,10 @@ public void shouldGetUserById() throws Exception { 35, List.of()); - String userJson = objectMapper.writeValueAsString(userDto); - - when(userService.getUser(anyLong())).thenReturn(userDto); + this.userService.getUserMap().put(1L, userDto); - var jsonResponse = mockMvc.perform(get("/users/1") - .contentType(MediaType.APPLICATION_JSON) - .content(userJson)) + var jsonResponse = mockMvc.perform(get("/users/{id}", 1L)) + .andDo(print()) .andExpect(status().isOk()) .andReturn() .getResponse() From 0c5e28505cc9f5d5957a01d837195c3c590531f0 Mon Sep 17 00:00:00 2001 From: alexk11 Date: Sat, 7 Feb 2026 11:57:37 +0300 Subject: [PATCH 09/10] updated service and test --- .../java/com/example/demo/model/PetDto.java | 4 + .../com/example/demo/service/PetService.java | 8 +- .../java/com/example/demo/PetServiceTest.java | 102 ++++++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/example/demo/PetServiceTest.java diff --git a/src/main/java/com/example/demo/model/PetDto.java b/src/main/java/com/example/demo/model/PetDto.java index 2c7d70d..7d65423 100644 --- a/src/main/java/com/example/demo/model/PetDto.java +++ b/src/main/java/com/example/demo/model/PetDto.java @@ -2,12 +2,16 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; @Getter @Setter +@RequiredArgsConstructor +@AllArgsConstructor public class PetDto { @NotNull private Long id; diff --git a/src/main/java/com/example/demo/service/PetService.java b/src/main/java/com/example/demo/service/PetService.java index 9377601..e8abad5 100644 --- a/src/main/java/com/example/demo/service/PetService.java +++ b/src/main/java/com/example/demo/service/PetService.java @@ -35,7 +35,7 @@ public PetDto createPet(PetDto pet) { .filter(u -> u.getId().compareTo(pet.getUserId()) == 0) .findFirst() .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "User not found", "Create pet")); - pet.setId((long) (this.getAllPets().size() + 1)); + //pet.setId((long) (this.getAllPets().size() + 1)); user.getPets().add(pet); return pet; } @@ -59,7 +59,7 @@ public Long updatePet(PetDto pet) { .findFirst() .map(u -> u.getPets().add(foundPet)) .orElseThrow(() -> new ResourceNotFoundException(pet.getUserId(), "New pet owner not found", "Update pet")); - } else { // pet owner has not changed + } else { foundPet.setName(pet.getName()); foundPet.setUserId(pet.getUserId()); } @@ -84,9 +84,7 @@ public Long deletePet(Long id) { // iterate users and remove the pet this.userService.getUserMap() .values() - .forEach(user -> { - user.getPets().removeIf(pet -> id.compareTo(pet.getId()) == 0); - }); + .forEach(user -> user.getPets().removeIf(pet -> id.compareTo(pet.getId()) == 0)); return id; } diff --git a/src/test/java/com/example/demo/PetServiceTest.java b/src/test/java/com/example/demo/PetServiceTest.java new file mode 100644 index 0000000..d2e9a9d --- /dev/null +++ b/src/test/java/com/example/demo/PetServiceTest.java @@ -0,0 +1,102 @@ +package com.example.demo; + +import com.example.demo.model.PetDto; +import com.example.demo.model.UserDto; +import com.example.demo.service.PetService; +import com.example.demo.service.UserService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.ArrayList; +import java.util.List; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + + +@AutoConfigureMockMvc +@SpringBootTest +class PetServiceTest { + + @Autowired + private PetService petService; + + @Autowired + private UserService userService; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void shouldCreateNewPet() throws Exception { + var userDto = new UserDto(1L, + "Alex", + "test@example.com", + 35, + new ArrayList<>()); + + var petDto = new PetDto(0L, + "Jack", + 1L); + + this.userService.getUserMap().put(1L, userDto); + + String newPetJson = objectMapper.writeValueAsString(petDto); + + var jsonResponse = mockMvc.perform(post("/pets/add") + .contentType(MediaType.APPLICATION_JSON) + .content(newPetJson)) + .andExpect(status().isCreated()) + .andReturn() + .getResponse() + .getContentAsString(); + + var petDtoResponse = objectMapper.readValue(jsonResponse, PetDto.class); + + Assertions.assertEquals(petDto.getId(), petDtoResponse.getId()); + Assertions.assertEquals(petDto.getName(), petDtoResponse.getName()); + Assertions.assertEquals(petDto.getUserId(), petDtoResponse.getUserId()); + Assertions.assertDoesNotThrow(() -> petService.getPet(petDtoResponse.getId())); + } + + @Test + public void shouldGetPetById() throws Exception { + var petDto = new PetDto(1L, + "Jack", + 1L); + + var userDto = new UserDto(1L, + "Alex", + "test@example.com", + 35, + List.of(petDto)); + + this.userService.getUserMap().put(1L, userDto); + + var jsonResponse = mockMvc.perform(get("/pets/{id}", 1L)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + var petDtoResponse = objectMapper.readValue(jsonResponse, PetDto.class); + + Assertions.assertEquals(petDto.getId(), petDtoResponse.getId()); + Assertions.assertEquals(petDto.getName(), petDtoResponse.getName()); + Assertions.assertEquals(petDto.getUserId(), petDtoResponse.getUserId()); + Assertions.assertDoesNotThrow(() -> petService.getPet(petDtoResponse.getId())); + } + +} + From 4dfb9d98322c27d9db666eea8c9174ab0bf5d3a1 Mon Sep 17 00:00:00 2001 From: alexk11 Date: Sat, 7 Feb 2026 12:04:11 +0300 Subject: [PATCH 10/10] updated pom.xml --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 16706ec..a52e326 100644 --- a/pom.xml +++ b/pom.xml @@ -43,12 +43,6 @@ spring-boot-starter-test test - org.projectlombok lombok