From 78ecee4c9a3c3096ccef3a3c98ad811fdb71e8c2 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Tue, 11 Mar 2025 00:44:36 +0300 Subject: [PATCH 01/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20request=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/ItemRequestController.java | 45 ++++++++++++ .../shareit/request/ItemRequestService.java | 71 +++++++++++++++++++ .../ItemRequestRepositoryInterface.java | 16 +++++ .../ItemRequestServiceInterface.java | 17 +++++ .../request/dto/ItemRequestCreateDto.java | 19 +++++ .../shareit/request/dto/ItemRequestDto.java | 7 -- .../request/dto/ItemRequestResponseDto.java | 21 ++++++ .../request/mapper/ItemRequestMapper.java | 20 ++++++ .../shareit/request/model/ItemRequest.java | 2 +- 9 files changed, 210 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestService.java create mode 100644 src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java create mode 100644 src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java create mode 100644 src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java delete mode 100644 src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java create mode 100644 src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java create mode 100644 src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java index d149fdb..d67778e 100644 --- a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java +++ b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -1,9 +1,54 @@ package ru.practicum.shareit.request; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import ru.practicum.shareit.request.contracts.ItemRequestServiceInterface; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; + +import java.util.List; @RestController @RequestMapping(path = "/requests") +@RequiredArgsConstructor public class ItemRequestController { + + public static final String X_SHARER_USER_ID = "X-Sharer-User-Id"; + + private final ItemRequestServiceInterface itemRequestService; + + @PostMapping + public ItemRequestResponseDto createItem( + final @RequestHeader(X_SHARER_USER_ID) Long userId, + final @Valid @RequestBody ItemRequestCreateDto itemRequestCreateDto + ) { + itemRequestCreateDto.setUserId(userId); + + return itemRequestService.create(itemRequestCreateDto); + } + + @GetMapping + public List getListByUser(final @RequestHeader(name = X_SHARER_USER_ID) Long userId) + { + return itemRequestService.getListByUser(userId); + } + + @GetMapping("/all") + public List getList(final @RequestHeader(name = X_SHARER_USER_ID) Long userId) + { + return itemRequestService.getList(userId); + } + + @GetMapping("/{requestId}") + public ItemRequestResponseDto getById(final @PathVariable Long requestId) + { + return itemRequestService.getById(requestId); + } } diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestService.java b/src/main/java/ru/practicum/shareit/request/ItemRequestService.java new file mode 100644 index 0000000..e90fce7 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/ItemRequestService.java @@ -0,0 +1,71 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; +import ru.practicum.shareit.request.contracts.ItemRequestServiceInterface; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; +import ru.practicum.shareit.request.mapper.ItemRequestMapper; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.contracts.UserRepositoryInterface; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ItemRequestService implements ItemRequestServiceInterface { + + private final ItemRequestRepositoryInterface itemRequestRepository; + private final UserRepositoryInterface userRepository; + + @Override + public ItemRequestResponseDto create(final ItemRequestCreateDto itemRequestCreateDto) { + User user = userRepository.findById(itemRequestCreateDto.getUserId()).orElseThrow( + () -> new NotFoundException("User not found with id: " + itemRequestCreateDto.getUserId()) + ); + + ItemRequest itemRequest = new ItemRequest(); + itemRequest.setRequestor(user); + itemRequest.setDescription(itemRequestCreateDto.getText()); + itemRequest.setCreated(LocalDateTime.now()); + + itemRequestRepository.save(itemRequest); + + return ItemRequestMapper.toItemRequestResponseDto(itemRequest); + } + + @Override + public List getListByUser(final Long userId) { + User user = userRepository.findById(userId).orElseThrow( + () -> new NotFoundException("User not found with id: " + userId) + ); + + return itemRequestRepository.findAllByRequestor_IdOrderByCreatedDesc(user.getId()) + .stream() + .map(ItemRequestMapper::toItemRequestResponseDto) + .toList(); + } + + @Override + public List getList(final Long userId) { + return ( + userId == null + ? itemRequestRepository.allOrderByCreatedDesc() + : itemRequestRepository.findAllByRequestor_IdIsNotOrderByCreatedDesc(userId) + ).stream() + .map(ItemRequestMapper::toItemRequestResponseDto) + .toList(); + } + + @Override + public ItemRequestResponseDto getById(final Long id) { + return ItemRequestMapper.toItemRequestResponseDto(itemRequestRepository.findById(id).orElseThrow( + () -> new NotFoundException("Item request with id: " + id) + )); + } + +} diff --git a/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java b/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java new file mode 100644 index 0000000..68f97e8 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.request.contracts; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.shareit.request.model.ItemRequest; + +import java.util.List; + +public interface ItemRequestRepositoryInterface extends JpaRepository { + + List findAllByRequestor_IdOrderByCreatedDesc(Long id); + + List allOrderByCreatedDesc(); + + List findAllByRequestor_IdIsNotOrderByCreatedDesc(Long id); + +} diff --git a/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java b/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java new file mode 100644 index 0000000..3492cd6 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.request.contracts; + +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; + +import java.util.List; + +public interface ItemRequestServiceInterface { + + ItemRequestResponseDto create(ItemRequestCreateDto itemRequestCreateDto); + + List getListByUser(Long userId); + + List getList(Long userId); + + ItemRequestResponseDto getById(Long id); +} diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java new file mode 100644 index 0000000..1dea194 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java @@ -0,0 +1,19 @@ +package ru.practicum.shareit.request.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ItemRequestCreateDto { + + @NotBlank + private String text; + + private Long userId; +} diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java deleted file mode 100644 index 7b3ed54..0000000 --- a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.request.dto; - -/** - * TODO Sprint add-item-requests. - */ -public class ItemRequestDto { -} diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java new file mode 100644 index 0000000..d0b322b --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.request.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ItemRequestResponseDto { + + private Long id; + + private String description; + + private LocalDateTime created; +} diff --git a/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java b/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java new file mode 100644 index 0000000..67526b1 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.request.mapper; + +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; +import ru.practicum.shareit.request.model.ItemRequest; + + +public class ItemRequestMapper { + + private ItemRequestMapper() { + } + + public static ItemRequestResponseDto toItemRequestResponseDto(ItemRequest itemRequest) { + return new ItemRequestResponseDto( + itemRequest.getId(), + itemRequest.getDescription(), + itemRequest.getCreated() + ); + } + +} diff --git a/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java b/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java index 8bc1f22..5f320df 100644 --- a/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java +++ b/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java @@ -26,7 +26,7 @@ public class ItemRequest { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; + private Long id; private String description; From 6955c15f30ccfbbac30af7de42c309d93ee35b06 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Wed, 12 Mar 2025 00:53:13 +0300 Subject: [PATCH 02/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20ItemRequest=20=D0=BF=D1=80=D0=B8=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B8=20Item?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru/practicum/shareit/item/ItemService.java | 15 +++++++++++++++ .../practicum/shareit/item/dto/ItemCreateDto.java | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/ru/practicum/shareit/item/ItemService.java b/src/main/java/ru/practicum/shareit/item/ItemService.java index b4ec145..19c5ab5 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -25,6 +25,8 @@ import ru.practicum.shareit.item.mapper.ItemMapper; import ru.practicum.shareit.item.model.Comment; import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; +import ru.practicum.shareit.request.model.ItemRequest; import ru.practicum.shareit.user.contracts.UserRepositoryInterface; import ru.practicum.shareit.user.model.User; @@ -49,6 +51,7 @@ public class ItemService implements ItemServiceInterface { private final UserRepositoryInterface userRepository; private final CommentRepositoryInterface commentRepository; private final BookingRepositoryInterface bookingRepository; + private final ItemRequestRepositoryInterface itemRequestRepositoryInterface; private final Sort commentsSort = Sort.by(Sort.Direction.DESC, "created"); private final Sort bookingOrder = Sort.by(Sort.Direction.ASC, "start"); @@ -61,12 +64,24 @@ public ItemDto create(final ItemCreateDto itemDto, final Long userId) { () -> new NotFoundException(USER_NOT_FOUND.formatted(userId)) ); + ItemRequest itemRequest = null; + + if (itemDto.getRequestId() == null) { + itemRequest = itemRequestRepositoryInterface.findById(itemDto.getRequestId()).orElseThrow( + () -> new NotFoundException("Item request with id='%d' not found".formatted(itemDto.getRequestId())) + ); + } + Item item = new Item(); item.setName(itemDto.getName()); item.setDescription(itemDto.getDescription()); item.setOwner(user); item.setAvailable(itemDto.getAvailable()); + if (itemRequest != null) { + item.setRequest(itemRequest); + } + Item newItem = itemRepository.save(item); return ItemMapper.toItemDto(newItem); diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java index 82d567d..c6ec24a 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.item.dto; +import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; @@ -20,4 +21,7 @@ public class ItemCreateDto { @NotNull private Boolean available; + @Nullable + private Long requestId; + } From 9f744f344bd084752d1675522b3b82f75d7c7e32 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Wed, 12 Mar 2025 01:28:19 +0300 Subject: [PATCH 03/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20gateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .run/ShareItGateway.run.xml | 15 +++ .run/ShareItServer.run.xml | 15 +++ compose.yaml | 34 ++++- gateway/Dockerfile | 5 + gateway/pom.xml | 70 ++++++++++ .../ru/practicum/shareit/ShareItGateway.java | 4 +- .../shareit/booking/BookingClient.java | 57 ++++++++ .../shareit/booking/BookingController.java | 64 +++++++++ .../booking/dto/BookItemRequestDto.java | 24 ++++ .../shareit/booking/dto/BookingState.java | 27 ++++ .../practicum/shareit/client/BaseClient.java | 124 ++++++++++++++++++ .../src/main/resources/application.properties | 7 + pom.xml | 100 +++----------- server/Dockerfile | 5 + server/pom.xml | 86 ++++++++++++ .../ru/practicum/shareit/ShareItServer.java | 13 ++ .../shareit/booking/BookingController.java | 0 .../shareit/booking/BookingMapper.java | 0 .../shareit/booking/BookingService.java | 0 .../contracts/BookingRepositoryInterface.java | 0 .../contracts/BookingServiceInterface.java | 0 .../shareit/booking/dto/BookingCreateDto.java | 0 .../shareit/booking/dto/BookingDto.java | 0 .../shareit/booking/model/Booking.java | 0 .../shareit/booking/model/BookingState.java | 0 .../shareit/booking/model/BookingStatus.java | 0 .../shareit/exception/EmptyIdException.java | 0 .../shareit/exception/ErrorHandler.java | 0 .../shareit/exception/ErrorResponse.java | 0 .../InvalidBookingStatusException.java | 0 .../exception/InvalidOwnerException.java | 0 .../exception/ItemUnavailableException.java | 0 .../shareit/exception/NotFoundException.java | 0 .../exception/NotUniqueEmailException.java | 0 .../exception/UserDoesNotHaveBookedItem.java | 0 .../shareit/item/ItemController.java | 0 .../practicum/shareit/item/ItemService.java | 0 .../contracts/CommentRepositoryInterface.java | 0 .../contracts/ItemRepositoryInterface.java | 0 .../item/contracts/ItemServiceInterface.java | 0 .../shareit/item/dto/CommentCreateDto.java | 0 .../shareit/item/dto/CommentDto.java | 0 .../shareit/item/dto/ItemCreateDto.java | 0 .../practicum/shareit/item/dto/ItemDto.java | 0 .../shareit/item/dto/ItemInfoDto.java | 0 .../shareit/item/dto/ItemUpdateDto.java | 0 .../shareit/item/mapper/CommentMapper.java | 0 .../shareit/item/mapper/ItemMapper.java | 0 .../practicum/shareit/item/model/Comment.java | 0 .../ru/practicum/shareit/item/model/Item.java | 0 .../request/ItemRequestController.java | 0 .../shareit/request/ItemRequestService.java | 0 .../ItemRequestRepositoryInterface.java | 0 .../ItemRequestServiceInterface.java | 0 .../request/dto/ItemRequestCreateDto.java | 0 .../request/dto/ItemRequestResponseDto.java | 0 .../request/mapper/ItemRequestMapper.java | 0 .../shareit/request/model/ItemRequest.java | 0 .../shareit/user/UserController.java | 0 .../ru/practicum/shareit/user/UserMapper.java | 0 .../practicum/shareit/user/UserService.java | 0 .../contracts/UserRepositoryInterface.java | 0 .../user/contracts/UserServiceInterface.java | 0 .../shareit/user/dto/UserCreateDto.java | 0 .../practicum/shareit/user/dto/UserDto.java | 0 .../shareit/user/dto/UserUpdateDto.java | 0 .../ru/practicum/shareit/user/model/User.java | 0 .../main/resources/application.properties | 15 ++- {src => server/src}/main/resources/schema.sql | 0 .../ru/practicum/shareit/ShareItTests.java | 0 .../resources/application-test.properties | 13 -- 71 files changed, 573 insertions(+), 105 deletions(-) create mode 100644 .run/ShareItGateway.run.xml create mode 100644 .run/ShareItServer.run.xml create mode 100644 gateway/Dockerfile create mode 100644 gateway/pom.xml rename src/main/java/ru/practicum/shareit/ShareItApp.java => gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java (72%) create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java create mode 100644 gateway/src/main/resources/application.properties create mode 100644 server/Dockerfile create mode 100644 server/pom.xml create mode 100644 server/src/main/java/ru/practicum/shareit/ShareItServer.java rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingController.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingService.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/dto/BookingDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/model/Booking.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/model/BookingState.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/model/BookingStatus.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/EmptyIdException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/ErrorHandler.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/ErrorResponse.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/InvalidBookingStatusException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/InvalidOwnerException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/ItemUnavailableException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/NotFoundException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/NotUniqueEmailException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/UserDoesNotHaveBookedItem.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemController.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemService.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/contracts/CommentRepositoryInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/contracts/ItemServiceInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/CommentDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/ItemDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/ItemInfoDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/model/Comment.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/model/Item.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/ItemRequestController.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/ItemRequestService.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/request/model/ItemRequest.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserController.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserService.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/contracts/UserRepositoryInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/contracts/UserServiceInterface.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/dto/UserDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/model/User.java (100%) rename {src => server/src}/main/resources/application.properties (51%) rename {src => server/src}/main/resources/schema.sql (100%) rename {src => server/src}/test/java/ru/practicum/shareit/ShareItTests.java (100%) delete mode 100644 src/main/resources/application-test.properties diff --git a/.run/ShareItGateway.run.xml b/.run/ShareItGateway.run.xml new file mode 100644 index 0000000..32c8129 --- /dev/null +++ b/.run/ShareItGateway.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.run/ShareItServer.run.xml b/.run/ShareItServer.run.xml new file mode 100644 index 0000000..a8ed9e5 --- /dev/null +++ b/.run/ShareItServer.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index ab7c00d..83dfc00 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,15 +1,39 @@ services: + gateway: + build: gateway + image: shareit-gateway + container_name: shareit-gateway + ports: + - "8080:8080" + depends_on: + - server + environment: + - SHAREIT_SERVER_URL=http://server:9090 + + server: + build: server + image: shareit-server + container_name: shareit-server + ports: + - "9090:9090" + depends_on: + - db + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shareit + - SPRING_DATASOURCE_USERNAME=shareit + - SPRING_DATASOURCE_PASSWORD=shareit + db: image: postgres:16.1 container_name: postgres ports: - - "5432:5432" - volumes: - - .docker/data/postgres:/var/lib/postgresql/data/ + - "6541:5432" environment: - - POSTGRES_DB=shareit - - POSTGRES_USER=shareit - POSTGRES_PASSWORD=shareit + - POSTGRES_USER=shareit + - POSTGRES_DB=shareit + volumes: + - .docker/data/postgres:/var/lib/postgresql/data/ healthcheck: test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER timeout: 5s diff --git a/gateway/Dockerfile b/gateway/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/gateway/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/gateway/pom.xml b/gateway/pom.xml new file mode 100644 index 0000000..f3394c1 --- /dev/null +++ b/gateway/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-gateway + 0.0.1-SNAPSHOT + + ShareIt Gateway + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.hibernate.validator + hibernate-validator + + + + org.apache.httpcomponents.client5 + httpclient5 + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java similarity index 72% rename from src/main/java/ru/practicum/shareit/ShareItApp.java rename to gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java index a10a87d..a1dbcae 100644 --- a/src/main/java/ru/practicum/shareit/ShareItApp.java +++ b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ShareItApp { +public class ShareItGateway { public static void main(String[] args) { - SpringApplication.run(ShareItApp.class, args); + SpringApplication.run(ShareItGateway.class, args); } } diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java new file mode 100644 index 0000000..84c9668 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -0,0 +1,57 @@ +package ru.practicum.shareit.booking; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; + +import ru.practicum.shareit.booking.dto.BookItemRequestDto; +import ru.practicum.shareit.booking.dto.BookingState; +import ru.practicum.shareit.client.BaseClient; + +@Service +public class BookingClient extends BaseClient { + + private static final String API_PREFIX = "/bookings"; + + @Autowired + public BookingClient( + @Value("${shareit-server.url}") String serverUrl, + RestTemplateBuilder builder + ) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity getBookings( + long userId, + BookingState state, + Integer from, + Integer size + ) { + Map parameters = Map.of( + "state", state.name(), + "from", from, + "size", size + ); + return get("?state={state}&from={from}&size={size}", userId, parameters); + } + + public ResponseEntity bookItem(long userId, BookItemRequestDto requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity getBooking(long userId, Long bookingId) { + return get("/" + bookingId, userId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java new file mode 100644 index 0000000..7a391b0 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,64 @@ +package ru.practicum.shareit.booking; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import ru.practicum.shareit.booking.dto.BookItemRequestDto; +import ru.practicum.shareit.booking.dto.BookingState; + +@Controller +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +@Slf4j +@Validated +public class BookingController { + + private final BookingClient bookingClient; + + @GetMapping + public ResponseEntity getBookings( + @RequestHeader("X-Sharer-User-Id") long userId, + @RequestParam(name = "state", defaultValue = "all") String stateParam, + @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, + @Positive @RequestParam(name = "size", defaultValue = "10") Integer size + ) { + BookingState state = BookingState.from(stateParam).orElseThrow( + () -> new IllegalArgumentException("Unknown state: " + stateParam) + ); + + log.info("Get booking with state {}, userId={}, from={}, size={}", stateParam, userId, from, size); + return bookingClient.getBookings(userId, state, from, size); + } + + @PostMapping + public ResponseEntity bookItem( + @RequestHeader("X-Sharer-User-Id") long userId, + @RequestBody @Valid BookItemRequestDto requestDto + ) { + log.info("Creating booking {}, userId={}", requestDto, userId); + return bookingClient.bookItem(userId, requestDto); + } + + @GetMapping("/{bookingId}") + public ResponseEntity getBooking( + @RequestHeader("X-Sharer-User-Id") long userId, + @PathVariable Long bookingId + ) { + log.info("Get booking {}, userId={}", bookingId, userId); + return bookingClient.getBooking(userId, bookingId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java new file mode 100644 index 0000000..76f978e --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.booking.dto; + +import java.time.LocalDateTime; + +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.FutureOrPresent; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class BookItemRequestDto { + + private long itemId; + + @FutureOrPresent + private LocalDateTime start; + + @Future + private LocalDateTime end; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java new file mode 100644 index 0000000..135de62 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java @@ -0,0 +1,27 @@ +package ru.practicum.shareit.booking.dto; + +import java.util.Optional; + +public enum BookingState { + // Все + ALL, + // Текущие + CURRENT, + // Будущие + FUTURE, + // Завершенные + PAST, + // Отклоненные + REJECTED, + // Ожидающие подтверждения + WAITING; + + public static Optional from(String stringState) { + for (BookingState state : values()) { + if (state.name().equalsIgnoreCase(stringState)) { + return Optional.of(state); + } + } + return Optional.empty(); + } +} diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java new file mode 100644 index 0000000..3b0c159 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -0,0 +1,124 @@ +package ru.practicum.shareit.client; + +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; + +public class BaseClient { + + protected final RestTemplate rest; + + public BaseClient(RestTemplate rest) { + this.rest = rest; + } + + protected ResponseEntity get(String path) { + return get(path, null, null); + } + + protected ResponseEntity get(String path, long userId) { + return get(path, userId, null); + } + + protected ResponseEntity get(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.GET, path, userId, parameters, null); + } + + protected ResponseEntity post(String path, T body) { + return post(path, null, null, body); + } + + protected ResponseEntity post(String path, long userId, T body) { + return post(path, userId, null, body); + } + + protected ResponseEntity post(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.POST, path, userId, parameters, body); + } + + protected ResponseEntity put(String path, long userId, T body) { + return put(path, userId, null, body); + } + + protected ResponseEntity put(String path, long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PUT, path, userId, parameters, body); + } + + protected ResponseEntity patch(String path, T body) { + return patch(path, null, null, body); + } + + protected ResponseEntity patch(String path, long userId) { + return patch(path, userId, null, null); + } + + protected ResponseEntity patch(String path, long userId, T body) { + return patch(path, userId, null, body); + } + + protected ResponseEntity patch(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PATCH, path, userId, parameters, body); + } + + protected ResponseEntity delete(String path) { + return delete(path, null, null); + } + + protected ResponseEntity delete(String path, long userId) { + return delete(path, userId, null); + } + + protected ResponseEntity delete(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.DELETE, path, userId, parameters, null); + } + + private ResponseEntity makeAndSendRequest( + HttpMethod method, String path, Long userId, @Nullable Map parameters, @Nullable T body) { + HttpEntity requestEntity = new HttpEntity<>(body, defaultHeaders(userId)); + + ResponseEntity shareitServerResponse; + try { + if (parameters != null) { + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class, parameters); + } else { + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class); + } + } catch (HttpStatusCodeException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray()); + } + return prepareGatewayResponse(shareitServerResponse); + } + + private HttpHeaders defaultHeaders(Long userId) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + if (userId != null) { + headers.set("X-Sharer-User-Id", String.valueOf(userId)); + } + return headers; + } + + private static ResponseEntity prepareGatewayResponse(ResponseEntity response) { + if (response.getStatusCode().is2xxSuccessful()) { + return response; + } + + ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(response.getStatusCode()); + + if (response.hasBody()) { + return responseBuilder.body(response.getBody()); + } + + return responseBuilder.build(); + } + +} diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties new file mode 100644 index 0000000..2ee0851 --- /dev/null +++ b/gateway/src/main/resources/application.properties @@ -0,0 +1,7 @@ +logging.level.org.springframework.web.client.RestTemplate=DEBUG +#logging.level.org.apache.http=DEBUG +#logging.level.httpclient.wire=DEBUG + +server.port=8080 + +shareit-server.url=http://localhost:9090 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 14274a6..79cba37 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ ru.practicum shareit + pom 0.0.1-SNAPSHOT ShareIt @@ -19,76 +20,29 @@ 21 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.postgresql - postgresql - runtime - - - - org.projectlombok - lombok - true - - - - com.h2database - h2 - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-validation - - + + gateway + server + - - - src/main/resources - true - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - + + org.springframework.boot + spring-boot-maven-plugin + + + true + + + + org.projectlombok + lombok + + + + org.apache.maven.plugins maven-surefire-plugin @@ -235,17 +189,5 @@ - - coverage - - - - org.jacoco - jacoco-maven-plugin - - - - - - + \ No newline at end of file diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/server/pom.xml b/server/pom.xml new file mode 100644 index 0000000..566db3e --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-server + 0.0.1-SNAPSHOT + + ShareIt Server + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.postgresql + postgresql + runtime + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + + diff --git a/server/src/main/java/ru/practicum/shareit/ShareItServer.java b/server/src/main/java/ru/practicum/shareit/ShareItServer.java new file mode 100644 index 0000000..303541d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/ShareItServer.java @@ -0,0 +1,13 @@ +package ru.practicum.shareit; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ShareItServer { + + public static void main(String[] args) { + SpringApplication.run(ShareItServer.class, args); + } + +} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingController.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingController.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingMapper.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingService.java b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingService.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingService.java diff --git a/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java rename to server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java diff --git a/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java rename to server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java rename to server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/Booking.java b/server/src/main/java/ru/practicum/shareit/booking/model/Booking.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/Booking.java rename to server/src/main/java/ru/practicum/shareit/booking/model/Booking.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/BookingState.java b/server/src/main/java/ru/practicum/shareit/booking/model/BookingState.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/BookingState.java rename to server/src/main/java/ru/practicum/shareit/booking/model/BookingState.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java b/server/src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java rename to server/src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java diff --git a/src/main/java/ru/practicum/shareit/exception/EmptyIdException.java b/server/src/main/java/ru/practicum/shareit/exception/EmptyIdException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/EmptyIdException.java rename to server/src/main/java/ru/practicum/shareit/exception/EmptyIdException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ErrorHandler.java rename to server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ErrorResponse.java rename to server/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java diff --git a/src/main/java/ru/practicum/shareit/exception/InvalidBookingStatusException.java b/server/src/main/java/ru/practicum/shareit/exception/InvalidBookingStatusException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/InvalidBookingStatusException.java rename to server/src/main/java/ru/practicum/shareit/exception/InvalidBookingStatusException.java diff --git a/src/main/java/ru/practicum/shareit/exception/InvalidOwnerException.java b/server/src/main/java/ru/practicum/shareit/exception/InvalidOwnerException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/InvalidOwnerException.java rename to server/src/main/java/ru/practicum/shareit/exception/InvalidOwnerException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ItemUnavailableException.java b/server/src/main/java/ru/practicum/shareit/exception/ItemUnavailableException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ItemUnavailableException.java rename to server/src/main/java/ru/practicum/shareit/exception/ItemUnavailableException.java diff --git a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/NotFoundException.java rename to server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java diff --git a/src/main/java/ru/practicum/shareit/exception/NotUniqueEmailException.java b/server/src/main/java/ru/practicum/shareit/exception/NotUniqueEmailException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/NotUniqueEmailException.java rename to server/src/main/java/ru/practicum/shareit/exception/NotUniqueEmailException.java diff --git a/src/main/java/ru/practicum/shareit/exception/UserDoesNotHaveBookedItem.java b/server/src/main/java/ru/practicum/shareit/exception/UserDoesNotHaveBookedItem.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/UserDoesNotHaveBookedItem.java rename to server/src/main/java/ru/practicum/shareit/exception/UserDoesNotHaveBookedItem.java diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/server/src/main/java/ru/practicum/shareit/item/ItemController.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/ItemController.java rename to server/src/main/java/ru/practicum/shareit/item/ItemController.java diff --git a/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/ItemService.java rename to server/src/main/java/ru/practicum/shareit/item/ItemService.java diff --git a/src/main/java/ru/practicum/shareit/item/contracts/CommentRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/item/contracts/CommentRepositoryInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/contracts/CommentRepositoryInterface.java rename to server/src/main/java/ru/practicum/shareit/item/contracts/CommentRepositoryInterface.java diff --git a/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java rename to server/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java diff --git a/src/main/java/ru/practicum/shareit/item/contracts/ItemServiceInterface.java b/server/src/main/java/ru/practicum/shareit/item/contracts/ItemServiceInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/contracts/ItemServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/item/contracts/ItemServiceInterface.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemInfoDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemInfoDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemInfoDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemInfoDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java diff --git a/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java rename to server/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java diff --git a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java rename to server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java diff --git a/src/main/java/ru/practicum/shareit/item/model/Comment.java b/server/src/main/java/ru/practicum/shareit/item/model/Comment.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/model/Comment.java rename to server/src/main/java/ru/practicum/shareit/item/model/Comment.java diff --git a/src/main/java/ru/practicum/shareit/item/model/Item.java b/server/src/main/java/ru/practicum/shareit/item/model/Item.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/model/Item.java rename to server/src/main/java/ru/practicum/shareit/item/model/Item.java diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/ItemRequestController.java rename to server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestService.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/ItemRequestService.java rename to server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java diff --git a/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java rename to server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java diff --git a/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java b/server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestServiceInterface.java diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java rename to server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java rename to server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java diff --git a/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java rename to server/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java diff --git a/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java b/server/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java similarity index 100% rename from src/main/java/ru/practicum/shareit/request/model/ItemRequest.java rename to server/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/server/src/main/java/ru/practicum/shareit/user/UserController.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserController.java rename to server/src/main/java/ru/practicum/shareit/user/UserController.java diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserMapper.java rename to server/src/main/java/ru/practicum/shareit/user/UserMapper.java diff --git a/src/main/java/ru/practicum/shareit/user/UserService.java b/server/src/main/java/ru/practicum/shareit/user/UserService.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserService.java rename to server/src/main/java/ru/practicum/shareit/user/UserService.java diff --git a/src/main/java/ru/practicum/shareit/user/contracts/UserRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/user/contracts/UserRepositoryInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/contracts/UserRepositoryInterface.java rename to server/src/main/java/ru/practicum/shareit/user/contracts/UserRepositoryInterface.java diff --git a/src/main/java/ru/practicum/shareit/user/contracts/UserServiceInterface.java b/server/src/main/java/ru/practicum/shareit/user/contracts/UserServiceInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/contracts/UserServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/user/contracts/UserServiceInterface.java diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java rename to server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserDto.java rename to server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java rename to server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java diff --git a/src/main/java/ru/practicum/shareit/user/model/User.java b/server/src/main/java/ru/practicum/shareit/user/model/User.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/model/User.java rename to server/src/main/java/ru/practicum/shareit/user/model/User.java diff --git a/src/main/resources/application.properties b/server/src/main/resources/application.properties similarity index 51% rename from src/main/resources/application.properties rename to server/src/main/resources/application.properties index 9ba84fe..6a96840 100644 --- a/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -1,14 +1,17 @@ +server.port=9090 + spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true -#spring.jpa.properties.hibernate.show_sql=true spring.sql.init.mode=always -logging.level.org.springframework.orm.jpa=INFO -logging.level.org.springframework.transaction=INFO -logging.level.org.springframework.transaction.interceptor=TRACE -logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG - +#--- spring.datasource.driverClassName=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/shareit spring.datasource.username=shareit spring.datasource.password=shareit +#--- +spring.config.activate.on-profile=test +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:shareit +spring.datasource.username=shareit +spring.datasource.password=shareit \ No newline at end of file diff --git a/src/main/resources/schema.sql b/server/src/main/resources/schema.sql similarity index 100% rename from src/main/resources/schema.sql rename to server/src/main/resources/schema.sql diff --git a/src/test/java/ru/practicum/shareit/ShareItTests.java b/server/src/test/java/ru/practicum/shareit/ShareItTests.java similarity index 100% rename from src/test/java/ru/practicum/shareit/ShareItTests.java rename to server/src/test/java/ru/practicum/shareit/ShareItTests.java diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties deleted file mode 100644 index 0768115..0000000 --- a/src/main/resources/application-test.properties +++ /dev/null @@ -1,13 +0,0 @@ -spring.jpa.hibernate.ddl-auto=none -spring.jpa.properties.hibernate.format_sql=true -spring.sql.init.mode=always - -logging.level.org.springframework.orm.jpa=INFO -logging.level.org.springframework.transaction=INFO -logging.level.org.springframework.transaction.interceptor=TRACE -logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG - -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.url=jdbc:h2:mem:shareit;MODE=PostgreSQL -spring.datasource.username=shareit -spring.datasource.password=shareit From 248ec04f04f9e0e773b8c9330c2fec9f4fcbcf7e Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Thu, 13 Mar 2025 00:36:12 +0300 Subject: [PATCH 04/17] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=D0=B9.=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shareit/booking/BookingClient.java | 23 ++++++ .../shareit/booking/BookingController.java | 44 +++++++++-- .../booking/dto/BookItemRequestDto.java | 5 +- .../ru/practicum/shareit/item/ItemClient.java | 61 +++++++++++++++ .../shareit/item/ItemController.java | 77 +++++++++++++++++++ .../shareit/item/dto/CommentCreateDto.java | 18 +++++ .../shareit/item/dto/ItemCreateDto.java | 27 +++++++ .../shareit/item/dto/ItemUpdateDto.java | 24 ++++++ .../shareit/booking/BookingController.java | 25 +++--- .../shareit/booking/BookingService.java | 41 +++++----- .../contracts/BookingRepositoryInterface.java | 21 ++--- .../contracts/BookingServiceInterface.java | 4 +- .../shareit/booking/dto/BookingCreateDto.java | 18 ----- .../shareit/item/ItemController.java | 19 +++-- .../practicum/shareit/item/ItemService.java | 8 -- .../shareit/item/dto/CommentCreateDto.java | 2 - .../shareit/item/dto/ItemCreateDto.java | 7 -- .../practicum/shareit/item/dto/ItemDto.java | 5 -- .../shareit/item/dto/ItemUpdateDto.java | 4 - 19 files changed, 328 insertions(+), 105 deletions(-) create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/ItemController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java index 84c9668..aafcd04 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.booking; +import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; @@ -50,8 +51,30 @@ public ResponseEntity bookItem(long userId, BookItemRequestDto requestDt return post("", userId, requestDto); } + public ResponseEntity approve(Long bookingId, Long ownerId, boolean approved) { + Map parameters = Map.of( + "bookingId", bookingId, + "approved", approved + ); + return post("/{bookingId}?approved={approved}", ownerId, parameters); + } + public ResponseEntity getBooking(long userId, Long bookingId) { return get("/" + bookingId, userId); } + public ResponseEntity getByOwnerAndState( + Long ownerId, BookingState state, + Integer from, + Integer size + ) { + Map parameters = Map.of( + "state", state.name(), + "from", from, + "size", size + ); + return get("/owner?state={state}&from={from}&size={size}", ownerId, parameters); + } + + } diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java index 7a391b0..73ac9ef 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Controller; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -26,39 +27,68 @@ @Validated public class BookingController { + private static final String HEADER_USER_ID = "X-Sharer-User-Id"; + private static final String LOG_GET_LIST_BY_PARAMS = "Get booking with state {}, userId={}, from={}, size={}"; + private final BookingClient bookingClient; @GetMapping public ResponseEntity getBookings( - @RequestHeader("X-Sharer-User-Id") long userId, + @RequestHeader(HEADER_USER_ID) long userId, @RequestParam(name = "state", defaultValue = "all") String stateParam, @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, @Positive @RequestParam(name = "size", defaultValue = "10") Integer size ) { - BookingState state = BookingState.from(stateParam).orElseThrow( - () -> new IllegalArgumentException("Unknown state: " + stateParam) - ); + BookingState state = getBookingState(stateParam); - log.info("Get booking with state {}, userId={}, from={}, size={}", stateParam, userId, from, size); + log.info(LOG_GET_LIST_BY_PARAMS, stateParam, userId, from, size); return bookingClient.getBookings(userId, state, from, size); } @PostMapping public ResponseEntity bookItem( - @RequestHeader("X-Sharer-User-Id") long userId, + @RequestHeader(HEADER_USER_ID) long userId, @RequestBody @Valid BookItemRequestDto requestDto ) { log.info("Creating booking {}, userId={}", requestDto, userId); return bookingClient.bookItem(userId, requestDto); } + @PatchMapping("/{bookingId}") + public ResponseEntity approve( + @PathVariable Long bookingId, + @RequestParam boolean approved, + @RequestHeader(name = HEADER_USER_ID) Long ownerId + ) { + return bookingClient.approve(bookingId, ownerId, approved); + } + @GetMapping("/{bookingId}") public ResponseEntity getBooking( - @RequestHeader("X-Sharer-User-Id") long userId, + @RequestHeader(HEADER_USER_ID) long userId, @PathVariable Long bookingId ) { log.info("Get booking {}, userId={}", bookingId, userId); return bookingClient.getBooking(userId, bookingId); } + @GetMapping("/owner") + public ResponseEntity getByOwnerAndState( + @RequestParam(required = false, defaultValue = "all") String stateParam, + @RequestHeader(name = HEADER_USER_ID) Long ownerId, + @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, + @Positive @RequestParam(name = "size", defaultValue = "10") Integer size + ) { + BookingState state = getBookingState(stateParam); + + log.info(LOG_GET_LIST_BY_PARAMS, stateParam, ownerId, from, size); + return bookingClient.getByOwnerAndState(ownerId, state, from, size); + } + + private static BookingState getBookingState(String stateParam) { + return BookingState.from(stateParam).orElseThrow( + () -> new IllegalArgumentException("Unknown state: " + stateParam) + ); + } + } diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java index 76f978e..13935ce 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.Future; import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -13,11 +14,13 @@ @AllArgsConstructor public class BookItemRequestDto { - private long itemId; + private Long itemId; + @NotNull @FutureOrPresent private LocalDateTime start; + @NotNull @Future private LocalDateTime end; diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java new file mode 100644 index 0000000..12b015f --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -0,0 +1,61 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.item.dto.CommentCreateDto; +import ru.practicum.shareit.item.dto.ItemCreateDto; +import ru.practicum.shareit.item.dto.ItemUpdateDto; + +import java.util.Map; + +@Service +public class ItemClient extends BaseClient { + + private static final String API_PREFIX = "/items"; + + @Autowired + public ItemClient( + @Value("${shareit-server.url}") String serverUrl, + RestTemplateBuilder builder + ) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity create(@Valid ItemCreateDto dto, Long userId) { + return post("", userId, dto); + } + + public ResponseEntity update(@Valid ItemUpdateDto dto, Long itemId, Long userId) { + return patch("/" + itemId, userId, dto); + } + + public ResponseEntity findItemById(Long itemId, Long userId) { + return get("/" + itemId, userId); + } + + public ResponseEntity findItemsByOwner(Long ownerId) { + return get("/owner", ownerId); + } + + public ResponseEntity findItemsByText(String text) { + Map params = Map.of("text", text); + return get("/search", null, params); + } + + public ResponseEntity addComment(Long itemId, Long authorId, @Valid CommentCreateDto commentDto) { + return post("/" + itemId, authorId, commentDto); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java new file mode 100644 index 0000000..1137d2f --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,77 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.practicum.shareit.item.dto.CommentCreateDto; +import ru.practicum.shareit.item.dto.ItemCreateDto; +import ru.practicum.shareit.item.dto.ItemUpdateDto; + +@Controller +@RequestMapping(path = "/items") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemController { + + private static final String HEADER_USER_ID = "X-Sharer-User-Id"; + + private final ItemClient itemClient; + + @PostMapping + public ResponseEntity create( + @Valid @RequestBody ItemCreateDto dto, + @RequestHeader(name = HEADER_USER_ID) Long userId + ) { + return itemClient.create(dto, userId); + } + + @PatchMapping("/{itemId}") + public ResponseEntity update( + @Valid @RequestBody ItemUpdateDto dto, + @PathVariable Long itemId, + @RequestHeader(name = HEADER_USER_ID) Long userId + ) { + + return itemClient.update(dto, itemId, userId); + } + + @GetMapping("/{itemId}") + public ResponseEntity getItem( + @PathVariable Long itemId, + @RequestHeader(name = HEADER_USER_ID) Long userId + ) { + return itemClient.findItemById(itemId, userId); + } + + @GetMapping + public ResponseEntity getItemsByOwner(@RequestHeader(name = HEADER_USER_ID) Long ownerId) { + return itemClient.findItemsByOwner(ownerId); + } + + @GetMapping("/search") + public ResponseEntity findItemsByText(@RequestParam String text) { + return itemClient.findItemsByText(text); + } + + @PostMapping("/{itemId}/comment") + public ResponseEntity addComment( + @PathVariable Long itemId, + @RequestHeader(name = HEADER_USER_ID) Long authorId, + @RequestBody @Valid CommentCreateDto commentDto + ) { + return itemClient.addComment(itemId, authorId, commentDto); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java new file mode 100644 index 0000000..3a788e3 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.item.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CommentCreateDto { + + @NotBlank + private String text; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java new file mode 100644 index 0000000..c6ec24a --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java @@ -0,0 +1,27 @@ +package ru.practicum.shareit.item.dto; + +import jakarta.annotation.Nullable; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class ItemCreateDto { + + @NotBlank + private String name; + + @NotBlank + private String description; + + @NotNull + private Boolean available; + + @Nullable + private Long requestId; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java new file mode 100644 index 0000000..874ba5f --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.item.dto; + +import jakarta.annotation.Nullable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class ItemUpdateDto { + + private Long id; + + @Nullable + private String name; + + @Nullable + private String description; + + @Nullable + private Boolean available; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java index 08c024b..3f4bc56 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.booking; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -23,14 +22,14 @@ @RequiredArgsConstructor public class BookingController { - public static final String X_SHARER_USER_ID = "X-Sharer-User-Id"; + public static final String HEADER_USER_ID = "X-Sharer-User-Id"; private final BookingServiceInterface bookingService; @PostMapping public BookingDto create( - final @RequestBody @Valid BookingCreateDto bookingDto, - final @RequestHeader(name = X_SHARER_USER_ID) Long bookerId + final @RequestBody BookingCreateDto bookingDto, + final @RequestHeader(name = HEADER_USER_ID) Long bookerId ) { bookingDto.setBookerId(bookerId); @@ -41,7 +40,7 @@ public BookingDto create( public BookingDto approve( final @PathVariable Long bookingId, final @RequestParam boolean approved, - final @RequestHeader(name = X_SHARER_USER_ID) Long ownerId + final @RequestHeader(name = HEADER_USER_ID) Long ownerId ) { return bookingService.approve(bookingId, ownerId, approved); } @@ -49,7 +48,7 @@ public BookingDto approve( @GetMapping("/{bookingId}") public BookingDto getById( final @PathVariable Long bookingId, - final @RequestHeader(name = X_SHARER_USER_ID) Long userId + final @RequestHeader(name = HEADER_USER_ID) Long userId ) { return bookingService.getById(bookingId, userId); } @@ -57,20 +56,26 @@ public BookingDto getById( @GetMapping public List getByBookerAndState( final @RequestParam(required = false) BookingState state, - final @RequestHeader(name = X_SHARER_USER_ID) Long bookerId + final @RequestHeader(name = HEADER_USER_ID) Long bookerId, + final @RequestParam(value = "from", defaultValue = "0") Integer from, + final @RequestParam(value = "size", defaultValue = "10") Integer size ) { return bookingService.getByBookerAndState( bookerId, - state == null ? BookingState.ALL : state + state == null ? BookingState.ALL : state, + from, + size ); } @GetMapping("/owner") public List getByOwnerAndState( final @RequestParam(required = false) BookingState state, - final @RequestHeader(name = X_SHARER_USER_ID) Long ownerId + final @RequestHeader(name = HEADER_USER_ID) Long ownerId, + final @RequestParam(value = "from", defaultValue = "0") Integer from, + final @RequestParam(value = "size", defaultValue = "10") Integer size ) { - return bookingService.getByOwnerAndState(ownerId, state); + return bookingService.getByOwnerAndState(ownerId, state, from, size); } } diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingService.java b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java index dba9962..2a16a93 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingService.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java @@ -1,7 +1,8 @@ package ru.practicum.shareit.booking; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +25,6 @@ import java.time.LocalDateTime; import java.util.List; -@Slf4j @Service @AllArgsConstructor @Transactional(readOnly = true) @@ -41,7 +41,6 @@ public class BookingService implements BookingServiceInterface { @Override @Transactional public BookingDto create(final BookingCreateDto bookingDto) { - log.info("Creating booking. ItemId - '{}', BookerId - '{}'", bookingDto.getItemId(), bookingDto.getBookerId()); Item item = itemRepository.findById(bookingDto.getItemId()).orElseThrow( () -> new NotFoundException("Item with id='%d' not found".formatted(bookingDto.getItemId())) @@ -71,7 +70,6 @@ public BookingDto create(final BookingCreateDto bookingDto) { @Override @Transactional public BookingDto approve(final Long id, final Long ownerId, final boolean approved) { - log.info("Approve booking with id='{}' and ownerId='{}'", id, ownerId); Booking booking = bookingRepository.findById(id).orElseThrow( () -> new NotFoundException(BOOKING_NOT_FOUND.formatted(id)) @@ -94,7 +92,6 @@ public BookingDto approve(final Long id, final Long ownerId, final boolean appro @Override public BookingDto getById(final Long id, final Long userId) { - log.info("Get booking with id '{}' from user with id '{}'", id, userId); Booking booking = bookingRepository.findById(id).orElseThrow( () -> new NotFoundException(BOOKING_NOT_FOUND.formatted(id)) @@ -111,25 +108,26 @@ public BookingDto getById(final Long id, final Long userId) { } @Override - public List getByBookerAndState(final Long bookerId, final BookingState state) { - log.info("getByBookerAndState: {}, {}", bookerId, state); + public List getByBookerAndState(final Long bookerId, final BookingState state, final Integer from, final Integer size) { User booker = userRepository.findById(bookerId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(bookerId)) ); + Pageable pageable = PageRequest.of(from, size, sort); + List list = switch (state) { - case WAITING -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.WAITING, sort); - case REJECTED -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.REJECTED, sort); + case WAITING -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.WAITING, pageable); + case REJECTED -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.REJECTED, pageable); case CURRENT -> bookingRepository.findAllByBookerAndStartBeforeAndEndAfter( booker, LocalDateTime.now(), LocalDateTime.now(), - sort + pageable ); - case PAST -> bookingRepository.findAllByBookerAndEndBefore(booker, LocalDateTime.now(), sort); - case FUTURE -> bookingRepository.findAllByBookerAndStartAfter(booker, LocalDateTime.now(), sort); - default -> bookingRepository.findAllByBooker(booker, sort); + case PAST -> bookingRepository.findAllByBookerAndEndBefore(booker, LocalDateTime.now(), pageable); + case FUTURE -> bookingRepository.findAllByBookerAndStartAfter(booker, LocalDateTime.now(), pageable); + default -> bookingRepository.findAllByBooker(booker, pageable); }; return list @@ -139,25 +137,26 @@ public List getByBookerAndState(final Long bookerId, final BookingSt } @Override - public List getByOwnerAndState(Long ownerId, BookingState state) { - log.info("getByOwnerAndState: {}, {}", ownerId, state); + public List getByOwnerAndState(final Long ownerId, final BookingState state, final Integer from, final Integer size) { User owner = userRepository.findById(ownerId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(ownerId)) ); + Pageable pageable = PageRequest.of(from, size, sort); + List list = switch (state) { - case WAITING -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.WAITING, sort); - case REJECTED -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.REJECTED, sort); + case WAITING -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.WAITING, pageable); + case REJECTED -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.REJECTED, pageable); case CURRENT -> bookingRepository.findAllByItemOwnerAndStartBeforeAndEndAfter( owner, LocalDateTime.now(), LocalDateTime.now(), - sort + pageable ); - case PAST -> bookingRepository.findAllByItemOwnerAndEndBefore(owner, LocalDateTime.now(), sort); - case FUTURE -> bookingRepository.findAllByItemOwnerAndStartAfter(owner, LocalDateTime.now(), sort); - default -> bookingRepository.findAllByItemOwner(owner, sort); + case PAST -> bookingRepository.findAllByItemOwnerAndEndBefore(owner, LocalDateTime.now(), pageable); + case FUTURE -> bookingRepository.findAllByItemOwnerAndStartAfter(owner, LocalDateTime.now(), pageable); + default -> bookingRepository.findAllByItemOwner(owner, pageable); }; return list diff --git a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java index aca251d..1dbaff6 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java +++ b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.booking.contracts; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; @@ -12,35 +13,35 @@ public interface BookingRepositoryInterface extends JpaRepository { - List findAllByBookerAndStatus(User booker, BookingStatus bookingStatus, Sort sort); + List findAllByBookerAndStatus(User booker, BookingStatus bookingStatus, Pageable pageable); - List findAllByBooker(User booker, Sort sort); + List findAllByBooker(User booker, Pageable pageable); List findAllByBookerAndStartBeforeAndEndAfter( User booker, LocalDateTime start, LocalDateTime end, - Sort sort + Pageable pageable ); - List findAllByBookerAndEndBefore(User booker, LocalDateTime start, Sort sort); + List findAllByBookerAndEndBefore(User booker, LocalDateTime start, Pageable pageable); - List findAllByBookerAndStartAfter(User booker, LocalDateTime start, Sort sort); + List findAllByBookerAndStartAfter(User booker, LocalDateTime start, Pageable pageable); - List findAllByItemOwnerAndStatus(User owner, BookingStatus bookingStatus, Sort sort); + List findAllByItemOwnerAndStatus(User owner, BookingStatus bookingStatus, Pageable pageable); - List findAllByItemOwner(User owner, Sort sort); + List findAllByItemOwner(User owner, Pageable pageable); List findAllByItemOwnerAndStartBeforeAndEndAfter( User owner, LocalDateTime start, LocalDateTime end, - Sort sort + Pageable pageable ); - List findAllByItemOwnerAndEndBefore(User owner, LocalDateTime start, Sort sort); + List findAllByItemOwnerAndEndBefore(User owner, LocalDateTime start, Pageable pageable); - List findAllByItemOwnerAndStartAfter(User owner, LocalDateTime start, Sort sort); + List findAllByItemOwnerAndStartAfter(User owner, LocalDateTime start, Pageable pageable); boolean existsByBookerIdAndItemIdAndStatusEqualsAndEndIsBefore(Long bookerId, Long itemId, BookingStatus bookingStatus, LocalDateTime end); diff --git a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java index 73837f2..6999a07 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java +++ b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java @@ -14,8 +14,8 @@ public interface BookingServiceInterface { BookingDto getById(Long id, Long userId); - List getByBookerAndState(Long bookerId, BookingState state); + List getByBookerAndState(Long bookerId, BookingState state, Integer from, Integer size); - List getByOwnerAndState(Long ownerId, BookingState state); + List getByOwnerAndState(Long ownerId, BookingState state, Integer from, Integer size); } diff --git a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java index 6593410..3bbda00 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java +++ b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java @@ -1,10 +1,5 @@ package ru.practicum.shareit.booking.dto; -import jakarta.annotation.Nullable; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.Future; -import jakarta.validation.constraints.FutureOrPresent; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,28 +14,15 @@ @Builder public class BookingCreateDto { - @Nullable private Long id; - @NotNull - @FutureOrPresent private LocalDateTime start; - @NotNull - @Future private LocalDateTime end; private Long itemId; private Long bookerId; - @Nullable private BookingStatus bookingStatus; - - @AssertTrue - boolean isStartBeforeEnd() { - return start != null - && end != null - && start.isBefore(end); - } } diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemController.java b/server/src/main/java/ru/practicum/shareit/item/ItemController.java index b27b4ad..8c5f488 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.item; -import jakarta.validation.Valid; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -27,22 +26,22 @@ @AllArgsConstructor public class ItemController { - public static final String X_SHARER_USER_ID = "X-Sharer-User-Id"; + private static final String HEADER_USER_ID = "X-Sharer-User-Id"; private ItemServiceInterface itemService; @PostMapping public ItemDto create( - final @Valid @RequestBody ItemCreateDto dto, - final @RequestHeader(name = X_SHARER_USER_ID) Long userId + final @RequestBody ItemCreateDto dto, + final @RequestHeader(name = HEADER_USER_ID) Long userId ) { return itemService.create(dto, userId); } @PatchMapping("/{itemId}") public ItemDto update( - final @Valid @RequestBody ItemUpdateDto dto, + final @RequestBody ItemUpdateDto dto, final @PathVariable Long itemId, - final @RequestHeader(name = X_SHARER_USER_ID) Long userId + final @RequestHeader(name = HEADER_USER_ID) Long userId ) { if (itemId == null) { throw new EmptyIdException("Id required"); @@ -56,13 +55,13 @@ public ItemDto update( @GetMapping("/{itemId}") public ItemInfoDto getItem( final @PathVariable Long itemId, - final @RequestHeader(name = X_SHARER_USER_ID) Long userId + final @RequestHeader(name = HEADER_USER_ID) Long userId ) { return itemService.findItemById(itemId, userId); } @GetMapping - public List getItemsByOwner(final @RequestHeader(name = X_SHARER_USER_ID) Long ownerId) { + public List getItemsByOwner(final @RequestHeader(name = HEADER_USER_ID) Long ownerId) { return itemService.findItemsByOwner(ownerId); } @@ -74,8 +73,8 @@ public List findItemsByText(final @RequestParam String text) { @PostMapping("/{itemId}/comment") public CommentDto addComment( final @PathVariable Long itemId, - final @RequestHeader(name = X_SHARER_USER_ID) Long authorId, - final @RequestBody @Valid CommentCreateDto commentDto + final @RequestHeader(name = HEADER_USER_ID) Long authorId, + final @RequestBody CommentCreateDto commentDto ) { return itemService.addComment(itemId, authorId, commentDto); } diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java index 19c5ab5..ac40a4f 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -1,7 +1,6 @@ package ru.practicum.shareit.item; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -39,7 +38,6 @@ import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; -@Slf4j @Service @AllArgsConstructor public class ItemService implements ItemServiceInterface { @@ -58,7 +56,6 @@ public class ItemService implements ItemServiceInterface { @Override public ItemDto create(final ItemCreateDto itemDto, final Long userId) { - log.info("Creating Item: {}, userId - '{}'", itemDto.getName(), userId); User user = userRepository.findById(userId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(userId)) @@ -89,7 +86,6 @@ public ItemDto create(final ItemCreateDto itemDto, final Long userId) { @Override public ItemDto update(final ItemUpdateDto itemDto, final Long userId) { - log.info("Updating Item: {}, userId - '{}'", itemDto.getId(), userId); if (itemDto.getId() == null) { throw new EmptyIdException("Item id is empty"); @@ -127,7 +123,6 @@ public ItemDto update(final ItemUpdateDto itemDto, final Long userId) { @Override @Transactional(readOnly = true) public ItemInfoDto findItemById(Long itemId, Long userId) { - log.info("Finding Item with id: '{}'", itemId); Item item = itemRepository.findById(itemId).orElseThrow( () -> new NotFoundException(ITEM_NOT_FOUND.formatted(itemId)) @@ -156,7 +151,6 @@ public ItemInfoDto findItemById(Long itemId, Long userId) { @Override @Transactional(readOnly = true) public List findItemsByOwner(final Long ownerId) { - log.info("Finding Items by owner with id: '{}'", ownerId); User user = userRepository.findById(ownerId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(ownerId)) @@ -201,7 +195,6 @@ public List findItemsByOwner(final Long ownerId) { @Override @Transactional(readOnly = true) public List findItemsByText(final String text) { - log.info("Finding Items by text: '{}'", text); if (text == null || text.isEmpty()) { return Collections.emptyList(); @@ -216,7 +209,6 @@ public List findItemsByText(final String text) { @Override public CommentDto addComment(final Long itemId, final Long authorId, final CommentCreateDto commentDto) { - log.info("Add comment: itemId - '{}', authorId - '{}'", itemId, authorId); Item item = itemRepository.findById(itemId).orElseThrow( () -> new NotFoundException(ITEM_NOT_FOUND.formatted(itemId)) diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java index 3a788e3..eb67a0d 100644 --- a/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.item.dto; -import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,7 +11,6 @@ @Builder public class CommentCreateDto { - @NotBlank private String text; } diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java index c6ec24a..1a3f667 100644 --- a/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java @@ -1,8 +1,5 @@ package ru.practicum.shareit.item.dto; -import jakarta.annotation.Nullable; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,16 +9,12 @@ @Builder public class ItemCreateDto { - @NotBlank private String name; - @NotBlank private String description; - @NotNull private Boolean available; - @Nullable private Long requestId; } diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java index 5fbf136..624f604 100644 --- a/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -1,7 +1,5 @@ package ru.practicum.shareit.item.dto; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,13 +11,10 @@ public class ItemDto { private Long id; - @NotBlank private String name; - @NotBlank private String description; - @NotNull private Boolean available; } diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java index 874ba5f..aa49ced 100644 --- a/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.item.dto; -import jakarta.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,13 +11,10 @@ public class ItemUpdateDto { private Long id; - @Nullable private String name; - @Nullable private String description; - @Nullable private Boolean available; } From 0def63a75c05daf7eac97ae22235ebae2479d617 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Sat, 15 Mar 2025 02:07:49 +0300 Subject: [PATCH 05/17] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=D0=B9.=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compose.yaml | 2 +- .../shareit/booking/BookingClient.java | 41 +++++--------- .../shareit/booking/BookingController.java | 21 ++------ .../shareit/exception/ErrorHandler.java | 20 +++++++ .../shareit/exception/ErrorResponse.java | 12 +++++ .../ru/practicum/shareit/item/ItemClient.java | 15 ++++-- .../shareit/item/ItemController.java | 3 -- .../shareit/item/dto/ItemCreateDto.java | 2 + .../shareit/item/dto/ItemUpdateDto.java | 4 +- .../shareit/request/ItemRequestClient.java | 53 ++++++++++++++++++ .../request/ItemRequestController.java | 49 +++++++++++++++++ .../request/dto/ItemRequestCreateDto.java | 18 +++++++ .../ru/practicum/shareit/user/UserClient.java | 54 +++++++++++++++++++ .../shareit/user/UserController.java | 49 +++++++++++++++++ .../shareit/user/dto/UserCreateDto.java | 23 ++++++++ .../shareit/user/dto/UserUpdateDto.java | 26 +++++++++ .../shareit/booking/BookingController.java | 14 ++--- .../shareit/booking/BookingService.java | 40 +++++++------- .../contracts/BookingRepositoryInterface.java | 21 ++++---- .../contracts/BookingServiceInterface.java | 4 +- .../shareit/exception/ErrorHandler.java | 16 +++--- .../practicum/shareit/item/ItemService.java | 10 +++- .../request/ItemRequestController.java | 11 ++-- .../shareit/request/ItemRequestService.java | 15 +++++- .../ItemRequestRepositoryInterface.java | 2 - .../request/dto/ItemRequestCreateDto.java | 4 +- .../shareit/user/UserController.java | 5 +- .../shareit/user/dto/UserCreateDto.java | 5 -- .../shareit/user/dto/UserUpdateDto.java | 6 --- .../src/main/resources/application.properties | 1 + 30 files changed, 411 insertions(+), 135 deletions(-) create mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/UserClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/UserController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java diff --git a/compose.yaml b/compose.yaml index 83dfc00..381d778 100644 --- a/compose.yaml +++ b/compose.yaml @@ -27,7 +27,7 @@ services: image: postgres:16.1 container_name: postgres ports: - - "6541:5432" + - "5432:5432" environment: - POSTGRES_PASSWORD=shareit - POSTGRES_USER=shareit diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java index aafcd04..e30966a 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -1,8 +1,6 @@ package ru.practicum.shareit.booking; -import java.util.List; -import java.util.Map; - +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; @@ -15,6 +13,7 @@ import ru.practicum.shareit.booking.dto.BookingState; import ru.practicum.shareit.client.BaseClient; +@Slf4j @Service public class BookingClient extends BaseClient { @@ -35,46 +34,30 @@ public BookingClient( public ResponseEntity getBookings( long userId, - BookingState state, - Integer from, - Integer size + BookingState state ) { - Map parameters = Map.of( - "state", state.name(), - "from", from, - "size", size - ); - return get("?state={state}&from={from}&size={size}", userId, parameters); + log.info("Get bookings by user: {}, state: {}", userId, state); + return get("?state=" + state, userId); } public ResponseEntity bookItem(long userId, BookItemRequestDto requestDto) { + log.info("Booking by user {} and request: {}", userId, requestDto); return post("", userId, requestDto); } public ResponseEntity approve(Long bookingId, Long ownerId, boolean approved) { - Map parameters = Map.of( - "bookingId", bookingId, - "approved", approved - ); - return post("/{bookingId}?approved={approved}", ownerId, parameters); + log.info("Approve booking by bookingId: {}, ownerId: {}", bookingId, ownerId); + return patch("/" + bookingId + "?approved=" + approved, ownerId, null, null); } public ResponseEntity getBooking(long userId, Long bookingId) { + log.info("Get booking by user {} and bookingId: {}", userId, bookingId); return get("/" + bookingId, userId); } - public ResponseEntity getByOwnerAndState( - Long ownerId, BookingState state, - Integer from, - Integer size - ) { - Map parameters = Map.of( - "state", state.name(), - "from", from, - "size", size - ); - return get("/owner?state={state}&from={from}&size={size}", ownerId, parameters); + public ResponseEntity getByOwnerAndState(Long ownerId, BookingState state) { + log.info("Get booking by owner and state: {}", state); + return get("/owner?state=" + state, ownerId); } - } diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java index 73ac9ef..f0b79d2 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -13,36 +13,28 @@ import org.springframework.web.bind.annotation.RequestParam; import jakarta.validation.Valid; -import jakarta.validation.constraints.Positive; -import jakarta.validation.constraints.PositiveOrZero; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import ru.practicum.shareit.booking.dto.BookItemRequestDto; import ru.practicum.shareit.booking.dto.BookingState; @Controller @RequestMapping(path = "/bookings") @RequiredArgsConstructor -@Slf4j @Validated public class BookingController { private static final String HEADER_USER_ID = "X-Sharer-User-Id"; - private static final String LOG_GET_LIST_BY_PARAMS = "Get booking with state {}, userId={}, from={}, size={}"; private final BookingClient bookingClient; @GetMapping public ResponseEntity getBookings( @RequestHeader(HEADER_USER_ID) long userId, - @RequestParam(name = "state", defaultValue = "all") String stateParam, - @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, - @Positive @RequestParam(name = "size", defaultValue = "10") Integer size + @RequestParam(name = "state", defaultValue = "all") String stateParam ) { BookingState state = getBookingState(stateParam); - log.info(LOG_GET_LIST_BY_PARAMS, stateParam, userId, from, size); - return bookingClient.getBookings(userId, state, from, size); + return bookingClient.getBookings(userId, state); } @PostMapping @@ -50,7 +42,6 @@ public ResponseEntity bookItem( @RequestHeader(HEADER_USER_ID) long userId, @RequestBody @Valid BookItemRequestDto requestDto ) { - log.info("Creating booking {}, userId={}", requestDto, userId); return bookingClient.bookItem(userId, requestDto); } @@ -68,21 +59,17 @@ public ResponseEntity getBooking( @RequestHeader(HEADER_USER_ID) long userId, @PathVariable Long bookingId ) { - log.info("Get booking {}, userId={}", bookingId, userId); return bookingClient.getBooking(userId, bookingId); } @GetMapping("/owner") public ResponseEntity getByOwnerAndState( @RequestParam(required = false, defaultValue = "all") String stateParam, - @RequestHeader(name = HEADER_USER_ID) Long ownerId, - @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, - @Positive @RequestParam(name = "size", defaultValue = "10") Integer size + @RequestHeader(name = HEADER_USER_ID) Long ownerId ) { BookingState state = getBookingState(stateParam); - log.info(LOG_GET_LIST_BY_PARAMS, stateParam, ownerId, from, size); - return bookingClient.getByOwnerAndState(ownerId, state, from, size); + return bookingClient.getByOwnerAndState(ownerId, state); } private static BookingState getBookingState(String stateParam) { diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java new file mode 100644 index 0000000..e0c8449 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleOtherException(final Throwable e) { + log.error("Gateway error 500: " + e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java new file mode 100644 index 0000000..53517ae --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ErrorResponse { + + private final String error; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java index 12b015f..fc531dc 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -1,6 +1,6 @@ package ru.practicum.shareit.item; -import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; @@ -15,6 +15,7 @@ import java.util.Map; +@Slf4j @Service public class ItemClient extends BaseClient { @@ -33,19 +34,23 @@ public ItemClient( ); } - public ResponseEntity create(@Valid ItemCreateDto dto, Long userId) { + public ResponseEntity create(ItemCreateDto dto, Long userId) { + log.info("Create item {} and user {}", dto, userId); return post("", userId, dto); } - public ResponseEntity update(@Valid ItemUpdateDto dto, Long itemId, Long userId) { + public ResponseEntity update(ItemUpdateDto dto, Long itemId, Long userId) { + log.info("Update item {} and user {}", dto, userId); return patch("/" + itemId, userId, dto); } public ResponseEntity findItemById(Long itemId, Long userId) { + log.info("Find item {} and user {}", itemId, userId); return get("/" + itemId, userId); } public ResponseEntity findItemsByOwner(Long ownerId) { + log.info("Find items by owner {}", ownerId); return get("/owner", ownerId); } @@ -54,8 +59,8 @@ public ResponseEntity findItemsByText(String text) { return get("/search", null, params); } - public ResponseEntity addComment(Long itemId, Long authorId, @Valid CommentCreateDto commentDto) { - return post("/" + itemId, authorId, commentDto); + public ResponseEntity addComment(Long itemId, Long authorId, CommentCreateDto commentDto) { + return post("/" + itemId + "/comment", authorId, commentDto); } } diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java index 1137d2f..df6f290 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -2,7 +2,6 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.validation.annotation.Validated; @@ -21,7 +20,6 @@ @Controller @RequestMapping(path = "/items") @RequiredArgsConstructor -@Slf4j @Validated public class ItemController { @@ -43,7 +41,6 @@ public ResponseEntity update( @PathVariable Long itemId, @RequestHeader(name = HEADER_USER_ID) Long userId ) { - return itemClient.update(dto, itemId, userId); } diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java index c6ec24a..7627076 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java @@ -6,9 +6,11 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @AllArgsConstructor +@NoArgsConstructor @Builder public class ItemCreateDto { diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java index 874ba5f..65063f3 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java @@ -4,14 +4,14 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @AllArgsConstructor +@NoArgsConstructor @Builder public class ItemUpdateDto { - private Long id; - @Nullable private String name; diff --git a/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.java b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.java new file mode 100644 index 0000000..062bdc1 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.java @@ -0,0 +1,53 @@ +package ru.practicum.shareit.request; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; + +@Slf4j +@Service +public class ItemRequestClient extends BaseClient { + + private static final String API_PREFIX = "/requests"; + + @Autowired + public ItemRequestClient( + @Value("${shareit-server.url}") String serverUrl, + RestTemplateBuilder builder + ) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity create(Long userId, ItemRequestCreateDto itemRequestCreateDto) { + log.info("Create itemRequest for userId={}, dto: {}", userId, itemRequestCreateDto); + return post("", userId, itemRequestCreateDto); + } + + public ResponseEntity getListByUser(Long userId) { + log.info("Get itemRequests for userId={}", userId); + return get("", userId); + } + + public ResponseEntity getList(Long userId) { + log.info("Get all itemRequests for userId={}", userId); + return get("/all", userId); + } + + public ResponseEntity getById(Long requestId) { + log.info("Get itemRequest for requestId={}", requestId); + return get("/" + requestId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java new file mode 100644 index 0000000..9473f84 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -0,0 +1,49 @@ +package ru.practicum.shareit.request; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; + +@Controller +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +@Validated +public class ItemRequestController { + + private static final String HEADER_USER_ID = "X-Sharer-User-Id"; + + private final ItemRequestClient itemRequestClient; + + @PostMapping + public ResponseEntity createItem( + @RequestHeader(HEADER_USER_ID) Long userId, + @Valid @RequestBody ItemRequestCreateDto itemRequestCreateDto + ) { + return itemRequestClient.create(userId, itemRequestCreateDto); + } + + @GetMapping + public ResponseEntity getListByUser(@RequestHeader(name = HEADER_USER_ID) Long userId) { + return itemRequestClient.getListByUser(userId); + } + + @GetMapping("/all") + public ResponseEntity getList(@RequestHeader(name = HEADER_USER_ID) Long userId) { + return itemRequestClient.getList(userId); + } + + @GetMapping("/{requestId}") + public ResponseEntity getById(@PathVariable Long requestId) { + return itemRequestClient.getById(requestId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java new file mode 100644 index 0000000..17be3b1 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.request.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ItemRequestCreateDto { + + @NotBlank + private String description; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java new file mode 100644 index 0000000..dbc7f38 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -0,0 +1,54 @@ +package ru.practicum.shareit.user; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +@Slf4j +@Service +public class UserClient extends BaseClient { + + private static final String API_PREFIX = "/users"; + + @Autowired + public UserClient( + @Value("${shareit-server.url}") String serverUrl, + RestTemplateBuilder builder + ) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity create(UserCreateDto dto) { + log.info("Create request for user={}", dto); + return post("", dto); + } + + public ResponseEntity update(Long userId, UserUpdateDto dto) { + log.info("Update request for user={}, dto: {}", userId, dto); + return patch("/" + userId, dto); + } + + public ResponseEntity deleteById(Long userId) { + log.info("Delete request for user={}", userId); + return delete("/" + userId); + } + + public ResponseEntity findById(Long userId) { + log.info("Find request for user={}", userId); + return get("/" + userId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserController.java b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java new file mode 100644 index 0000000..c2f9c27 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,49 @@ +package ru.practicum.shareit.user; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +@Controller +@RequestMapping(path = "/users") +@RequiredArgsConstructor +@Validated +public class UserController { + + private final UserClient userClient; + + @PostMapping + public ResponseEntity create(@Valid @RequestBody UserCreateDto dto) { + return userClient.create(dto); + } + + @PatchMapping("/{userId}") + public ResponseEntity update( + @Valid @RequestBody UserUpdateDto dto, + @PathVariable Long userId + ) { + return userClient.update(userId, dto); + } + + @DeleteMapping("/{userId}") + public ResponseEntity delete(@PathVariable Long userId) { + return userClient.deleteById(userId); + } + + @GetMapping("/{userId}") + public ResponseEntity findById(@PathVariable Long userId) { + return userClient.findById(userId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java new file mode 100644 index 0000000..68c8ab0 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java @@ -0,0 +1,23 @@ +package ru.practicum.shareit.user.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UserCreateDto { + + @NotBlank + private String name; + + @Email + @NotBlank + private String email; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java new file mode 100644 index 0000000..e86023c --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java @@ -0,0 +1,26 @@ +package ru.practicum.shareit.user.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.annotation.Nullable; +import jakarta.validation.constraints.Email; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UserUpdateDto { + + @Nullable + private Long id; + + @Nullable + private String name; + + @Email + @Nullable + private String email; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java index 3f4bc56..35b5bc9 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -56,26 +56,20 @@ public BookingDto getById( @GetMapping public List getByBookerAndState( final @RequestParam(required = false) BookingState state, - final @RequestHeader(name = HEADER_USER_ID) Long bookerId, - final @RequestParam(value = "from", defaultValue = "0") Integer from, - final @RequestParam(value = "size", defaultValue = "10") Integer size + final @RequestHeader(name = HEADER_USER_ID) Long bookerId ) { return bookingService.getByBookerAndState( bookerId, - state == null ? BookingState.ALL : state, - from, - size + state == null ? BookingState.ALL : state ); } @GetMapping("/owner") public List getByOwnerAndState( final @RequestParam(required = false) BookingState state, - final @RequestHeader(name = HEADER_USER_ID) Long ownerId, - final @RequestParam(value = "from", defaultValue = "0") Integer from, - final @RequestParam(value = "size", defaultValue = "10") Integer size + final @RequestHeader(name = HEADER_USER_ID) Long ownerId ) { - return bookingService.getByOwnerAndState(ownerId, state, from, size); + return bookingService.getByOwnerAndState(ownerId, state); } } diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingService.java b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java index 2a16a93..61d4dda 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingService.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java @@ -1,8 +1,7 @@ package ru.practicum.shareit.booking; import lombok.AllArgsConstructor; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -25,6 +24,7 @@ import java.time.LocalDateTime; import java.util.List; +@Slf4j @Service @AllArgsConstructor @Transactional(readOnly = true) @@ -41,6 +41,7 @@ public class BookingService implements BookingServiceInterface { @Override @Transactional public BookingDto create(final BookingCreateDto bookingDto) { + log.info("Create booking={}", bookingDto); Item item = itemRepository.findById(bookingDto.getItemId()).orElseThrow( () -> new NotFoundException("Item with id='%d' not found".formatted(bookingDto.getItemId())) @@ -70,6 +71,7 @@ public BookingDto create(final BookingCreateDto bookingDto) { @Override @Transactional public BookingDto approve(final Long id, final Long ownerId, final boolean approved) { + log.info("Approve booking={}, owner: {}", id, ownerId); Booking booking = bookingRepository.findById(id).orElseThrow( () -> new NotFoundException(BOOKING_NOT_FOUND.formatted(id)) @@ -92,6 +94,7 @@ public BookingDto approve(final Long id, final Long ownerId, final boolean appro @Override public BookingDto getById(final Long id, final Long userId) { + log.info("Get booking={}, userId: {}", id, userId); Booking booking = bookingRepository.findById(id).orElseThrow( () -> new NotFoundException(BOOKING_NOT_FOUND.formatted(id)) @@ -108,26 +111,25 @@ public BookingDto getById(final Long id, final Long userId) { } @Override - public List getByBookerAndState(final Long bookerId, final BookingState state, final Integer from, final Integer size) { + public List getByBookerAndState(final Long bookerId, final BookingState state) { + log.info("Get bookings by booker={}, state: {}", bookerId, state); User booker = userRepository.findById(bookerId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(bookerId)) ); - Pageable pageable = PageRequest.of(from, size, sort); - List list = switch (state) { - case WAITING -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.WAITING, pageable); - case REJECTED -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.REJECTED, pageable); + case WAITING -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.WAITING, sort); + case REJECTED -> bookingRepository.findAllByBookerAndStatus(booker, BookingStatus.REJECTED, sort); case CURRENT -> bookingRepository.findAllByBookerAndStartBeforeAndEndAfter( booker, LocalDateTime.now(), LocalDateTime.now(), - pageable + sort ); - case PAST -> bookingRepository.findAllByBookerAndEndBefore(booker, LocalDateTime.now(), pageable); - case FUTURE -> bookingRepository.findAllByBookerAndStartAfter(booker, LocalDateTime.now(), pageable); - default -> bookingRepository.findAllByBooker(booker, pageable); + case PAST -> bookingRepository.findAllByBookerAndEndBefore(booker, LocalDateTime.now(), sort); + case FUTURE -> bookingRepository.findAllByBookerAndStartAfter(booker, LocalDateTime.now(), sort); + default -> bookingRepository.findAllByBooker(booker, sort); }; return list @@ -137,26 +139,24 @@ public List getByBookerAndState(final Long bookerId, final BookingSt } @Override - public List getByOwnerAndState(final Long ownerId, final BookingState state, final Integer from, final Integer size) { + public List getByOwnerAndState(final Long ownerId, final BookingState state) { User owner = userRepository.findById(ownerId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(ownerId)) ); - Pageable pageable = PageRequest.of(from, size, sort); - List list = switch (state) { - case WAITING -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.WAITING, pageable); - case REJECTED -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.REJECTED, pageable); + case WAITING -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.WAITING, sort); + case REJECTED -> bookingRepository.findAllByItemOwnerAndStatus(owner, BookingStatus.REJECTED, sort); case CURRENT -> bookingRepository.findAllByItemOwnerAndStartBeforeAndEndAfter( owner, LocalDateTime.now(), LocalDateTime.now(), - pageable + sort ); - case PAST -> bookingRepository.findAllByItemOwnerAndEndBefore(owner, LocalDateTime.now(), pageable); - case FUTURE -> bookingRepository.findAllByItemOwnerAndStartAfter(owner, LocalDateTime.now(), pageable); - default -> bookingRepository.findAllByItemOwner(owner, pageable); + case PAST -> bookingRepository.findAllByItemOwnerAndEndBefore(owner, LocalDateTime.now(), sort); + case FUTURE -> bookingRepository.findAllByItemOwnerAndStartAfter(owner, LocalDateTime.now(), sort); + default -> bookingRepository.findAllByItemOwner(owner, sort); }; return list diff --git a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java index 1dbaff6..aca251d 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java +++ b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingRepositoryInterface.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.booking.contracts; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; @@ -13,35 +12,35 @@ public interface BookingRepositoryInterface extends JpaRepository { - List findAllByBookerAndStatus(User booker, BookingStatus bookingStatus, Pageable pageable); + List findAllByBookerAndStatus(User booker, BookingStatus bookingStatus, Sort sort); - List findAllByBooker(User booker, Pageable pageable); + List findAllByBooker(User booker, Sort sort); List findAllByBookerAndStartBeforeAndEndAfter( User booker, LocalDateTime start, LocalDateTime end, - Pageable pageable + Sort sort ); - List findAllByBookerAndEndBefore(User booker, LocalDateTime start, Pageable pageable); + List findAllByBookerAndEndBefore(User booker, LocalDateTime start, Sort sort); - List findAllByBookerAndStartAfter(User booker, LocalDateTime start, Pageable pageable); + List findAllByBookerAndStartAfter(User booker, LocalDateTime start, Sort sort); - List findAllByItemOwnerAndStatus(User owner, BookingStatus bookingStatus, Pageable pageable); + List findAllByItemOwnerAndStatus(User owner, BookingStatus bookingStatus, Sort sort); - List findAllByItemOwner(User owner, Pageable pageable); + List findAllByItemOwner(User owner, Sort sort); List findAllByItemOwnerAndStartBeforeAndEndAfter( User owner, LocalDateTime start, LocalDateTime end, - Pageable pageable + Sort sort ); - List findAllByItemOwnerAndEndBefore(User owner, LocalDateTime start, Pageable pageable); + List findAllByItemOwnerAndEndBefore(User owner, LocalDateTime start, Sort sort); - List findAllByItemOwnerAndStartAfter(User owner, LocalDateTime start, Pageable pageable); + List findAllByItemOwnerAndStartAfter(User owner, LocalDateTime start, Sort sort); boolean existsByBookerIdAndItemIdAndStatusEqualsAndEndIsBefore(Long bookerId, Long itemId, BookingStatus bookingStatus, LocalDateTime end); diff --git a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java index 6999a07..73837f2 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java +++ b/server/src/main/java/ru/practicum/shareit/booking/contracts/BookingServiceInterface.java @@ -14,8 +14,8 @@ public interface BookingServiceInterface { BookingDto getById(Long id, Long userId); - List getByBookerAndState(Long bookerId, BookingState state, Integer from, Integer size); + List getByBookerAndState(Long bookerId, BookingState state); - List getByOwnerAndState(Long ownerId, BookingState state, Integer from, Integer size); + List getByOwnerAndState(Long ownerId, BookingState state); } diff --git a/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java index cf7b905..4d99124 100644 --- a/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -10,10 +10,12 @@ @RestControllerAdvice public class ErrorHandler { + private static final String ERROR_LOG_MESSAGE = "Server error ({}): {}"; + @ExceptionHandler @ResponseStatus(HttpStatus.NOT_FOUND) public ErrorResponse handleNotFoundException(final NotFoundException e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.NOT_FOUND, e.getMessage()); return new ErrorResponse(e.getMessage()); } @@ -21,7 +23,7 @@ public ErrorResponse handleNotFoundException(final NotFoundException e) { @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleNotFoundException(final EmptyIdException e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.BAD_REQUEST, e.getMessage()); return new ErrorResponse(e.getMessage()); } @@ -29,7 +31,7 @@ public ErrorResponse handleNotFoundException(final EmptyIdException e) { @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleNotFoundException(final InvalidOwnerException e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.BAD_REQUEST, e.getMessage()); return new ErrorResponse(e.getMessage()); } @@ -37,7 +39,7 @@ public ErrorResponse handleNotFoundException(final InvalidOwnerException e) { @ExceptionHandler @ResponseStatus(HttpStatus.CONFLICT) public ErrorResponse handleNotFoundException(final NotUniqueEmailException e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.CONFLICT, e.getMessage()); return new ErrorResponse(e.getMessage()); } @@ -45,7 +47,7 @@ public ErrorResponse handleNotFoundException(final NotUniqueEmailException e) { @ExceptionHandler @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ErrorResponse handleOtherException(final Throwable e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); return new ErrorResponse(e.getMessage()); } @@ -53,7 +55,7 @@ public ErrorResponse handleOtherException(final Throwable e) { @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleOtherException(final InvalidBookingStatusException e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.BAD_REQUEST, e.getMessage()); return new ErrorResponse(e.getMessage()); } @@ -61,7 +63,7 @@ public ErrorResponse handleOtherException(final InvalidBookingStatusException e) @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleOtherException(final UserDoesNotHaveBookedItem e) { - log.warn(e.getMessage()); + log.warn(ERROR_LOG_MESSAGE, HttpStatus.BAD_REQUEST, e.getMessage()); return new ErrorResponse(e.getMessage()); } diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java index ac40a4f..10666fe 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -1,6 +1,7 @@ package ru.practicum.shareit.item; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,6 +39,7 @@ import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; +@Slf4j @Service @AllArgsConstructor public class ItemService implements ItemServiceInterface { @@ -56,6 +58,7 @@ public class ItemService implements ItemServiceInterface { @Override public ItemDto create(final ItemCreateDto itemDto, final Long userId) { + log.info("Create item {} by user {}", itemDto, userId); User user = userRepository.findById(userId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(userId)) @@ -63,7 +66,7 @@ public ItemDto create(final ItemCreateDto itemDto, final Long userId) { ItemRequest itemRequest = null; - if (itemDto.getRequestId() == null) { + if (itemDto.getRequestId() != null) { itemRequest = itemRequestRepositoryInterface.findById(itemDto.getRequestId()).orElseThrow( () -> new NotFoundException("Item request with id='%d' not found".formatted(itemDto.getRequestId())) ); @@ -86,6 +89,7 @@ public ItemDto create(final ItemCreateDto itemDto, final Long userId) { @Override public ItemDto update(final ItemUpdateDto itemDto, final Long userId) { + log.info("Update item {} by user {}", itemDto, userId); if (itemDto.getId() == null) { throw new EmptyIdException("Item id is empty"); @@ -123,6 +127,7 @@ public ItemDto update(final ItemUpdateDto itemDto, final Long userId) { @Override @Transactional(readOnly = true) public ItemInfoDto findItemById(Long itemId, Long userId) { + log.info("Find item by id {} and user {}", itemId, userId); Item item = itemRepository.findById(itemId).orElseThrow( () -> new NotFoundException(ITEM_NOT_FOUND.formatted(itemId)) @@ -151,6 +156,7 @@ public ItemInfoDto findItemById(Long itemId, Long userId) { @Override @Transactional(readOnly = true) public List findItemsByOwner(final Long ownerId) { + log.info("Find items by owner {}", ownerId); User user = userRepository.findById(ownerId).orElseThrow( () -> new NotFoundException(USER_NOT_FOUND.formatted(ownerId)) @@ -195,6 +201,7 @@ public List findItemsByOwner(final Long ownerId) { @Override @Transactional(readOnly = true) public List findItemsByText(final String text) { + log.info("Find items by text {}", text); if (text == null || text.isEmpty()) { return Collections.emptyList(); @@ -209,6 +216,7 @@ public List findItemsByText(final String text) { @Override public CommentDto addComment(final Long itemId, final Long authorId, final CommentCreateDto commentDto) { + log.info("Add comment {} for item {} by user {}", commentDto, itemId, authorId); Item item = itemRepository.findById(itemId).orElseThrow( () -> new NotFoundException(ITEM_NOT_FOUND.formatted(itemId)) diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java index d67778e..95a290b 100644 --- a/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.request; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -20,14 +19,14 @@ @RequiredArgsConstructor public class ItemRequestController { - public static final String X_SHARER_USER_ID = "X-Sharer-User-Id"; + private static final String HEADER_USER_ID = "X-Sharer-User-Id"; private final ItemRequestServiceInterface itemRequestService; @PostMapping public ItemRequestResponseDto createItem( - final @RequestHeader(X_SHARER_USER_ID) Long userId, - final @Valid @RequestBody ItemRequestCreateDto itemRequestCreateDto + final @RequestHeader(HEADER_USER_ID) Long userId, + final @RequestBody ItemRequestCreateDto itemRequestCreateDto ) { itemRequestCreateDto.setUserId(userId); @@ -35,13 +34,13 @@ public ItemRequestResponseDto createItem( } @GetMapping - public List getListByUser(final @RequestHeader(name = X_SHARER_USER_ID) Long userId) + public List getListByUser(final @RequestHeader(name = HEADER_USER_ID) Long userId) { return itemRequestService.getListByUser(userId); } @GetMapping("/all") - public List getList(final @RequestHeader(name = X_SHARER_USER_ID) Long userId) + public List getList(final @RequestHeader(name = HEADER_USER_ID) Long userId) { return itemRequestService.getList(userId); } diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java index e90fce7..ca96a87 100644 --- a/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java @@ -1,6 +1,8 @@ package ru.practicum.shareit.request; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; @@ -15,6 +17,7 @@ import java.time.LocalDateTime; import java.util.List; +@Slf4j @Service @RequiredArgsConstructor public class ItemRequestService implements ItemRequestServiceInterface { @@ -24,13 +27,15 @@ public class ItemRequestService implements ItemRequestServiceInterface { @Override public ItemRequestResponseDto create(final ItemRequestCreateDto itemRequestCreateDto) { + log.info("Create itemRequest request: {}", itemRequestCreateDto); + User user = userRepository.findById(itemRequestCreateDto.getUserId()).orElseThrow( () -> new NotFoundException("User not found with id: " + itemRequestCreateDto.getUserId()) ); ItemRequest itemRequest = new ItemRequest(); itemRequest.setRequestor(user); - itemRequest.setDescription(itemRequestCreateDto.getText()); + itemRequest.setDescription(itemRequestCreateDto.getDescription()); itemRequest.setCreated(LocalDateTime.now()); itemRequestRepository.save(itemRequest); @@ -40,6 +45,8 @@ public ItemRequestResponseDto create(final ItemRequestCreateDto itemRequestCreat @Override public List getListByUser(final Long userId) { + log.info("Get itemRequests by user: {}", userId); + User user = userRepository.findById(userId).orElseThrow( () -> new NotFoundException("User not found with id: " + userId) ); @@ -52,9 +59,11 @@ public List getListByUser(final Long userId) { @Override public List getList(final Long userId) { + log.info("Get itemRequests with user: {}", userId); + return ( userId == null - ? itemRequestRepository.allOrderByCreatedDesc() + ? itemRequestRepository.findAll(Sort.by(Sort.Direction.DESC, "created")) : itemRequestRepository.findAllByRequestor_IdIsNotOrderByCreatedDesc(userId) ).stream() .map(ItemRequestMapper::toItemRequestResponseDto) @@ -63,6 +72,8 @@ public List getList(final Long userId) { @Override public ItemRequestResponseDto getById(final Long id) { + log.info("Get itemRequest by id: {}", id); + return ItemRequestMapper.toItemRequestResponseDto(itemRequestRepository.findById(id).orElseThrow( () -> new NotFoundException("Item request with id: " + id) )); diff --git a/server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java index 68f97e8..afcc920 100644 --- a/server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java +++ b/server/src/main/java/ru/practicum/shareit/request/contracts/ItemRequestRepositoryInterface.java @@ -9,8 +9,6 @@ public interface ItemRequestRepositoryInterface extends JpaRepository findAllByRequestor_IdOrderByCreatedDesc(Long id); - List allOrderByCreatedDesc(); - List findAllByRequestor_IdIsNotOrderByCreatedDesc(Long id); } diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java index 1dea194..e353ac5 100644 --- a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java +++ b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.request.dto; -import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,8 +11,7 @@ @Builder public class ItemRequestCreateDto { - @NotBlank - private String text; + private String description; private Long userId; } diff --git a/server/src/main/java/ru/practicum/shareit/user/UserController.java b/server/src/main/java/ru/practicum/shareit/user/UserController.java index 9ddb2d2..17da548 100644 --- a/server/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserController.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.user; -import jakarta.validation.Valid; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -23,13 +22,13 @@ public class UserController { private final UserServiceInterface userService; @PostMapping - public UserDto create(final @Valid @RequestBody UserCreateDto dto) { + public UserDto create(final @RequestBody UserCreateDto dto) { return userService.create(dto); } @PatchMapping("/{userId}") public UserDto update( - final @Valid @RequestBody UserUpdateDto dto, + final @RequestBody UserUpdateDto dto, final @PathVariable Long userId ) { dto.setId(userId); diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java index 68c8ab0..8f1e459 100644 --- a/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java @@ -1,8 +1,6 @@ package ru.practicum.shareit.user.dto; import com.fasterxml.jackson.annotation.JsonInclude; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,11 +11,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public class UserCreateDto { - @NotBlank private String name; - @Email - @NotBlank private String email; } diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java index e86023c..3b47cff 100644 --- a/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java @@ -1,8 +1,6 @@ package ru.practicum.shareit.user.dto; import com.fasterxml.jackson.annotation.JsonInclude; -import jakarta.annotation.Nullable; -import jakarta.validation.constraints.Email; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,14 +11,10 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public class UserUpdateDto { - @Nullable private Long id; - @Nullable private String name; - @Email - @Nullable private String email; } diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 6a96840..4d33e8d 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -2,6 +2,7 @@ server.port=9090 spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.show_sql=true spring.sql.init.mode=always #--- From b00f210f6589dc6e10f771a7727d18391b1e6238 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Sun, 16 Mar 2025 00:57:00 +0300 Subject: [PATCH 06/17] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=D0=B9.=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contracts/ItemRepositoryInterface.java | 2 ++ .../shareit/request/ItemRequestService.java | 17 ++++++++++++----- .../request/dto/ItemRequestResponseDto.java | 6 ++++++ .../request/mapper/ItemRequestMapper.java | 18 ++++++++++++++++-- .../src/main/resources/application.properties | 2 +- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java b/server/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java index 9013bc0..efa01fa 100644 --- a/server/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java +++ b/server/src/main/java/ru/practicum/shareit/item/contracts/ItemRepositoryInterface.java @@ -21,4 +21,6 @@ public interface ItemRepositoryInterface extends JpaRepository { ) List findAllByText(String text); + List findAllByRequest_IdOrderByIdDesc(Long id); + } diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java index ca96a87..7611f6f 100644 --- a/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java @@ -5,6 +5,7 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.contracts.ItemRepositoryInterface; import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; import ru.practicum.shareit.request.contracts.ItemRequestServiceInterface; import ru.practicum.shareit.request.dto.ItemRequestCreateDto; @@ -24,6 +25,7 @@ public class ItemRequestService implements ItemRequestServiceInterface { private final ItemRequestRepositoryInterface itemRequestRepository; private final UserRepositoryInterface userRepository; + private final ItemRepositoryInterface itemRepository; @Override public ItemRequestResponseDto create(final ItemRequestCreateDto itemRequestCreateDto) { @@ -40,7 +42,7 @@ public ItemRequestResponseDto create(final ItemRequestCreateDto itemRequestCreat itemRequestRepository.save(itemRequest); - return ItemRequestMapper.toItemRequestResponseDto(itemRequest); + return ItemRequestMapper.toItemRequestResponseDto(itemRequest, null); } @Override @@ -53,7 +55,7 @@ public List getListByUser(final Long userId) { return itemRequestRepository.findAllByRequestor_IdOrderByCreatedDesc(user.getId()) .stream() - .map(ItemRequestMapper::toItemRequestResponseDto) + .map(itemRequest -> ItemRequestMapper.toItemRequestResponseDto(itemRequest, null)) .toList(); } @@ -66,7 +68,7 @@ public List getList(final Long userId) { ? itemRequestRepository.findAll(Sort.by(Sort.Direction.DESC, "created")) : itemRequestRepository.findAllByRequestor_IdIsNotOrderByCreatedDesc(userId) ).stream() - .map(ItemRequestMapper::toItemRequestResponseDto) + .map(itemRequest -> ItemRequestMapper.toItemRequestResponseDto(itemRequest, null)) .toList(); } @@ -74,9 +76,14 @@ public List getList(final Long userId) { public ItemRequestResponseDto getById(final Long id) { log.info("Get itemRequest by id: {}", id); - return ItemRequestMapper.toItemRequestResponseDto(itemRequestRepository.findById(id).orElseThrow( + ItemRequest itemRequest = itemRequestRepository.findById(id).orElseThrow( () -> new NotFoundException("Item request with id: " + id) - )); + ); + + return ItemRequestMapper.toItemRequestResponseDto( + itemRequest, + itemRepository.findAllByRequest_IdOrderByIdDesc(itemRequest.getId()) + ); } } diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java index d0b322b..d7de2d6 100644 --- a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java +++ b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestResponseDto.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import java.time.LocalDateTime; +import java.util.List; @Data @NoArgsConstructor @@ -18,4 +19,9 @@ public class ItemRequestResponseDto { private String description; private LocalDateTime created; + + private List items; + + public record ItemDto(Long id, String name) { + } } diff --git a/server/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java index 67526b1..cd45e71 100644 --- a/server/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java +++ b/server/src/main/java/ru/practicum/shareit/request/mapper/ItemRequestMapper.java @@ -1,19 +1,33 @@ package ru.practicum.shareit.request.mapper; +import ru.practicum.shareit.item.model.Item; import ru.practicum.shareit.request.dto.ItemRequestResponseDto; import ru.practicum.shareit.request.model.ItemRequest; +import java.util.ArrayList; +import java.util.List; public class ItemRequestMapper { private ItemRequestMapper() { } - public static ItemRequestResponseDto toItemRequestResponseDto(ItemRequest itemRequest) { + public static ItemRequestResponseDto toItemRequestResponseDto(ItemRequest itemRequest, List items) { + List itemDtos = new ArrayList<>(); + + if (items != null) { + for (Item item : items) { + itemDtos.add( + new ItemRequestResponseDto.ItemDto(item.getId(), item.getName()) + ); + } + } + return new ItemRequestResponseDto( itemRequest.getId(), itemRequest.getDescription(), - itemRequest.getCreated() + itemRequest.getCreated(), + itemDtos ); } diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 4d33e8d..c1d43cb 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -2,7 +2,7 @@ server.port=9090 spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true -spring.jpa.properties.hibernate.show_sql=true +#spring.jpa.properties.hibernate.show_sql=true spring.sql.init.mode=always #--- From ccc775816964c41a13835b2d5bfb29f042691df7 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Sun, 16 Mar 2025 01:00:17 +0300 Subject: [PATCH 07/17] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=D0=B9.=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shareit/request/ItemRequestController.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java index 95a290b..4bb4ede 100644 --- a/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -34,20 +34,18 @@ public ItemRequestResponseDto createItem( } @GetMapping - public List getListByUser(final @RequestHeader(name = HEADER_USER_ID) Long userId) - { + public List getListByUser(final @RequestHeader(name = HEADER_USER_ID) Long userId) { return itemRequestService.getListByUser(userId); } @GetMapping("/all") - public List getList(final @RequestHeader(name = HEADER_USER_ID) Long userId) - { + public List getList(final @RequestHeader(name = HEADER_USER_ID) Long userId) { return itemRequestService.getList(userId); } @GetMapping("/{requestId}") - public ItemRequestResponseDto getById(final @PathVariable Long requestId) - { + public ItemRequestResponseDto getById(final @PathVariable Long requestId) { return itemRequestService.getById(requestId); } + } From 105c77a5a3ecbf373e33822206b5bc5fb89b99ca Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Mon, 17 Mar 2025 23:51:42 +0300 Subject: [PATCH 08/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compose.yaml => docker-compose.yml | 0 .../booking/dto/BookItemRequestDto.java | 2 + .../booking/validator/DateValidator.java | 22 +++ .../validator/StartDateIsBeforeEnd.java | 22 +++ .../practicum/shareit/item/ItemService.java | 3 +- .../shareit/item/ItemServiceTest.java | 183 ++++++++++++++++++ 6 files changed, 230 insertions(+), 2 deletions(-) rename compose.yaml => docker-compose.yml (100%) create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/validator/DateValidator.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/validator/StartDateIsBeforeEnd.java create mode 100644 server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java diff --git a/compose.yaml b/docker-compose.yml similarity index 100% rename from compose.yaml rename to docker-compose.yml diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java index 13935ce..c733f0f 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookItemRequestDto.java @@ -8,10 +8,12 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.validator.StartDateIsBeforeEnd; @Getter @NoArgsConstructor @AllArgsConstructor +@StartDateIsBeforeEnd public class BookItemRequestDto { private Long itemId; diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/validator/DateValidator.java b/gateway/src/main/java/ru/practicum/shareit/booking/validator/DateValidator.java new file mode 100644 index 0000000..8a29756 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/validator/DateValidator.java @@ -0,0 +1,22 @@ +package ru.practicum.shareit.booking.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import ru.practicum.shareit.booking.dto.BookItemRequestDto; + +import java.time.LocalDateTime; + +public class DateValidator + implements ConstraintValidator { + + @Override + public boolean isValid(BookItemRequestDto bookItemRequestDto, ConstraintValidatorContext constraintValidatorContext) { + LocalDateTime start = bookItemRequestDto.getStart(); + LocalDateTime end = bookItemRequestDto.getEnd(); + + return start != null + && end != null + && start.isBefore(end); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/validator/StartDateIsBeforeEnd.java b/gateway/src/main/java/ru/practicum/shareit/booking/validator/StartDateIsBeforeEnd.java new file mode 100644 index 0000000..f2b5162 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/validator/StartDateIsBeforeEnd.java @@ -0,0 +1,22 @@ +package ru.practicum.shareit.booking.validator; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Constraint(validatedBy = DateValidator.class) +public @interface StartDateIsBeforeEnd { + + String message() default "Start must be before end"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java index 10666fe..39d9d7a 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -34,7 +34,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; @@ -103,7 +102,7 @@ public ItemDto update(final ItemUpdateDto itemDto, final Long userId) { () -> new NotFoundException(ITEM_NOT_FOUND.formatted(itemDto.getId())) ); - if (!Objects.equals(item.getOwner(), user)) { + if (item.getOwner().equals(user)) { throw new InvalidOwnerException("Owner is not the same user"); } diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java b/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java new file mode 100644 index 0000000..c16158e --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java @@ -0,0 +1,183 @@ +package ru.practicum.shareit.item; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Sort; +import org.springframework.test.annotation.DirtiesContext; +import ru.practicum.shareit.booking.contracts.BookingRepositoryInterface; +import ru.practicum.shareit.item.contracts.CommentRepositoryInterface; +import ru.practicum.shareit.item.contracts.ItemRepositoryInterface; +import ru.practicum.shareit.item.dto.CommentCreateDto; +import ru.practicum.shareit.item.dto.ItemCreateDto; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.dto.ItemInfoDto; +import ru.practicum.shareit.item.dto.ItemUpdateDto; +import ru.practicum.shareit.item.model.Comment; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.contracts.UserRepositoryInterface; +import ru.practicum.shareit.user.model.User; + +import java.util.List; +import java.util.Optional; +import java.util.Random; + +@SpringBootTest +@ExtendWith(MockitoExtension.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class ItemServiceTest { + + @MockBean + BookingRepositoryInterface bookingRepository; + + @Autowired + ItemRepositoryInterface itemRepository; + + @Autowired + UserRepositoryInterface userRepository; + + @Autowired + CommentRepositoryInterface commentRepository; + + @Autowired + ItemService itemService; + + @Test + void create() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + Optional item = itemRepository.findById(itemRes.getId()); + + Assertions.assertTrue(item.isPresent()); + } + + @Test + void update() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + String itemNewName = "item new name"; + + itemService.update( + new ItemUpdateDto( + itemRes.getId(), + itemNewName, + itemRes.getDescription(), + itemRes.getAvailable() + ), + owner.getId() + ); + + Optional item = itemRepository.findById(itemRes.getId()); + + Assertions.assertTrue(item.isPresent()); + Assertions.assertEquals(itemNewName, item.get().getName()); + } + + @Test + void findItemById() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + User user = createUser(); + ItemInfoDto item = itemService.findItemById(itemRes.getId(), user.getId()); + + Assertions.assertNotNull(item); + Assertions.assertEquals(item.getId(), itemRes.getId()); + } + + @Test + void findItemsByOwner() { + User owner = createUser(); + int ownerItemsCount = new Random().nextInt(10); + + for (int i = 1; i <= ownerItemsCount; i++) { + ItemCreateDto itemCreateDto = makeItemCreateDto(); + itemService.create(itemCreateDto, owner.getId()); + } + + User user = createUser(); + int userItemsCount = new Random().nextInt(10); + + for (int i = 0; i <= userItemsCount; i++) { + ItemCreateDto itemCreateDto = makeItemCreateDto(); + itemService.create(itemCreateDto, user.getId()); + } + + List items = itemService.findItemsByOwner(owner.getId()); + + Assertions.assertEquals(ownerItemsCount, items.size()); + } + + @Test + void findItemsByText() { + String searchText = "text for search test"; + + ItemCreateDto itemCreateDto = makeItemCreateDto(); + itemCreateDto.setDescription(searchText); + User owner = createUser(); + + itemService.create(itemCreateDto, owner.getId()); + + List res = itemService.findItemsByText(searchText); + + Assertions.assertFalse(res.isEmpty()); + Assertions.assertEquals(searchText, res.getFirst().getDescription()); + + } + + @Test + void addComment() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + User user = createUser(); + String commentText = "test comment"; + + Mockito.when( + bookingRepository.existsByBookerIdAndItemIdAndStatusEqualsAndEndIsBefore(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(), Mockito.any()) + ).thenReturn(true); + + itemService.addComment( + itemRes.getId(), + user.getId(), + new CommentCreateDto(commentText) + ); + + List comments = commentRepository.findAllByItem_Id(itemRes.getId(), Sort.by(Sort.Direction.DESC, "created")); + + Assertions.assertFalse(comments.isEmpty()); + Assertions.assertEquals(commentText, comments.getFirst().getText()); + } + + private User createUser() { + Random random = new Random(); + + return userRepository.save(new User( + null, + "user name #" + random.nextInt(), + "user" + random.nextInt() + "@yandex.net" + )); + } + + private ItemCreateDto makeItemCreateDto() { + return new ItemCreateDto( + "Item name", + "item description", + true, + null + ); + } + +} \ No newline at end of file From ab3bdebe27f3430e692598742d3ae0b22990ee31 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Tue, 18 Mar 2025 13:04:02 +0300 Subject: [PATCH 09/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shareit/item/CommentDtoTests.java | 39 +++++++++++++ .../shareit/item/ItemControllerTests.java | 58 +++++++++++++++++++ .../practicum/shareit/item/ItemDtoTests.java | 35 +++++++++++ 3 files changed, 132 insertions(+) create mode 100644 server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/item/ItemDtoTests.java diff --git a/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java b/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java new file mode 100644 index 0000000..6a7e4d5 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java @@ -0,0 +1,39 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; +import ru.practicum.shareit.item.dto.CommentDto; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@JsonTest +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class CommentDtoTests { + + private final JacksonTester json; + + @Test + void commentDto() throws Exception { + LocalDateTime dateTime = LocalDateTime.now(); + + CommentDto commentDto = new CommentDto( + null, + "text", + "authorName", + dateTime + ); + + JsonContent result = json.write(commentDto); + + assertThat(result).extractingJsonPathStringValue("$.text").isEqualTo(commentDto.getText()); + assertThat(result).extractingJsonPathStringValue("$.authorName").isEqualTo(commentDto.getAuthorName()); + assertThat(result).extractingJsonPathStringValue("$.created").isEqualTo(commentDto.getCreated().toString()); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java b/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java new file mode 100644 index 0000000..58ff768 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java @@ -0,0 +1,58 @@ +package ru.practicum.shareit.item; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.shareit.item.dto.ItemDto; + +import java.nio.charset.StandardCharsets; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = ItemController.class) +class ItemControllerTests { + + @Autowired + ObjectMapper mapper; + + @MockBean + ItemService itemService; + + @Autowired + private MockMvc mockMvc; + + @Test + void createItem() throws Exception { + ItemDto itemDto = new ItemDto( + null, + "item name", + "item description", + true + ); + + when(itemService.create(Mockito.any(), Mockito.anyLong())) + .thenReturn(itemDto); + + mockMvc.perform( + post("/items") + .content(mapper.writeValueAsString(itemDto)) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value(itemDto.getName())) + .andExpect(jsonPath("$.description").value(itemDto.getDescription())) + .andExpect(jsonPath("$.available").value(itemDto.getAvailable())); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemDtoTests.java b/server/src/test/java/ru/practicum/shareit/item/ItemDtoTests.java new file mode 100644 index 0000000..42d1b76 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/ItemDtoTests.java @@ -0,0 +1,35 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; +import ru.practicum.shareit.item.dto.ItemDto; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@JsonTest +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class ItemDtoTests { + + private final JacksonTester json; + + @Test + void itemDto() throws Exception { + ItemDto itemDto = new ItemDto( + null, + "name", + "desc", + false + ); + + JsonContent result = json.write(itemDto); + + assertThat(result).extractingJsonPathStringValue("$.name").isEqualTo(itemDto.getName()); + assertThat(result).extractingJsonPathStringValue("$.description").isEqualTo(itemDto.getDescription()); + assertThat(result).extractingJsonPathBooleanValue("$.available").isEqualTo(itemDto.getAvailable()); + } + +} From 8fb4d559050db81ece23087a7e3f87e08702d712 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Wed, 19 Mar 2025 00:19:20 +0300 Subject: [PATCH 10/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shareit/item/ItemControllerTests.java | 160 +++++++++++++++++- .../shareit/user/UserControllerTests.java | 124 ++++++++++++++ .../practicum/shareit/user/UserDtoTests.java | 33 ++++ .../shareit/user/UserServiceTest.java | 98 +++++++++++ 4 files changed, 408 insertions(+), 7 deletions(-) create mode 100644 server/src/test/java/ru/practicum/shareit/user/UserControllerTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/user/UserDtoTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java b/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java index 58ff768..0ae6566 100644 --- a/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java +++ b/server/src/test/java/ru/practicum/shareit/item/ItemControllerTests.java @@ -8,11 +8,21 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.shareit.item.dto.CommentCreateDto; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemCreateDto; import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.dto.ItemInfoDto; +import ru.practicum.shareit.item.dto.ItemUpdateDto; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Random; import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -31,18 +41,39 @@ class ItemControllerTests { @Test void createItem() throws Exception { - ItemDto itemDto = new ItemDto( - null, - "item name", - "item description", - true - ); + ItemDto itemDto = makeItemDto(); - when(itemService.create(Mockito.any(), Mockito.anyLong())) + when(itemService.create(Mockito.any(ItemCreateDto.class), Mockito.anyLong())) .thenReturn(itemDto); mockMvc.perform( post("/items") + .content(mapper.writeValueAsString(new ItemCreateDto( + itemDto.getName(), + itemDto.getDescription(), + itemDto.getAvailable(), + null + ))) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value(itemDto.getName())) + .andExpect(jsonPath("$.description").value(itemDto.getDescription())) + .andExpect(jsonPath("$.available").value(itemDto.getAvailable())); + } + + @Test + void updateItem() throws Exception { + ItemDto itemDto = makeItemDto(); + + when(itemService.update(Mockito.any(ItemUpdateDto.class), Mockito.anyLong())) + .thenReturn(itemDto); + + mockMvc.perform( + patch("/items/{id}", itemDto.getId()) .content(mapper.writeValueAsString(itemDto)) .header("X-Sharer-User-Id", "1") .characterEncoding(StandardCharsets.UTF_8) @@ -55,4 +86,119 @@ void createItem() throws Exception { .andExpect(jsonPath("$.available").value(itemDto.getAvailable())); } + @Test + void getItem() throws Exception { + ItemInfoDto itemInfoDto = makeItemInfoDto(); + + when(itemService.findItemById(Mockito.anyLong(), Mockito.anyLong())) + .thenReturn(itemInfoDto); + + mockMvc.perform( + get("/items/{id}", itemInfoDto.getId()) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value(itemInfoDto.getName())) + .andExpect(jsonPath("$.description").value(itemInfoDto.getDescription())) + .andExpect(jsonPath("$.available").value(itemInfoDto.getAvailable())); + } + + @Test + void getItemsByOwner() throws Exception { + ItemInfoDto itemInfoDto = makeItemInfoDto(); + + when(itemService.findItemsByOwner(Mockito.anyLong())) + .thenReturn(List.of(itemInfoDto)); + + mockMvc.perform( + get("/items") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].name").value(itemInfoDto.getName())) + .andExpect(jsonPath("$[0].description").value(itemInfoDto.getDescription())) + .andExpect(jsonPath("$[0].available").value(itemInfoDto.getAvailable())); + } + + @Test + void search() throws Exception { + ItemDto itemDto = makeItemDto(); + String text = "search text"; + + when(itemService.findItemsByText(text)) + .thenReturn(List.of(itemDto)); + + mockMvc.perform( + get("/items/search") + .header("X-Sharer-User-Id", "1") + .param("text", text) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].name").value(itemDto.getName())) + .andExpect(jsonPath("$[0].description").value(itemDto.getDescription())) + .andExpect(jsonPath("$[0].available").value(itemDto.getAvailable())); + } + + @Test + void addComment() throws Exception { + ItemDto itemDto = makeItemDto(); + CommentDto commentDto = new CommentDto( + new Random().nextLong(), + "comment text", + "author name", + LocalDateTime.now() + ); + + when(itemService.addComment(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(CommentCreateDto.class))) + .thenReturn(commentDto); + + mockMvc.perform( + post("/items/{id}/comment", itemDto.getId()) + .content(mapper.writeValueAsString(new CommentCreateDto( + commentDto.getText() + ))) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.text").value(commentDto.getText())) + .andExpect(jsonPath("$.authorName").value(commentDto.getAuthorName())); + } + + private ItemDto makeItemDto() { + Long id = new Random().nextLong(); + + return new ItemDto( + id, + "item name #" + id, + "item description #" + id, + true + ); + } + + private ItemInfoDto makeItemInfoDto() { + Long id = new Random().nextLong(); + + return new ItemInfoDto( + id, + "item name #" + id, + "item description #" + id, + true, + null, + null, + null + ); + } + } diff --git a/server/src/test/java/ru/practicum/shareit/user/UserControllerTests.java b/server/src/test/java/ru/practicum/shareit/user/UserControllerTests.java new file mode 100644 index 0000000..6125b32 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/user/UserControllerTests.java @@ -0,0 +1,124 @@ +package ru.practicum.shareit.user; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +import java.nio.charset.StandardCharsets; +import java.util.Random; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = UserController.class) +class UserControllerTests { + + @Autowired + ObjectMapper mapper; + + @MockBean + UserService userService; + + @Autowired + private MockMvc mockMvc; + + @Test + void createUser() throws Exception { + UserDto userDto = makeUser(); + + when(userService.create(Mockito.any(UserCreateDto.class))) + .thenReturn(userDto); + + mockMvc.perform( + post("/users") + .content(mapper.writeValueAsString(new UserCreateDto( + userDto.getName(), + userDto.getEmail() + ))) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value(userDto.getName())) + .andExpect(jsonPath("$.email").value(userDto.getEmail())); + } + + @Test + void updateUser() throws Exception { + UserDto userDto = makeUser(); + + when(userService.update(Mockito.any(UserUpdateDto.class))) + .thenReturn(userDto); + + mockMvc.perform( + patch("/users/{id}", userDto.getId()) + .content(mapper.writeValueAsString(new UserUpdateDto( + userDto.getId(), + userDto.getName(), + userDto.getEmail() + ))) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value(userDto.getName())) + .andExpect(jsonPath("$.email").value(userDto.getEmail())); + } + + @Test + void getUser() throws Exception { + UserDto userDto = makeUser(); + + when(userService.findById(Mockito.anyLong())) + .thenReturn(userDto); + + mockMvc.perform( + get("/users/{id}", userDto.getId()) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value(userDto.getName())) + .andExpect(jsonPath("$.email").value(userDto.getEmail())); + } + + @Test + void deleteUser() throws Exception { + Mockito.doNothing().when(userService).deleteById(Mockito.anyLong()); + + mockMvc.perform( + delete("/users/{id}", new Random().nextLong()) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + } + + private UserDto makeUser() { + Long id = new Random().nextLong(); + + return new UserDto( + id, + "username #" + id, + "user-" + id + "@yandex.net" + ); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/user/UserDtoTests.java b/server/src/test/java/ru/practicum/shareit/user/UserDtoTests.java new file mode 100644 index 0000000..fd0295b --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/user/UserDtoTests.java @@ -0,0 +1,33 @@ +package ru.practicum.shareit.user; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; +import ru.practicum.shareit.user.dto.UserDto; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@JsonTest +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class UserDtoTests { + + private final JacksonTester json; + + @Test + void userDto() throws Exception { + UserDto userDto = new UserDto( + null, + "username", + "user@yandex.net" + ); + + JsonContent result = json.write(userDto); + + assertThat(result).extractingJsonPathStringValue("$.name").isEqualTo(userDto.getName()); + assertThat(result).extractingJsonPathStringValue("$.email").isEqualTo(userDto.getEmail()); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java b/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java new file mode 100644 index 0000000..e59cf52 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java @@ -0,0 +1,98 @@ +package ru.practicum.shareit.user; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import ru.practicum.shareit.user.contracts.UserRepositoryInterface; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; +import ru.practicum.shareit.user.model.User; + +import java.util.Optional; +import java.util.Random; + +@SpringBootTest +@ExtendWith(MockitoExtension.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class UserServiceTest { + + @Autowired + UserRepositoryInterface userRepository; + + @Autowired + UserService userService; + + @Test + void create() { + UserCreateDto userCreateDto = makeUserCreateDto(); + + UserDto userDto = userService.create(userCreateDto); + + Optional user = userRepository.findById(userDto.getId()); + + Assertions.assertTrue(user.isPresent()); + } + + @Test + void update() { + UserCreateDto userCreateDto = makeUserCreateDto(); + + UserDto userDto = userService.create(userCreateDto); + + String userNewName = "user new name"; + + userService.update( + new UserUpdateDto( + userDto.getId(), + userNewName, + userDto.getEmail() + ) + ); + + Optional user = userRepository.findById(userDto.getId()); + + Assertions.assertTrue(user.isPresent()); + Assertions.assertEquals(userNewName, user.get().getName()); + } + + @Test + void findItemById() { + UserCreateDto userCreateDto = makeUserCreateDto(); + + UserDto userDto = userService.create(userCreateDto); + UserDto user = userService.findById(userDto.getId()); + + Assertions.assertNotNull(user); + Assertions.assertEquals(user.getId(), userDto.getId()); + } + + @Test + void delete() { + UserCreateDto userCreateDto = makeUserCreateDto(); + + UserDto userDto = userService.create(userCreateDto); + Optional userBeforeDelete = userRepository.findById(userDto.getId()); + + userService.deleteById(userDto.getId()); + + Optional userAfterDelete = userRepository.findById(userDto.getId()); + + Assertions.assertTrue(userBeforeDelete.isPresent()); + Assertions.assertFalse(userAfterDelete.isPresent()); + } + + private UserCreateDto makeUserCreateDto() { + long id = new Random().nextLong(); + + return new UserCreateDto( + "username #" + id, + "user-" + id + "@yandex.net" + ); + } + +} \ No newline at end of file From 71eb6d52bfe66b7555725fc15603073753571ff4 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 00:13:38 +0300 Subject: [PATCH 11/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/pom.xml | 7 +- .../shareit/booking/dto/BookingCreateDto.java | 5 - .../practicum/shareit/item/ItemService.java | 2 +- .../booking/BookingControllerTests.java | 146 +++++++++ .../shareit/booking/BookingDtoTests.java | 39 +++ .../shareit/booking/BookingServiceTest.java | 249 +++++++++++++++ .../shareit/item/CommentDtoTests.java | 1 - .../shareit/item/ItemRepositoryTests.java | 113 +++++++ .../shareit/item/ItemServiceTest.java | 286 +++++++++++++++++- .../request/ItemRequestControllerTests.java | 140 +++++++++ .../request/ItemRequestRepositoryTests.java | 95 ++++++ .../request/ItemRequestServiceTest.java | 181 +++++++++++ .../request/RequestResponseDtoTests.java | 38 +++ .../shareit/user/UserRepositoryTests.java | 32 ++ .../shareit/user/UserServiceTest.java | 74 +++++ 15 files changed, 1396 insertions(+), 12 deletions(-) create mode 100644 server/src/test/java/ru/practicum/shareit/booking/BookingControllerTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/booking/BookingDtoTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java create mode 100644 server/src/test/java/ru/practicum/shareit/item/ItemRepositoryTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java create mode 100644 server/src/test/java/ru/practicum/shareit/request/RequestResponseDtoTests.java create mode 100644 server/src/test/java/ru/practicum/shareit/user/UserRepositoryTests.java diff --git a/server/pom.xml b/server/pom.xml index 566db3e..89546a4 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -58,7 +58,12 @@ spring-boot-starter-test test - + + jakarta.validation + jakarta.validation-api + test + + diff --git a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java index 3bbda00..4783123 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java +++ b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java @@ -4,7 +4,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.model.BookingStatus; import java.time.LocalDateTime; @@ -14,8 +13,6 @@ @Builder public class BookingCreateDto { - private Long id; - private LocalDateTime start; private LocalDateTime end; @@ -23,6 +20,4 @@ public class BookingCreateDto { private Long itemId; private Long bookerId; - - private BookingStatus bookingStatus; } diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java index 39d9d7a..9c53b61 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -102,7 +102,7 @@ public ItemDto update(final ItemUpdateDto itemDto, final Long userId) { () -> new NotFoundException(ITEM_NOT_FOUND.formatted(itemDto.getId())) ); - if (item.getOwner().equals(user)) { + if (!item.getOwner().getId().equals(user.getId())) { throw new InvalidOwnerException("Owner is not the same user"); } diff --git a/server/src/test/java/ru/practicum/shareit/booking/BookingControllerTests.java b/server/src/test/java/ru/practicum/shareit/booking/BookingControllerTests.java new file mode 100644 index 0000000..fb93f8e --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/booking/BookingControllerTests.java @@ -0,0 +1,146 @@ +package ru.practicum.shareit.booking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.shareit.booking.dto.BookingCreateDto; +import ru.practicum.shareit.booking.dto.BookingDto; +import ru.practicum.shareit.booking.model.BookingStatus; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Random; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = BookingController.class) +class BookingControllerTests { + + @Autowired + ObjectMapper mapper; + + @MockBean + BookingService bookingService; + + @Autowired + private MockMvc mockMvc; + + @Test + void createBooking() throws Exception { + BookingDto bookingDto = makeBookingDto(); + + when(bookingService.create(Mockito.any(BookingCreateDto.class))) + .thenReturn(bookingDto); + + mockMvc.perform( + post("/bookings") + .content(mapper.writeValueAsString(new BookingCreateDto( + bookingDto.getStart(), + bookingDto.getEnd(), + bookingDto.getBooker().id(), + bookingDto.getItem().id() + ))) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.name").value(bookingDto.getItem().name())); + } + + @Test + void approveBooking() throws Exception { + BookingDto bookingDto = makeBookingDto(); + + when(bookingService.approve(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyBoolean())) + .thenReturn(bookingDto); + + mockMvc.perform( + patch("/bookings/{id}?approved={approve}", bookingDto.getId(), true) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.name").value(bookingDto.getItem().name())); + } + + @Test + void getById() throws Exception { + BookingDto bookingDto = makeBookingDto(); + + when(bookingService.getById(Mockito.anyLong(), Mockito.anyLong())) + .thenReturn(bookingDto); + + mockMvc.perform( + get("/bookings/{id}", bookingDto.getId()) + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.name").value(bookingDto.getItem().name())); + } + + @Test + void getByBookerAndState() throws Exception { + BookingDto bookingDto = makeBookingDto(); + + when(bookingService.getByBookerAndState(Mockito.anyLong(), Mockito.any())) + .thenReturn(List.of(bookingDto)); + + mockMvc.perform( + get("/bookings") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].item.name").value(bookingDto.getItem().name())); + } + + @Test + void getByOwnerAndState() throws Exception { + BookingDto bookingDto = makeBookingDto(); + + when(bookingService.getByOwnerAndState(Mockito.anyLong(), Mockito.any())) + .thenReturn(List.of(bookingDto)); + + mockMvc.perform( + get("/bookings/owner") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].item.name").value(bookingDto.getItem().name())); + } + + private BookingDto makeBookingDto() { + return new BookingDto( + new Random().nextLong(), + new BookingDto.ItemDto(null, "item name"), + new BookingDto.BookerDto(null, "user name"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(2L), + BookingStatus.APPROVED + ); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/booking/BookingDtoTests.java b/server/src/test/java/ru/practicum/shareit/booking/BookingDtoTests.java new file mode 100644 index 0000000..75267d7 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/booking/BookingDtoTests.java @@ -0,0 +1,39 @@ +package ru.practicum.shareit.booking; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; +import ru.practicum.shareit.booking.dto.BookingDto; +import ru.practicum.shareit.booking.model.BookingStatus; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@JsonTest +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class BookingDtoTests { + + private final JacksonTester json; + + @Test + void bookingDto() throws Exception { + BookingDto bookingDto = new BookingDto( + null, + new BookingDto.ItemDto(null, "item name"), + new BookingDto.BookerDto(null, "user name"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(2L), + BookingStatus.APPROVED + ); + + JsonContent result = json.write(bookingDto); + + assertThat(result).extractingJsonPathStringValue("$.item.name").isEqualTo(bookingDto.getItem().name()); + assertThat(result).extractingJsonPathStringValue("$.booker.name").isEqualTo(bookingDto.getBooker().name()); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java b/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java new file mode 100644 index 0000000..d6d7c42 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java @@ -0,0 +1,249 @@ +package ru.practicum.shareit.booking; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import ru.practicum.shareit.booking.contracts.BookingRepositoryInterface; +import ru.practicum.shareit.booking.dto.BookingCreateDto; +import ru.practicum.shareit.booking.dto.BookingDto; +import ru.practicum.shareit.booking.model.Booking; +import ru.practicum.shareit.booking.model.BookingState; +import ru.practicum.shareit.booking.model.BookingStatus; +import ru.practicum.shareit.exception.InvalidBookingStatusException; +import ru.practicum.shareit.exception.InvalidOwnerException; +import ru.practicum.shareit.exception.ItemUnavailableException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.contracts.ItemRepositoryInterface; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.contracts.UserRepositoryInterface; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +@SpringBootTest +@ExtendWith(MockitoExtension.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class BookingServiceTest { + + @Autowired + BookingRepositoryInterface bookingRepository; + + @Autowired + ItemRepositoryInterface itemRepository; + + @Autowired + UserRepositoryInterface userRepository; + + @Autowired + BookingService bookingService; + + @Test + void create() { + User owner = createUser(); + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + + BookingDto bookingRes = bookingService.create(bookingCreateDto); + + Optional booking = bookingRepository.findById(bookingRes.getId()); + + Assertions.assertTrue(booking.isPresent()); + } + + @Test + void createWithInvalidItemReturnThrow() { + Random random = new Random(); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> bookingService.create(makeBookingCreateDto( + new User(random.nextLong(), null, null), + new Item(random.nextLong(), null, null, true, null, null) + )) + ); + } + + @Test + void createWithInvalidUserReturnThrow() { + User owner = createUser(); + User user = new User(new Random().nextLong(), null, null); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> bookingService.create(makeBookingCreateDto( + user, + createItem(owner) + )) + ); + } + + @Test + void createWithUnavailableItemReturnThrow() { + User owner = createUser(); + + Item item = createItem(owner); + item.setAvailable(false); + itemRepository.save(item); + + Assertions.assertThrowsExactly( + ItemUnavailableException.class, + () -> bookingService.create(makeBookingCreateDto( + owner, + item + )) + ); + } + + @Test + void approve() { + User owner = createUser(); + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + + BookingDto bookingRes = bookingService.create(bookingCreateDto); + + BookingDto booking = bookingService.approve( + bookingRes.getId(), + owner.getId(), + true + ); + + Assertions.assertNotNull(booking); + Assertions.assertEquals(BookingStatus.APPROVED, booking.getStatus()); + } + + @Test + void approveWithInvalidIdReturnThrow() { + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> bookingService.approve(new Random().nextLong(), null, true) + ); + } + + @Test + void approveWithInvalidStatusReturnThrow() { + User owner = createUser(); + Item item = createItem(owner); + + Booking booking = bookingRepository.save(new Booking( + null, + item, + owner, + LocalDateTime.now(), + LocalDateTime.now().plusDays(1), + BookingStatus.APPROVED + )); + + Assertions.assertThrowsExactly( + InvalidBookingStatusException.class, + () -> bookingService.approve(booking.getId(), owner.getId(), true) + ); + } + + @Test + void approveWithInvalidOwnerReturnThrow() { + User owner = createUser(); + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + BookingDto bookingRes = bookingService.create(bookingCreateDto); + User invalidOwner = createUser(); + + Assertions.assertThrowsExactly( + InvalidOwnerException.class, + () -> bookingService.approve(bookingRes.getId(), invalidOwner.getId(), true) + ); + } + + @Test + void getById() { + User owner = createUser(); + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + + BookingDto bookingRes = bookingService.create(bookingCreateDto); + BookingDto booking = bookingService.getById(bookingRes.getId(), owner.getId()); + + Assertions.assertEquals(bookingRes, booking); + } + + @Test + void getByIdWithInvalidIdReturnThrow() { + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> bookingService.getById(new Random().nextLong(), null) + ); + } + + @Test + void findItemsByOwner() { + User owner = createUser(); + int ownerItemsCount = new Random().nextInt(10); + + for (int i = 1; i <= ownerItemsCount; i++) { + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + bookingService.create(bookingCreateDto); + } + + List bookings = bookingService.getByOwnerAndState(owner.getId(), BookingState.ALL); + + Assertions.assertEquals(ownerItemsCount, bookings.size()); + } + + @Test + void findItemsByBooker() { + User owner = createUser(); + User booker = createUser(); + int ownerItemsCount = new Random().nextInt(10); + + for (int i = 1; i <= ownerItemsCount; i++) { + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(booker, item); + bookingService.create(bookingCreateDto); + } + + List bookings = bookingService.getByBookerAndState(booker.getId(), BookingState.ALL); + + Assertions.assertEquals(ownerItemsCount, bookings.size()); + } + + private User createUser() { + long id = new Random().nextLong(); + + return userRepository.save(new User( + null, + "user name #" + id, + "user" + id + "@yandex.net" + )); + } + + private Item createItem(User owner) { + long id = new Random().nextLong(); + + return itemRepository.save(new Item( + null, + "name #" + id, + "description " + id, + true, + owner, + null + )); + } + + private BookingCreateDto makeBookingCreateDto(User booker, Item item) { + return new BookingCreateDto( + LocalDateTime.now(), + LocalDateTime.now().plusDays(2L), + item.getId(), + booker.getId() + ); + } + +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java b/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java index 6a7e4d5..363785e 100644 --- a/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java +++ b/server/src/test/java/ru/practicum/shareit/item/CommentDtoTests.java @@ -33,7 +33,6 @@ void commentDto() throws Exception { assertThat(result).extractingJsonPathStringValue("$.text").isEqualTo(commentDto.getText()); assertThat(result).extractingJsonPathStringValue("$.authorName").isEqualTo(commentDto.getAuthorName()); - assertThat(result).extractingJsonPathStringValue("$.created").isEqualTo(commentDto.getCreated().toString()); } } diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemRepositoryTests.java b/server/src/test/java/ru/practicum/shareit/item/ItemRepositoryTests.java new file mode 100644 index 0000000..1c296c9 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/ItemRepositoryTests.java @@ -0,0 +1,113 @@ +package ru.practicum.shareit.item; + +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import ru.practicum.shareit.item.contracts.ItemRepositoryInterface; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@DataJpaTest() +class ItemRepositoryTests { + + @Autowired + ItemRepositoryInterface repository; + + @Autowired + TestEntityManager em; + + @Test + void findAllByOwnerId() { + int rowsCount = 0; + int userCount = 2; + + List userList = new ArrayList<>(userCount); + + for (int j = 1; j <= userCount; j++) { + User user = new User(null, "username" + j, "user" + j); + em.persist(user); + userList.add(user); + + rowsCount = new Random().nextInt(1, 10); + + for (int i = 1; i <= rowsCount; i++) { + Item item = new Item( + null, + "Name" + i, + "Description" + i, + true, + user, + null + ); + em.persist(item); + } + } + + em.flush(); + + Assertions.assertEquals( + rowsCount, + repository.findAllByOwnerId( + userList.getLast().getId() + ).size() + ); + } + + @Test + void findAllByText() { + + User user = new User(null, "username", "user"); + em.persist(user); + + Item item = new Item( + null, + "Name", + "Description", + true, + user, + null + ); + em.persist(item); + + em.flush(); + + Assertions.assertEquals(1, repository.findAllByText("description").size()); + Assertions.assertEquals(1, repository.findAllByText("name").size()); + Assertions.assertEquals(0, repository.findAllByText("some text").size()); + } + + @Test + void findAllByRequest_IdOrderByIdDesc() { + + User user = new User(null, "username", "user"); + em.persist(user); + + ItemRequest itemRequest = new ItemRequest(null, "Description", user, LocalDateTime.now()); + em.persist(itemRequest); + + Item item = new Item( + null, + "Name", + "Description", + true, + user, + itemRequest + ); + em.persist(item); + + em.flush(); + + Assertions.assertEquals( + 1, + repository.findAllByRequest_IdOrderByIdDesc(itemRequest.getId()).size() + ); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java b/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java index c16158e..3db7391 100644 --- a/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/item/ItemServiceTest.java @@ -11,6 +11,12 @@ import org.springframework.data.domain.Sort; import org.springframework.test.annotation.DirtiesContext; import ru.practicum.shareit.booking.contracts.BookingRepositoryInterface; +import ru.practicum.shareit.booking.model.Booking; +import ru.practicum.shareit.booking.model.BookingStatus; +import ru.practicum.shareit.exception.EmptyIdException; +import ru.practicum.shareit.exception.InvalidOwnerException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.UserDoesNotHaveBookedItem; import ru.practicum.shareit.item.contracts.CommentRepositoryInterface; import ru.practicum.shareit.item.contracts.ItemRepositoryInterface; import ru.practicum.shareit.item.dto.CommentCreateDto; @@ -20,9 +26,12 @@ import ru.practicum.shareit.item.dto.ItemUpdateDto; import ru.practicum.shareit.item.model.Comment; import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; +import ru.practicum.shareit.request.model.ItemRequest; import ru.practicum.shareit.user.contracts.UserRepositoryInterface; import ru.practicum.shareit.user.model.User; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.Random; @@ -47,6 +56,9 @@ class ItemServiceTest { @Autowired ItemService itemService; + @Autowired + ItemRequestRepositoryInterface itemRequestRepository; + @Test void create() { User owner = createUser(); @@ -59,6 +71,48 @@ void create() { Assertions.assertTrue(item.isPresent()); } + @Test + void createWithRequest() { + User owner = createUser(); + ItemRequest itemRequest = itemRequestRepository.save(new ItemRequest( + null, + "Request description", + owner, + LocalDateTime.now() + )); + + ItemCreateDto itemCreateDto = makeItemCreateDto(); + itemCreateDto.setRequestId(itemRequest.getId()); + + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + Optional item = itemRepository.findById(itemRes.getId()); + + Assertions.assertTrue(item.isPresent()); + } + + @Test + void createWithWrongUserReturnThrow() { + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.create(itemCreateDto, new Random().nextLong()) + ); + } + + @Test + void createWithWrongRequestReturnThrow() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + itemCreateDto.setRequestId(new Random().nextLong()); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.create(itemCreateDto, owner.getId()) + ); + } + @Test void update() { User owner = createUser(); @@ -83,6 +137,90 @@ void update() { Assertions.assertEquals(itemNewName, item.get().getName()); } + @Test + void updateWithWrongIdReturnThrows() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.update( + new ItemUpdateDto( + new Random().nextLong(), + itemCreateDto.getName(), + itemCreateDto.getDescription(), + itemCreateDto.getAvailable() + ), + owner.getId() + ) + ); + } + + @Test + void updateWithWrongOwnerReturnThrow() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + User wrongOwner = createUser(); + + Assertions.assertThrowsExactly( + InvalidOwnerException.class, + () -> itemService.update( + new ItemUpdateDto( + itemRes.getId(), + itemRes.getName(), + itemRes.getDescription(), + itemRes.getAvailable() + ), + wrongOwner.getId() + ) + ); + } + + @Test + void updateWithWrongUserReturnThrow() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.update( + new ItemUpdateDto( + itemRes.getId(), + itemRes.getName(), + itemRes.getDescription(), + itemRes.getAvailable() + ), + new Random().nextLong() + ) + ); + } + + @Test + void updateWithInvalidItemIdReturnThrow() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + Assertions.assertThrowsExactly( + EmptyIdException.class, + () -> itemService.update( + new ItemUpdateDto( + null, + itemRes.getName(), + itemRes.getDescription(), + itemRes.getAvailable() + ), + new Random().nextLong() + ) + ); + } + @Test void findItemById() { User owner = createUser(); @@ -96,29 +234,103 @@ void findItemById() { Assertions.assertEquals(item.getId(), itemRes.getId()); } + @Test + void findItemByIdWithWrongIdReturnThrow() { + Random random = new Random(); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.findItemById(random.nextLong(), random.nextLong()) + ); + } + + @Test + void findItemByIdByOwner() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + ItemInfoDto item = itemService.findItemById(itemRes.getId(), owner.getId()); + + Assertions.assertNotNull(item); + Assertions.assertEquals(item.getId(), itemRes.getId()); + } + @Test void findItemsByOwner() { User owner = createUser(); - int ownerItemsCount = new Random().nextInt(10); + int ownerItemsCount = new Random().nextInt(2, 10); + ItemDto itemDto = null; for (int i = 1; i <= ownerItemsCount; i++) { ItemCreateDto itemCreateDto = makeItemCreateDto(); - itemService.create(itemCreateDto, owner.getId()); + itemDto = itemService.create(itemCreateDto, owner.getId()); } User user = createUser(); - int userItemsCount = new Random().nextInt(10); + int userItemsCount = new Random().nextInt(2,10); - for (int i = 0; i <= userItemsCount; i++) { + for (int i = 1; i <= userItemsCount; i++) { ItemCreateDto itemCreateDto = makeItemCreateDto(); itemService.create(itemCreateDto, user.getId()); } + Random random = new Random(); + Item item = new Item( + itemDto.getId(), + itemDto.getName(), + itemDto.getDescription(), + itemDto.getAvailable(), + owner, + null + ); + + Mockito.when(bookingRepository.findAllByItem_IdInAndStatus( + Mockito.anyList(), + Mockito.any(), + Mockito.any() + )).thenReturn( + List.of( + new Booking( + random.nextLong(), + item, + user, + LocalDateTime.now().minusDays(4), + LocalDateTime.now().minusDays(3), + BookingStatus.APPROVED + ), + new Booking( + random.nextLong(), + item, + user, + LocalDateTime.now().plusDays(1), + LocalDateTime.now().plusDays(2), + BookingStatus.APPROVED + ) + ) + ); + + commentRepository.save(new Comment( + null, + "comment text", + item, + user, + LocalDateTime.now() + )); + List items = itemService.findItemsByOwner(owner.getId()); Assertions.assertEquals(ownerItemsCount, items.size()); } + @Test + void findItemsByOwnerWithWrongIdReturnThrow() { + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.findItemsByOwner(new Random().nextLong()) + ); + } + @Test void findItemsByText() { String searchText = "text for search test"; @@ -136,6 +348,19 @@ void findItemsByText() { } + @Test + void findItemsByTextWithEmptyString() { + + ItemCreateDto itemCreateDto = makeItemCreateDto(); + User owner = createUser(); + + itemService.create(itemCreateDto, owner.getId()); + + List res = itemService.findItemsByText(""); + + Assertions.assertTrue(res.isEmpty()); + } + @Test void addComment() { User owner = createUser(); @@ -161,6 +386,59 @@ void addComment() { Assertions.assertEquals(commentText, comments.getFirst().getText()); } + @Test + void addCommentWithWrongIdReturnThrow() { + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.addComment( + new Random().nextLong(), + null, + new CommentCreateDto("text") + ) + ); + } + + @Test + void addCommentWithWrongUserReturnThrow() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemService.addComment( + itemRes.getId(), + new Random().nextLong(), + new CommentCreateDto("text") + ) + ); + } + + @Test + void addCommentWithInvalidBookerIdReturnThrow() { + User owner = createUser(); + ItemCreateDto itemCreateDto = makeItemCreateDto(); + ItemDto itemRes = itemService.create(itemCreateDto, owner.getId()); + + Mockito.when( + bookingRepository.existsByBookerIdAndItemIdAndStatusEqualsAndEndIsBefore( + Mockito.anyLong(), Mockito.anyLong(), Mockito.any(), Mockito.any() + ) + ).thenReturn(false); + + User user = createUser(); + + Assertions.assertThrowsExactly( + UserDoesNotHaveBookedItem.class, + () -> itemService.addComment( + itemRes.getId(), + user.getId(), + new CommentCreateDto("comment text") + ) + ); + } + private User createUser() { Random random = new Random(); diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTests.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTests.java new file mode 100644 index 0000000..8119bfc --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTests.java @@ -0,0 +1,140 @@ +package ru.practicum.shareit.request; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; +import ru.practicum.shareit.user.model.User; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import static org.mockito.Mockito.when; +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.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = ItemRequestController.class) +class ItemRequestControllerTests { + + @Autowired + ObjectMapper mapper; + + @MockBean + ItemRequestService itemRequestService; + + @Autowired + private MockMvc mockMvc; + + @Test + void create() throws Exception { + ItemRequestResponseDto itemRequestResponseDto = makeItemRequestDto(); + User user = makeUser(); + + when(itemRequestService.create(Mockito.any(ItemRequestCreateDto.class))) + .thenReturn(itemRequestResponseDto); + + mockMvc.perform( + post("/requests") + .content(mapper.writeValueAsString(new ItemRequestCreateDto( + itemRequestResponseDto.getDescription(), + user.getId() + ))) + .header("X-Sharer-User-Id", user.getId().toString()) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.description").value(itemRequestResponseDto.getDescription())); + } + + @Test + void getListByUser() throws Exception { + ItemRequestResponseDto itemRequestResponseDto = makeItemRequestDto(); + User user = makeUser(); + + when(itemRequestService.getListByUser(Mockito.anyLong())) + .thenReturn(List.of(itemRequestResponseDto)); + + mockMvc.perform( + get("/requests") + .header("X-Sharer-User-Id", user.getId().toString()) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].description").value(itemRequestResponseDto.getDescription())); + } + + @Test + void getList() throws Exception { + ItemRequestResponseDto itemRequestResponseDto = makeItemRequestDto(); + User user = makeUser(); + + when(itemRequestService.getList(Mockito.anyLong())) + .thenReturn(List.of(itemRequestResponseDto)); + + mockMvc.perform( + get("/requests/all") + .header("X-Sharer-User-Id", user.getId().toString()) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].description").value(itemRequestResponseDto.getDescription())); + } + + @Test + void getById() throws Exception { + ItemRequestResponseDto itemRequestResponseDto = makeItemRequestDto(); + User user = makeUser(); + + when(itemRequestService.getById(Mockito.anyLong())) + .thenReturn(itemRequestResponseDto); + + mockMvc.perform( + get("/requests/{id}", itemRequestResponseDto.getId()) + .header("X-Sharer-User-Id", user.getId().toString()) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.description").value(itemRequestResponseDto.getDescription())); + } + + private ItemRequestResponseDto makeItemRequestDto() { + Long id = new Random().nextLong(); + + return new ItemRequestResponseDto( + id, + "description #" + id, + LocalDateTime.now(), + Collections.emptyList() + ); + } + + private User makeUser() { + Long id = new Random().nextLong(); + + return new User( + id, + "user name #" + id, + "user-" + id + "@yandex.net" + ); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTests.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTests.java new file mode 100644 index 0000000..0fabccc --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTests.java @@ -0,0 +1,95 @@ +package ru.practicum.shareit.request; + +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@DataJpaTest() +class ItemRequestRepositoryTests { + + @Autowired + ItemRequestRepositoryInterface repository; + + @Autowired + TestEntityManager em; + + @Test + void findAllByRequestor_IdOrderByCreatedDesc() { + int rowsCount = 0; + int userCount = 2; + + List userList = new ArrayList<>(userCount); + + for (int j = 1; j <= userCount; j++) { + User user = new User(null, "username" + j, "user" + j); + em.persist(user); + userList.add(user); + + rowsCount = new Random().nextInt(1, 10); + + for (int i = 1; i <= rowsCount; i++) { + ItemRequest itemRequest = new ItemRequest( + null, + "description", + user, + LocalDateTime.now() + ); + em.persist(itemRequest); + } + } + + em.flush(); + + Assertions.assertEquals( + rowsCount, + repository.findAllByRequestor_IdOrderByCreatedDesc( + userList.getLast().getId() + ).size() + ); + } + + @Test + void findAllByRequestor_IdIsNotOrderByCreatedDesc() { + int rowsCount = 0; + int userCount = 2; + + List userList = new ArrayList<>(userCount); + + for (int j = 1; j <= userCount; j++) { + User user = new User(null, "username" + j, "user" + j); + em.persist(user); + userList.add(user); + + rowsCount = new Random().nextInt(1, 10); + + for (int i = 1; i <= rowsCount; i++) { + ItemRequest itemRequest = new ItemRequest( + null, + "description", + user, + LocalDateTime.now() + ); + em.persist(itemRequest); + } + } + + em.flush(); + + Assertions.assertEquals( + rowsCount, + repository.findAllByRequestor_IdIsNotOrderByCreatedDesc( + userList.getFirst().getId() + ).size() + ); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java new file mode 100644 index 0000000..d09110e --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java @@ -0,0 +1,181 @@ +package ru.practicum.shareit.request; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.contracts.ItemRepositoryInterface; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.request.contracts.ItemRequestRepositoryInterface; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.contracts.UserRepositoryInterface; +import ru.practicum.shareit.user.model.User; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +@SpringBootTest +@ExtendWith(MockitoExtension.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class ItemRequestServiceTest { + + @Autowired + ItemRequestRepositoryInterface itemRequestRepository; + + @MockBean + ItemRepositoryInterface itemRepository; + + @Autowired + UserRepositoryInterface userRepository; + + @Autowired + ItemRequestService itemRequestService; + + @Test + void create() { + User user = createUser(); + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); + + ItemRequestResponseDto itemRequestResponseDto = itemRequestService.create(itemRequestCreateDto); + + Optional itemRequest = itemRequestRepository.findById(itemRequestResponseDto.getId()); + + Assertions.assertTrue(itemRequest.isPresent()); + } + + @Test + void createWithInvalidUserReturnThrow() { + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemRequestService.create(makeItemRequestCreateDto( + new User(new Random().nextLong(), "username", "email") + )) + ); + } + + @Test + void getById() { + User user = createUser(); + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); + + ItemRequestResponseDto itemRequestResponseDto = itemRequestService.create(itemRequestCreateDto); + + Mockito.when( + itemRepository.findAllByRequest_IdOrderByIdDesc(Mockito.anyLong()) + ).thenReturn( + List.of( + new Item( + null, + "item name", + "item description", + true, + user, + null + ) + ) + ); + + ItemRequestResponseDto itemRequest = itemRequestService.getById(itemRequestResponseDto.getId()); + + Assertions.assertEquals(itemRequestResponseDto.getId(), itemRequest.getId()); + Assertions.assertFalse(itemRequest.getItems().isEmpty()); + } + + @Test + void getByIdWithInvalidIdReturnThrow() { + Assertions.assertThrows( + NotFoundException.class, () -> itemRequestService.getById(new Random().nextLong()) + ); + } + + @Test + void getListByUser() { + User user = createUser(); + int userRequestsCount = new Random().nextInt(10); + + for (int i = 1; i <= userRequestsCount; i++) { + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); + itemRequestService.create(itemRequestCreateDto); + } + + List itemRequests = itemRequestService.getListByUser(user.getId()); + + Assertions.assertEquals(userRequestsCount, itemRequests.size()); + } + + @Test + void getListByUserWithInvalidUserReturnThrow() { + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> itemRequestService.getListByUser(new Random().nextLong()) + ); + } + + @Test + void getList() { + List users = new ArrayList<>(); + List userRequestsCounts = new ArrayList<>(); + + for (int j = 0; j < 2; j++) { + users.add(createUser()); + userRequestsCounts.add(new Random().nextInt(10)); + + for (int i = 1; i <= userRequestsCounts.getLast(); i++) { + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(users.getLast()); + itemRequestService.create(itemRequestCreateDto); + } + } + + List itemRequests = itemRequestService.getList(users.getFirst().getId()); + + Assertions.assertEquals(userRequestsCounts.getLast(), itemRequests.size()); + } + + @Test + void getListWithAllUsers() { + List users = new ArrayList<>(); + List userRequestsCounts = new ArrayList<>(); + + for (int j = 0; j < 2; j++) { + users.add(createUser()); + userRequestsCounts.add(new Random().nextInt(10)); + + for (int i = 1; i <= userRequestsCounts.getLast(); i++) { + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(users.getLast()); + itemRequestService.create(itemRequestCreateDto); + } + } + + List itemRequests = itemRequestService.getList(null); + + Assertions.assertEquals(userRequestsCounts.stream().mapToInt(Integer::intValue).sum(), itemRequests.size()); + } + + private User createUser() { + long id = new Random().nextLong(); + + return userRepository.save(new User( + null, + "user name #" + id, + "user" + id + "@yandex.net" + )); + } + + private ItemRequestCreateDto makeItemRequestCreateDto(User user) { + return new ItemRequestCreateDto( + "description", + user.getId() + ); + } + +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/request/RequestResponseDtoTests.java b/server/src/test/java/ru/practicum/shareit/request/RequestResponseDtoTests.java new file mode 100644 index 0000000..b9a78d2 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/RequestResponseDtoTests.java @@ -0,0 +1,38 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; +import ru.practicum.shareit.request.dto.ItemRequestResponseDto; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@JsonTest +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class RequestResponseDtoTests { + + private final JacksonTester json; + + @Test + void itemRequestResponseDto() throws Exception { + ItemRequestResponseDto itemRequestResponseDto = new ItemRequestResponseDto( + null, + "description", + LocalDateTime.now(), + List.of( + new ItemRequestResponseDto.ItemDto(null, "item name") + ) + ); + + JsonContent result = json.write(itemRequestResponseDto); + + assertThat(result).extractingJsonPathStringValue("$.description").isEqualTo(itemRequestResponseDto.getDescription()); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/user/UserRepositoryTests.java b/server/src/test/java/ru/practicum/shareit/user/UserRepositoryTests.java new file mode 100644 index 0000000..845a01d --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/user/UserRepositoryTests.java @@ -0,0 +1,32 @@ +package ru.practicum.shareit.user; + +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import ru.practicum.shareit.user.contracts.UserRepositoryInterface; +import ru.practicum.shareit.user.model.User; + +@DataJpaTest() +class UserRepositoryTests { + + @Autowired + UserRepositoryInterface repository; + + @Autowired + TestEntityManager em; + + @Test + void existsByEmail() { + String email = "user@somehost.net"; + User user = new User(null, "username", email); + + em.persist(user); + em.flush(); + + Assertions.assertTrue( + repository.existsByEmail(email) + ); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java b/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java index e59cf52..237d5a2 100644 --- a/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/user/UserServiceTest.java @@ -7,6 +7,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import ru.practicum.shareit.exception.EmptyIdException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.NotUniqueEmailException; import ru.practicum.shareit.user.contracts.UserRepositoryInterface; import ru.practicum.shareit.user.dto.UserCreateDto; import ru.practicum.shareit.user.dto.UserDto; @@ -38,6 +41,18 @@ void create() { Assertions.assertTrue(user.isPresent()); } + @Test + void createWithExistingEmailReturnThrow() { + UserCreateDto userCreateDto = makeUserCreateDto(); + + userService.create(userCreateDto); + + Assertions.assertThrowsExactly( + NotUniqueEmailException.class, + () -> userService.create(userCreateDto) + ); + } + @Test void update() { UserCreateDto userCreateDto = makeUserCreateDto(); @@ -60,6 +75,57 @@ void update() { Assertions.assertEquals(userNewName, user.get().getName()); } + @Test + void updateWithWrongUserIdReturnThrow() { + UserCreateDto userCreateDto = makeUserCreateDto(); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> userService.update( + new UserUpdateDto( + new Random().nextLong(), + userCreateDto.getName(), + userCreateDto.getEmail() + ) + ) + ); + } + + @Test + void updateWithNonUniqueEmailReturnThrow() { + + UserDto userDto1 = userService.create(makeUserCreateDto()); + UserDto userDto2 = userService.create(makeUserCreateDto()); + + Assertions.assertThrowsExactly( + NotUniqueEmailException.class, + () -> userService.update( + new UserUpdateDto( + userDto2.getId(), + userDto2.getName(), + userDto1.getEmail() + ) + ) + ); + } + + @Test + void updateWithIdIsNullReturnThrow() { + + UserDto userDto = userService.create(makeUserCreateDto()); + + Assertions.assertThrowsExactly( + EmptyIdException.class, + () -> userService.update( + new UserUpdateDto( + null, + userDto.getName(), + userDto.getEmail() + ) + ) + ); + } + @Test void findItemById() { UserCreateDto userCreateDto = makeUserCreateDto(); @@ -71,6 +137,14 @@ void findItemById() { Assertions.assertEquals(user.getId(), userDto.getId()); } + @Test + void findItemByIdReturnThrow() { + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> userService.findById(new Random().nextLong()) + ); + } + @Test void delete() { UserCreateDto userCreateDto = makeUserCreateDto(); From 2eab5c2105b8ee0f75f88a4d9a4ed4af8dca5262 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 00:37:18 +0300 Subject: [PATCH 12/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=205?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shareit/booking/BookingServiceTest.java | 87 +++++++++++++++++-- .../request/ItemRequestServiceTest.java | 5 +- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java b/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java index d6d7c42..840d894 100644 --- a/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/booking/BookingServiceTest.java @@ -23,7 +23,6 @@ import ru.practicum.shareit.user.model.User; import java.time.LocalDateTime; -import java.util.List; import java.util.Optional; import java.util.Random; @@ -164,13 +163,14 @@ void approveWithInvalidOwnerReturnThrow() { @Test void getById() { User owner = createUser(); + User booker = createUser(); Item item = createItem(owner); - BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(booker, item); BookingDto bookingRes = bookingService.create(bookingCreateDto); BookingDto booking = bookingService.getById(bookingRes.getId(), owner.getId()); - Assertions.assertEquals(bookingRes, booking); + Assertions.assertEquals(bookingRes.getId(), booking.getId()); } @Test @@ -181,6 +181,33 @@ void getByIdWithInvalidIdReturnThrow() { ); } + @Test + void getByIdAndOwner() { + User owner = createUser(); + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + + BookingDto bookingRes = bookingService.create(bookingCreateDto); + BookingDto booking = bookingService.getById(bookingRes.getId(), owner.getId()); + + Assertions.assertEquals(bookingRes.getId(), booking.getId()); + } + + @Test + void getByIdAndWrongBooker() { + User owner = createUser(); + User wrongBooker = createUser(); + Item item = createItem(owner); + BookingCreateDto bookingCreateDto = makeBookingCreateDto(owner, item); + + BookingDto bookingRes = bookingService.create(bookingCreateDto); + + Assertions.assertThrowsExactly( + NotFoundException.class, + () -> bookingService.getById(bookingRes.getId(), wrongBooker.getId()) + ); + } + @Test void findItemsByOwner() { User owner = createUser(); @@ -192,9 +219,30 @@ void findItemsByOwner() { bookingService.create(bookingCreateDto); } - List bookings = bookingService.getByOwnerAndState(owner.getId(), BookingState.ALL); - - Assertions.assertEquals(ownerItemsCount, bookings.size()); + Assertions.assertEquals( + ownerItemsCount, + bookingService.getByOwnerAndState(owner.getId(), BookingState.ALL).size() + ); + Assertions.assertEquals( + ownerItemsCount, + bookingService.getByOwnerAndState(owner.getId(), BookingState.CURRENT).size() + ); + Assertions.assertEquals( + 0, + bookingService.getByOwnerAndState(owner.getId(), BookingState.REJECTED).size() + ); + Assertions.assertEquals( + ownerItemsCount, + bookingService.getByOwnerAndState(owner.getId(), BookingState.WAITING).size() + ); + Assertions.assertEquals( + 0, + bookingService.getByOwnerAndState(owner.getId(), BookingState.PAST).size() + ); + Assertions.assertEquals( + 0, + bookingService.getByOwnerAndState(owner.getId(), BookingState.FUTURE).size() + ); } @Test @@ -209,9 +257,30 @@ void findItemsByBooker() { bookingService.create(bookingCreateDto); } - List bookings = bookingService.getByBookerAndState(booker.getId(), BookingState.ALL); - - Assertions.assertEquals(ownerItemsCount, bookings.size()); + Assertions.assertEquals( + ownerItemsCount, + bookingService.getByBookerAndState(booker.getId(), BookingState.ALL).size() + ); + Assertions.assertEquals( + ownerItemsCount, + bookingService.getByBookerAndState(booker.getId(), BookingState.CURRENT).size() + ); + Assertions.assertEquals( + 0, + bookingService.getByBookerAndState(booker.getId(), BookingState.REJECTED).size() + ); + Assertions.assertEquals( + ownerItemsCount, + bookingService.getByBookerAndState(booker.getId(), BookingState.WAITING).size() + ); + Assertions.assertEquals( + 0, + bookingService.getByBookerAndState(booker.getId(), BookingState.PAST).size() + ); + Assertions.assertEquals( + 0, + bookingService.getByBookerAndState(booker.getId(), BookingState.FUTURE).size() + ); } private User createUser() { diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java index d09110e..0f2b9d0 100644 --- a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java @@ -158,7 +158,10 @@ void getListWithAllUsers() { List itemRequests = itemRequestService.getList(null); - Assertions.assertEquals(userRequestsCounts.stream().mapToInt(Integer::intValue).sum(), itemRequests.size()); + Assertions.assertEquals( + userRequestsCounts.stream().mapToInt(Integer::intValue).sum(), + itemRequests.size() + ); } private User createUser() { From a78318356080bf6682c26eb52fcfc8bbc4e7fe81 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 00:40:38 +0300 Subject: [PATCH 13/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=206?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru/practicum/shareit/request/ItemRequestServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java index 0f2b9d0..e6d0e9d 100644 --- a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java @@ -128,7 +128,7 @@ void getList() { for (int j = 0; j < 2; j++) { users.add(createUser()); - userRequestsCounts.add(new Random().nextInt(10)); + userRequestsCounts.add(new Random().nextInt(2, 5)); for (int i = 1; i <= userRequestsCounts.getLast(); i++) { ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(users.getLast()); @@ -148,7 +148,7 @@ void getListWithAllUsers() { for (int j = 0; j < 2; j++) { users.add(createUser()); - userRequestsCounts.add(new Random().nextInt(10)); + userRequestsCounts.add(new Random().nextInt(2, 5)); for (int i = 1; i <= userRequestsCounts.getLast(); i++) { ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(users.getLast()); From 7f76dd8712e348dbd466ea62a82d0cb5ce0600bb Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 00:48:49 +0300 Subject: [PATCH 14/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=207?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/ItemRequestServiceTest.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java index e6d0e9d..8ca3d80 100644 --- a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java @@ -124,44 +124,42 @@ void getListByUserWithInvalidUserReturnThrow() { @Test void getList() { List users = new ArrayList<>(); - List userRequestsCounts = new ArrayList<>(); + int cnt = 0; for (int j = 0; j < 2; j++) { - users.add(createUser()); - userRequestsCounts.add(new Random().nextInt(2, 5)); + User user = createUser(); + cnt = new Random().nextInt(2, 5); + users.add(user); - for (int i = 1; i <= userRequestsCounts.getLast(); i++) { - ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(users.getLast()); + for (int i = 1; i <= cnt; i++) { + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); itemRequestService.create(itemRequestCreateDto); } } - List itemRequests = itemRequestService.getList(users.getFirst().getId()); + List itemRequests = itemRequestService.getList(users.getLast().getId()); - Assertions.assertEquals(userRequestsCounts.getLast(), itemRequests.size()); + Assertions.assertEquals(cnt, itemRequests.size()); } @Test void getListWithAllUsers() { - List users = new ArrayList<>(); - List userRequestsCounts = new ArrayList<>(); + int total = 0; for (int j = 0; j < 2; j++) { - users.add(createUser()); - userRequestsCounts.add(new Random().nextInt(2, 5)); + User user = createUser(); + int cnt = new Random().nextInt(2, 5); + total += cnt; - for (int i = 1; i <= userRequestsCounts.getLast(); i++) { - ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(users.getLast()); + for (int i = 1; i <= cnt; i++) { + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); itemRequestService.create(itemRequestCreateDto); } } List itemRequests = itemRequestService.getList(null); - Assertions.assertEquals( - userRequestsCounts.stream().mapToInt(Integer::intValue).sum(), - itemRequests.size() - ); + Assertions.assertEquals(total, itemRequests.size()); } private User createUser() { From 5f4e853f52b8c5f4c5a26c2c9c8d34e13e19b857 Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 00:53:53 +0300 Subject: [PATCH 15/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=208?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/ItemRequestServiceTest.java | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java index 8ca3d80..adf88b7 100644 --- a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java @@ -124,42 +124,33 @@ void getListByUserWithInvalidUserReturnThrow() { @Test void getList() { List users = new ArrayList<>(); - int cnt = 0; for (int j = 0; j < 2; j++) { User user = createUser(); - cnt = new Random().nextInt(2, 5); users.add(user); - for (int i = 1; i <= cnt; i++) { - ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); - itemRequestService.create(itemRequestCreateDto); - } + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); + itemRequestService.create(itemRequestCreateDto); } List itemRequests = itemRequestService.getList(users.getLast().getId()); - Assertions.assertEquals(cnt, itemRequests.size()); + Assertions.assertEquals(1, itemRequests.size()); } @Test void getListWithAllUsers() { - int total = 0; for (int j = 0; j < 2; j++) { User user = createUser(); - int cnt = new Random().nextInt(2, 5); - total += cnt; - for (int i = 1; i <= cnt; i++) { - ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); - itemRequestService.create(itemRequestCreateDto); - } + ItemRequestCreateDto itemRequestCreateDto = makeItemRequestCreateDto(user); + itemRequestService.create(itemRequestCreateDto); } List itemRequests = itemRequestService.getList(null); - Assertions.assertEquals(total, itemRequests.size()); + Assertions.assertEquals(2, itemRequests.size()); } private User createUser() { From cba8f5a4ad6130fe43805c25dcf5426257d589cc Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 00:58:13 +0300 Subject: [PATCH 16/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=209?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../practicum/shareit/request/ItemRequestServiceTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java index adf88b7..0a904c7 100644 --- a/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestServiceTest.java @@ -1,6 +1,7 @@ package ru.practicum.shareit.request; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; @@ -41,6 +42,13 @@ class ItemRequestServiceTest { @Autowired ItemRequestService itemRequestService; + @BeforeEach + void setUp() { + userRepository.deleteAll(); + itemRepository.deleteAll(); + itemRequestRepository.deleteAll(); + } + @Test void create() { User user = createUser(); From a54944770c49449c33bf1c38f23817649f1a3b2c Mon Sep 17 00:00:00 2001 From: Alexander Krutov Date: Fri, 21 Mar 2025 01:19:43 +0300 Subject: [PATCH 17/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.=2010?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/practicum/shareit/item/dto/CommentCreateDto.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java index 3a788e3..d698785 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java @@ -1,6 +1,6 @@ package ru.practicum.shareit.item.dto; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,7 +12,7 @@ @Builder public class CommentCreateDto { - @NotBlank + @NotNull private String text; }